Some design
This commit is contained in:
+4
-2
@@ -16,8 +16,6 @@ import {msg} from '@lingui/macro'
|
|||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {KeyboardControllerProvider} from '#/lib/hooks/useEnableKeyboardController'
|
import {KeyboardControllerProvider} from '#/lib/hooks/useEnableKeyboardController'
|
||||||
import {isIOS, isAndroid} from '#/platform/detection'
|
|
||||||
import {RC_APPLE_PUBLIC_KEY, RC_GOOGLE_PUBLIC_KEY} from '#/env'
|
|
||||||
import {QueryProvider} from '#/lib/react-query'
|
import {QueryProvider} from '#/lib/react-query'
|
||||||
import {
|
import {
|
||||||
initialize,
|
initialize,
|
||||||
@@ -28,6 +26,7 @@ import {s} from '#/lib/styles'
|
|||||||
import {ThemeProvider} from '#/lib/ThemeContext'
|
import {ThemeProvider} from '#/lib/ThemeContext'
|
||||||
import I18nProvider from '#/locale/i18nProvider'
|
import I18nProvider from '#/locale/i18nProvider'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
import {isAndroid,isIOS} from '#/platform/detection'
|
||||||
import {Provider as A11yProvider} from '#/state/a11y'
|
import {Provider as A11yProvider} from '#/state/a11y'
|
||||||
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
||||||
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
||||||
@@ -72,6 +71,7 @@ import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry'
|
|||||||
import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialogs'
|
import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialogs'
|
||||||
import {Provider as PortalProvider} from '#/components/Portal'
|
import {Provider as PortalProvider} from '#/components/Portal'
|
||||||
import {AppProfiler} from '#/AppProfiler'
|
import {AppProfiler} from '#/AppProfiler'
|
||||||
|
import {RC_APPLE_PUBLIC_KEY, RC_GOOGLE_PUBLIC_KEY} from '#/env'
|
||||||
import {Splash} from '#/Splash'
|
import {Splash} from '#/Splash'
|
||||||
import {BottomSheetProvider} from '../modules/bottom-sheet'
|
import {BottomSheetProvider} from '../modules/bottom-sheet'
|
||||||
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
|
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
|
||||||
@@ -187,6 +187,8 @@ function App() {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Purchases.setLogLevel(Purchases.LOG_LEVEL.DEBUG)
|
||||||
|
|
||||||
if (isIOS) {
|
if (isIOS) {
|
||||||
Purchases.configure({apiKey: RC_APPLE_PUBLIC_KEY})
|
Purchases.configure({apiKey: RC_APPLE_PUBLIC_KEY})
|
||||||
} else if (isAndroid) {
|
} else if (isAndroid) {
|
||||||
|
|||||||
+1
-1
@@ -108,7 +108,7 @@ export const gradients = {
|
|||||||
nordic: {
|
nordic: {
|
||||||
values: [
|
values: [
|
||||||
[0, '#083367'],
|
[0, '#083367'],
|
||||||
[0.4, '#386D84'],
|
// [0.4, '#386D84'],
|
||||||
[1, '#9EE8C1'],
|
[1, '#9EE8C1'],
|
||||||
],
|
],
|
||||||
hover_value: '#3A7085',
|
hover_value: '#3A7085',
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import {useMemo} from 'react'
|
import {useMemo} from 'react'
|
||||||
import {LinearGradient} from 'expo-linear-gradient'
|
import {LinearGradient} from 'expo-linear-gradient'
|
||||||
|
|
||||||
import {atoms as a, tokens} from '#/alf'
|
import {atoms as a, tokens, ViewStyleProp} from '#/alf'
|
||||||
|
|
||||||
export function GradientFill({
|
export function GradientFill({
|
||||||
gradient,
|
gradient,
|
||||||
rotate = '0deg',
|
rotate = '0deg',
|
||||||
}: {
|
style,
|
||||||
|
}: ViewStyleProp & {
|
||||||
gradient:
|
gradient:
|
||||||
| typeof tokens.gradients.primary
|
| typeof tokens.gradients.primary
|
||||||
| typeof tokens.gradients.sky
|
| typeof tokens.gradients.sky
|
||||||
@@ -75,7 +76,7 @@ export function GradientFill({
|
|||||||
}
|
}
|
||||||
start={start}
|
start={start}
|
||||||
end={end}
|
end={end}
|
||||||
style={[a.absolute, a.inset_0]}
|
style={[a.absolute, a.inset_0, style]}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import {CheckThick_Stroke2_Corner0_Rounded as CheckThink} from '#/components/ico
|
|||||||
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
||||||
import {InlineLinkText, Link} from '#/components/Link'
|
import {InlineLinkText, Link} from '#/components/Link'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
|
import {SubduedFill} from '#/components/purchases/BlueskyPlusGradients'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
export function BlueskyPlusCore({
|
export function BlueskyPlusCore({
|
||||||
@@ -151,17 +152,43 @@ function DialogInner({control}: {control: Dialog.DialogControlProps}) {
|
|||||||
a.overflow_hidden,
|
a.overflow_hidden,
|
||||||
gtMobile ? {width: '100%', maxWidth: 400, minWidth: 200} : a.w_full,
|
gtMobile ? {width: '100%', maxWidth: 400, minWidth: 200} : a.w_full,
|
||||||
]}>
|
]}>
|
||||||
<BlueskyPlusLogo width={100} gradient="nordic" />
|
<View style={[a.relative, a.pb_md]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.absolute,
|
||||||
|
a.overflow_hidden,
|
||||||
|
isWeb
|
||||||
|
? undefined
|
||||||
|
: {borderTopLeftRadius: 20, borderTopRightRadius: 20},
|
||||||
|
{
|
||||||
|
top: (a.p_xl.padding + 4) * -1,
|
||||||
|
bottom: 0,
|
||||||
|
left: a.p_xl.padding * -1,
|
||||||
|
right: a.p_xl.padding * -1,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<GradientFill gradient={tokens.gradients.nordic} />
|
||||||
|
</View>
|
||||||
|
|
||||||
<Text style={[a.text_3xl, a.font_heavy, a.pt_lg]}>
|
<BlueskyPlusLogo width={80} fill="white" style={[a.z_10]} />
|
||||||
<Trans>Let's build the social web.</Trans>
|
|
||||||
</Text>
|
<Text
|
||||||
|
style={[
|
||||||
|
a.text_5xl,
|
||||||
|
a.font_heavy,
|
||||||
|
a.leading_tight,
|
||||||
|
a.pt_lg,
|
||||||
|
{color: 'white'},
|
||||||
|
]}>
|
||||||
|
<Trans>Let's build the social web.</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
a.text_md,
|
a.text_md,
|
||||||
a.leading_snug,
|
a.leading_snug,
|
||||||
a.pt_xs,
|
a.pt_lg,
|
||||||
t.atoms.text_contrast_medium,
|
t.atoms.text_contrast_medium,
|
||||||
]}>
|
]}>
|
||||||
<Trans>
|
<Trans>
|
||||||
@@ -196,10 +223,7 @@ function DialogInner({control}: {control: Dialog.DialogControlProps}) {
|
|||||||
: t.atoms.border_contrast_low,
|
: t.atoms.border_contrast_low,
|
||||||
]}>
|
]}>
|
||||||
{Boolean(selected || hovered) && (
|
{Boolean(selected || hovered) && (
|
||||||
<GradientFill
|
<SubduedFill style={{opacity: 0.2}} />
|
||||||
gradient={tokens.gradients.nordic}
|
|
||||||
style={{opacity: 0.1}}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -258,12 +282,14 @@ function DialogInner({control}: {control: Dialog.DialogControlProps}) {
|
|||||||
a.align_center,
|
a.align_center,
|
||||||
a.justify_center,
|
a.justify_center,
|
||||||
{height: 24, width: 24},
|
{height: 24, width: 24},
|
||||||
|
{
|
||||||
|
backgroundColor: t.atoms.bg.backgroundColor,
|
||||||
|
},
|
||||||
]}>
|
]}>
|
||||||
{selected ? (
|
{selected ? (
|
||||||
<>
|
<>
|
||||||
<GradientFill gradient={tokens.gradients.nordic} />
|
|
||||||
<CheckThink
|
<CheckThink
|
||||||
fill="white"
|
fill={t.atoms.text_contrast_high.color}
|
||||||
size="xs"
|
size="xs"
|
||||||
style={[a.z_10]}
|
style={[a.z_10]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import {View} from 'react-native'
|
||||||
|
import {LinearGradient} from 'expo-linear-gradient'
|
||||||
|
|
||||||
|
import {atoms as a, tokens, useTheme, ViewStyleProp} from '#/alf'
|
||||||
|
|
||||||
|
const dark = tokens.gradients.nordic
|
||||||
|
const light = {
|
||||||
|
values: [
|
||||||
|
[0, '#0F62C7'],
|
||||||
|
[1, '#9EE8C1'],
|
||||||
|
],
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export function SubduedFill({style}: ViewStyleProp) {
|
||||||
|
const t = useTheme()
|
||||||
|
const isDark = t.name !== 'light'
|
||||||
|
const gradient = isDark ? dark : light
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[a.absolute, a.inset_0, t.atoms.bg]}>
|
||||||
|
<LinearGradient
|
||||||
|
colors={gradient.values.map(c => c[1]) as [string, string, ...string[]]}
|
||||||
|
locations={
|
||||||
|
gradient.values.map(c => c[0]) as [number, number, ...number[]]
|
||||||
|
}
|
||||||
|
start={{x: 0, y: 1}}
|
||||||
|
end={{x: 1, y: 1}}
|
||||||
|
style={[a.absolute, a.inset_0, {opacity: 0.3}, style]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
APISubscription,
|
APISubscription,
|
||||||
NativePurchaseRestricted,
|
NativePurchaseRestricted,
|
||||||
} from '#/state/purchases/types'
|
} from '#/state/purchases/types'
|
||||||
import {atoms as a, tokens, useBreakpoints, useGutters,useTheme} from '#/alf'
|
import {atoms as a, tokens, useBreakpoints, useGutters, useTheme} from '#/alf'
|
||||||
import {Admonition} from '#/components/Admonition'
|
import {Admonition} from '#/components/Admonition'
|
||||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||||
import {useDialogControl} from '#/components/Dialog'
|
import {useDialogControl} from '#/components/Dialog'
|
||||||
@@ -38,6 +38,7 @@ import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus
|
|||||||
import * as Layout from '#/components/Layout'
|
import * as Layout from '#/components/Layout'
|
||||||
import {createStaticClick, InlineLinkText, Link} from '#/components/Link'
|
import {createStaticClick, InlineLinkText, Link} from '#/components/Link'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
|
import {SubduedFill} from '#/components/purchases/BlueskyPlusGradients'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
export type ScreenProps = NativeStackScreenProps<
|
export type ScreenProps = NativeStackScreenProps<
|
||||||
@@ -46,6 +47,8 @@ export type ScreenProps = NativeStackScreenProps<
|
|||||||
>
|
>
|
||||||
|
|
||||||
export function Subscriptions(_props: ScreenProps) {
|
export function Subscriptions(_props: ScreenProps) {
|
||||||
|
const t = useTheme()
|
||||||
|
const isDark = t.name !== 'light'
|
||||||
const gutters = useGutters(['base', 'base'])
|
const gutters = useGutters(['base', 'base'])
|
||||||
const insets = useSafeAreaInsets()
|
const insets = useSafeAreaInsets()
|
||||||
const purchases = usePurchases()
|
const purchases = usePurchases()
|
||||||
@@ -64,12 +67,16 @@ export function Subscriptions(_props: ScreenProps) {
|
|||||||
return (
|
return (
|
||||||
<Layout.Screen noPaddingTop>
|
<Layout.Screen noPaddingTop>
|
||||||
<Layout.Center>
|
<Layout.Center>
|
||||||
<GradientFill gradient={tokens.gradients.nordic} />
|
<SubduedFill />
|
||||||
|
|
||||||
<View style={{height: insets.top}} />
|
<View style={{height: insets.top}} />
|
||||||
<Layout.Header.Outer noBottomBorder>
|
<Layout.Header.Outer>
|
||||||
<Layout.Header.BackButton />
|
<Layout.Header.BackButton />
|
||||||
<Layout.Header.Content>
|
<Layout.Header.Content>
|
||||||
<BlueskyPlusLogo width={100} fill="white" />
|
<BlueskyPlusLogo
|
||||||
|
width={100}
|
||||||
|
fill={isDark ? t.palette.white : t.atoms.text_contrast_high.color}
|
||||||
|
/>
|
||||||
</Layout.Header.Content>
|
</Layout.Header.Content>
|
||||||
<Layout.Header.Slot />
|
<Layout.Header.Slot />
|
||||||
</Layout.Header.Outer>
|
</Layout.Header.Outer>
|
||||||
@@ -114,17 +121,17 @@ function Core({
|
|||||||
const coreSubscriptions = state.subscriptions.filter(
|
const coreSubscriptions = state.subscriptions.filter(
|
||||||
s => s.group === SubscriptionGroupId.Core,
|
s => s.group === SubscriptionGroupId.Core,
|
||||||
)
|
)
|
||||||
const activeCoreSubscriptions = coreSubscriptions.filter(
|
const activeSubscriptions = coreSubscriptions.filter(
|
||||||
s => s.status === 'active' || s.status === 'paused',
|
s => s.status === 'active' || s.status === 'paused',
|
||||||
)
|
)
|
||||||
const inactiveCoreSubscriptions = coreSubscriptions.filter(
|
const inactiveSubscriptions = coreSubscriptions.filter(
|
||||||
s => s.status !== 'active' && s.status !== 'paused',
|
s => s.status !== 'active' && s.status !== 'paused',
|
||||||
)
|
)
|
||||||
const hasActiveSubscriptions = activeCoreSubscriptions.length > 0
|
const hasActiveSubscriptions = activeSubscriptions.length > 0
|
||||||
const hasInactiveCoreSubscriptions = inactiveCoreSubscriptions.length > 0
|
const hasInactiveSubscriptions = inactiveSubscriptions.length > 0
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.gap_md]}>
|
<View style={[isWeb ? a.gap_lg : a.gap_md]}>
|
||||||
{restricted === 'yes' ? (
|
{restricted === 'yes' ? (
|
||||||
<Admonition type="info">
|
<Admonition type="info">
|
||||||
<Trans>
|
<Trans>
|
||||||
@@ -132,45 +139,60 @@ function Core({
|
|||||||
Please subscribe additional accounts through our web application.
|
Please subscribe additional accounts through our web application.
|
||||||
</Trans>
|
</Trans>
|
||||||
</Admonition>
|
</Admonition>
|
||||||
) : hasActiveSubscriptions ? null : hasInactiveCoreSubscriptions ? (
|
) : hasActiveSubscriptions ? null : hasInactiveSubscriptions ? (
|
||||||
<Repurchase />
|
<PurchaseHeader />
|
||||||
) : (
|
) : (
|
||||||
<Purchase />
|
<PurchaseHeader />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Divider />
|
{hasActiveSubscriptions ||
|
||||||
|
(hasInactiveSubscriptions && (
|
||||||
|
<>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
{hasActiveSubscriptions && (
|
{hasActiveSubscriptions && (
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_sm]}>
|
||||||
<Text style={[a.text_md, a.font_bold, t.atoms.text_contrast_medium]}>
|
<Text
|
||||||
<Trans>My subscriptions</Trans>
|
style={[
|
||||||
</Text>
|
a.text_md,
|
||||||
|
a.font_bold,
|
||||||
|
t.atoms.text_contrast_medium,
|
||||||
|
]}>
|
||||||
|
<Trans>My subscriptions</Trans>
|
||||||
|
</Text>
|
||||||
|
|
||||||
<View style={[a.gap_lg]}>
|
<View style={[a.gap_lg]}>
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_sm]}>
|
||||||
{activeCoreSubscriptions.map(sub => (
|
{activeSubscriptions.map(sub => (
|
||||||
<Subscription key={sub.purchasedAt} subscription={sub} />
|
<Subscription key={sub.purchasedAt} subscription={sub} />
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{hasInactiveCoreSubscriptions && (
|
{hasInactiveSubscriptions && (
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_sm]}>
|
||||||
<Text style={[a.text_md, a.font_bold, t.atoms.text_contrast_medium]}>
|
<Text
|
||||||
<Trans>Past subscriptions</Trans>
|
style={[
|
||||||
</Text>
|
a.text_md,
|
||||||
|
a.font_bold,
|
||||||
|
t.atoms.text_contrast_medium,
|
||||||
|
]}>
|
||||||
|
<Trans>Past subscriptions</Trans>
|
||||||
|
</Text>
|
||||||
|
|
||||||
<View style={[a.gap_lg]}>
|
<View style={[a.gap_lg]}>
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_sm]}>
|
||||||
{inactiveCoreSubscriptions.map(sub => (
|
{inactiveSubscriptions.map(sub => (
|
||||||
<Subscription key={sub.purchasedAt} subscription={sub} />
|
<Subscription key={sub.purchasedAt} subscription={sub} />
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
@@ -219,6 +241,9 @@ function InfoCards() {
|
|||||||
a.p_lg,
|
a.p_lg,
|
||||||
a.rounded_md,
|
a.rounded_md,
|
||||||
a.gap_sm,
|
a.gap_sm,
|
||||||
|
a.overflow_hidden,
|
||||||
|
a.border,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
t.atoms.bg_contrast_25,
|
t.atoms.bg_contrast_25,
|
||||||
]}>
|
]}>
|
||||||
<Text
|
<Text
|
||||||
@@ -233,7 +258,7 @@ function InfoCards() {
|
|||||||
<View style={[a.py_xs, a.gap_sm]}>
|
<View style={[a.py_xs, a.gap_sm]}>
|
||||||
{activeFeatures.map(f => (
|
{activeFeatures.map(f => (
|
||||||
<View key={f.text} style={[a.flex_row, a.align_center, a.gap_xs]}>
|
<View key={f.text} style={[a.flex_row, a.align_center, a.gap_xs]}>
|
||||||
<f.icon fill={t.atoms.text_contrast_low.color} size="xs" />
|
<f.icon fill={tokens.blueskyPlus.mid} size="xs" />
|
||||||
<Text style={[a.text_sm]}>{f.text}</Text>
|
<Text style={[a.text_sm]}>{f.text}</Text>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
@@ -283,14 +308,23 @@ function InfoCards() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Repurchase() {
|
function PurchaseHeader() {
|
||||||
|
const t = useTheme()
|
||||||
|
const isDark = t.name !== 'light'
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const control = useDialogControl()
|
const control = useDialogControl()
|
||||||
|
const {gtMobile} = useBreakpoints()
|
||||||
|
const large = gtMobile
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[large ? [a.py_sm, a.gap_md] : a.gap_sm]}>
|
||||||
<Text style={[a.text_3xl, a.font_heavy]}>
|
<Text
|
||||||
<Trans>You're part of something bigger.</Trans>
|
style={[
|
||||||
|
large ? a.text_5xl : a.text_3xl,
|
||||||
|
a.font_heavy,
|
||||||
|
isDark ? undefined : t.atoms.text_contrast_high,
|
||||||
|
]}>
|
||||||
|
<Trans>Building a better internet needs your support.</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View style={[a.gap_xs]}>
|
<View style={[a.gap_xs]}>
|
||||||
@@ -320,81 +354,6 @@ function Repurchase() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Purchase() {
|
|
||||||
const t = useTheme()
|
|
||||||
const {_} = useLingui()
|
|
||||||
const control = useDialogControl()
|
|
||||||
const {features} = useFeatures()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Text style={[a.text_3xl, a.font_heavy, a.pb_xs]}>
|
|
||||||
<Trans>Building a better internet needs your support.</Trans>
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<View style={[a.gap_xs]}>
|
|
||||||
<Text style={[a.text_md, a.leading_snug, a.pt_xs]}>
|
|
||||||
<Trans>
|
|
||||||
Subscribing to Bluesky+ helps ensure that our work to build an open,
|
|
||||||
secure, and user-first internet can continue.
|
|
||||||
</Trans>
|
|
||||||
</Text>
|
|
||||||
<Text style={[a.text_md, a.leading_snug, a.pt_xs]}>
|
|
||||||
<Trans>Plus, you'll get access to exclusive features!</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
a.my_md,
|
|
||||||
a.p_lg,
|
|
||||||
a.gap_sm,
|
|
||||||
a.rounded_sm,
|
|
||||||
a.border,
|
|
||||||
t.atoms.border_contrast_low,
|
|
||||||
]}>
|
|
||||||
{features.map(f => (
|
|
||||||
<View key={f.text} style={[a.flex_row, a.gap_sm]}>
|
|
||||||
<View style={{paddingTop: 2}}>
|
|
||||||
<f.icon
|
|
||||||
fill={
|
|
||||||
f.available
|
|
||||||
? tokens.blueskyPlus.mid
|
|
||||||
: t.atoms.text_contrast_low.color
|
|
||||||
}
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
a.text_md,
|
|
||||||
a.leading_snug,
|
|
||||||
f.available ? t.atoms.text : t.atoms.text_contrast_medium,
|
|
||||||
]}>
|
|
||||||
{f.text}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
label={_('Subscribe')}
|
|
||||||
onPress={() => control.open()}
|
|
||||||
size="large"
|
|
||||||
variant="solid"
|
|
||||||
color="primary"
|
|
||||||
style={[a.overflow_hidden]}>
|
|
||||||
<GradientFill gradient={tokens.gradients.nordic} />
|
|
||||||
<ButtonText style={[{color: 'white'}]}>
|
|
||||||
<Trans>Subscribe</Trans>
|
|
||||||
</ButtonText>
|
|
||||||
<ButtonIcon icon={Plus} position="right" style={[{color: 'white'}]} />
|
|
||||||
</Button>
|
|
||||||
<BlueskyPlusCore control={control} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useSubscriptionUI({
|
export function useSubscriptionUI({
|
||||||
subscription: sub,
|
subscription: sub,
|
||||||
}: {
|
}: {
|
||||||
@@ -464,12 +423,13 @@ export function Subscription({
|
|||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {mutateAsync: manageSubscription} = useManageSubscription()
|
const {mutateAsync: manageSubscription} = useManageSubscription()
|
||||||
const ui = useSubscriptionUI({subscription: sub})
|
const ui = useSubscriptionUI({subscription: sub})
|
||||||
|
const isExpired = sub.status !== 'active' && sub.status !== 'paused'
|
||||||
|
|
||||||
const canManage =
|
const canManage =
|
||||||
(isWeb && sub.platform === PlatformId.Web) ||
|
(isWeb && sub.platform === PlatformId.Web) ||
|
||||||
(isIOS && sub.platform === PlatformId.Ios) ||
|
(isIOS && sub.platform === PlatformId.Ios) ||
|
||||||
(isAndroid && sub.platform === PlatformId.Android)
|
(isAndroid && sub.platform === PlatformId.Android)
|
||||||
const showManage = canManage && sub.status !== 'expired'
|
const showManage = canManage && !isExpired
|
||||||
|
|
||||||
const statusStyles = React.useMemo<TextStyle>(() => {
|
const statusStyles = React.useMemo<TextStyle>(() => {
|
||||||
return {
|
return {
|
||||||
@@ -503,8 +463,13 @@ export function Subscription({
|
|||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
key={sub.purchasedAt}
|
key={sub.purchasedAt}
|
||||||
style={[a.px_lg, a.rounded_md, a.overflow_hidden]}>
|
style={[
|
||||||
<GradientFill gradient={tokens.gradients.nordic} />
|
a.px_lg,
|
||||||
|
a.rounded_md,
|
||||||
|
a.overflow_hidden,
|
||||||
|
isExpired && [a.border, t.atoms.border_contrast_low],
|
||||||
|
]}>
|
||||||
|
{!isExpired && <GradientFill gradient={tokens.gradients.nordic} />}
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.absolute,
|
a.absolute,
|
||||||
@@ -613,6 +578,11 @@ function useFeatures() {
|
|||||||
icon: Check,
|
icon: Check,
|
||||||
text: _(msg`Custom app icons`),
|
text: _(msg`Custom app icons`),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
available: true,
|
||||||
|
icon: Check,
|
||||||
|
text: _(msg`Lorem ipsum dolor sit amet`),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
available: false,
|
available: false,
|
||||||
icon: Clock,
|
icon: Clock,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export function usePurchaseOffering() {
|
|||||||
|
|
||||||
return useMutation<void, Error | ReceiptAlreadyInUseError, Params>({
|
return useMutation<void, Error | ReceiptAlreadyInUseError, Params>({
|
||||||
async mutationFn({did, email, offering}: Params) {
|
async mutationFn({did, email, offering}: Params) {
|
||||||
|
console.log(offering)
|
||||||
if (offering.platform === PlatformId.Web) {
|
if (offering.platform === PlatformId.Web) {
|
||||||
throw new Error('Unsupported platform')
|
throw new Error('Unsupported platform')
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user