WIP
This commit is contained in:
+8
-10
@@ -178,17 +178,15 @@ function App() {
|
|||||||
const [isReady, setReady] = useState(false)
|
const [isReady, setReady] = useState(false)
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
Promise.all([initPersistedState(), ensureGeolocationResolved()]).then(() =>
|
Promise.all([initPersistedState(), ensureGeolocationResolved(), Purchases.setLogLevel(Purchases.LOG_LEVEL.DEBUG)]).then(() => {
|
||||||
setReady(true),
|
if (isIOS) {
|
||||||
)
|
Purchases.configure({apiKey: RC_APPLE_PUBLIC_KEY})
|
||||||
|
} else if (isAndroid) {
|
||||||
|
Purchases.configure({apiKey: RC_GOOGLE_PUBLIC_KEY})
|
||||||
|
}
|
||||||
|
|
||||||
Purchases.setLogLevel(Purchases.LOG_LEVEL.DEBUG)
|
setReady(true)
|
||||||
|
})
|
||||||
if (isIOS) {
|
|
||||||
Purchases.configure({apiKey: RC_APPLE_PUBLIC_KEY})
|
|
||||||
} else if (isAndroid) {
|
|
||||||
Purchases.configure({apiKey: RC_GOOGLE_PUBLIC_KEY})
|
|
||||||
}
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
if (!isReady) {
|
if (!isReady) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
|||||||
|
|
||||||
import {CommonNavigatorParams} from '#/lib/routes/types'
|
import {CommonNavigatorParams} from '#/lib/routes/types'
|
||||||
import {useSubscriptionsState} from '#/state/purchases/subscriptions/useSubscriptionsState'
|
import {useSubscriptionsState} from '#/state/purchases/subscriptions/useSubscriptionsState'
|
||||||
|
import {useSyncPurchases} from '#/state/purchases/subscriptions/useSyncPurchases'
|
||||||
import {useManageSubscription} from '#/state/purchases/subscriptions/useManageSubscription'
|
import {useManageSubscription} from '#/state/purchases/subscriptions/useManageSubscription'
|
||||||
import {Subscription} from '#/state/purchases/subscriptions/types'
|
import {Subscription} from '#/state/purchases/subscriptions/types'
|
||||||
import {CenteredView} from '#/view/com/util/Views'
|
import {CenteredView} from '#/view/com/util/Views'
|
||||||
@@ -17,7 +18,6 @@ import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus
|
|||||||
import * as Layout from '#/components/Layout'
|
import * as Layout from '#/components/Layout'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {useCurrencyFormatter} from '#/lib/currency'
|
|
||||||
import {InlineLinkText, createStaticClick} from '#/components/Link'
|
import {InlineLinkText, createStaticClick} from '#/components/Link'
|
||||||
|
|
||||||
export type ScreenProps = NativeStackScreenProps<
|
export type ScreenProps = NativeStackScreenProps<
|
||||||
@@ -32,36 +32,64 @@ export function Subscriptions(_props: ScreenProps) {
|
|||||||
const isSubscribed = state?.entitlements?.some(e => e.id === 'core')
|
const isSubscribed = state?.entitlements?.some(e => e.id === 'core')
|
||||||
const control = useDialogControl()
|
const control = useDialogControl()
|
||||||
|
|
||||||
|
const {mutateAsync: syncPurchases} = useSyncPurchases()
|
||||||
|
const [data, setData] = React.useState<any>(null)
|
||||||
|
|
||||||
|
const sync = async () => {
|
||||||
|
try {
|
||||||
|
const data = await syncPurchases()
|
||||||
|
setData(data)
|
||||||
|
} catch (e: any) {
|
||||||
|
setData({ message: e.message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Screen>
|
<Layout.Screen>
|
||||||
<Layout.Header title={_(msg`Subscriptions`)} />
|
<Layout.Header title={_(msg`Subscriptions`)} />
|
||||||
|
|
||||||
<CenteredView sideBorders={true} style={[a.util_screen_outer]}>
|
<Layout.Content>
|
||||||
<View style={[a.px_xl, a.py_xl, a.gap_lg]}>
|
<CenteredView sideBorders={true} style={[a.util_screen_outer]}>
|
||||||
{isStateLoading ? (
|
<View style={[a.px_xl, a.py_xl, a.gap_lg]}>
|
||||||
<Loader />
|
{isStateLoading ? (
|
||||||
) : (
|
<Loader />
|
||||||
<>
|
) : (
|
||||||
{isSubscribed ? (
|
<>
|
||||||
<CoreSubscriptions subscriptions={state!.subscriptions} />
|
{isSubscribed ? (
|
||||||
) : (
|
<CoreSubscriptions subscriptions={state!.subscriptions} />
|
||||||
<>
|
) : (
|
||||||
<Button
|
<>
|
||||||
label={_('Subscribe')}
|
<Button
|
||||||
onPress={() => control.open()}
|
label={_('Subscribe')}
|
||||||
size="large"
|
onPress={() => control.open()}
|
||||||
variant="solid"
|
size="large"
|
||||||
color="primary">
|
variant="solid"
|
||||||
<ButtonText>Subscribe</ButtonText>
|
color="primary">
|
||||||
<ButtonIcon icon={Plus} />
|
<ButtonText>Subscribe</ButtonText>
|
||||||
</Button>
|
<ButtonIcon icon={Plus} />
|
||||||
<BlueskyPlus control={control} />
|
</Button>
|
||||||
</>
|
<BlueskyPlus control={control} />
|
||||||
)}
|
</>
|
||||||
</>
|
)}
|
||||||
)}
|
</>
|
||||||
</View>
|
)}
|
||||||
</CenteredView>
|
|
||||||
|
<Button
|
||||||
|
label={_('Subscribe')}
|
||||||
|
onPress={() => sync()}
|
||||||
|
size="large"
|
||||||
|
variant="solid"
|
||||||
|
color="secondary">
|
||||||
|
<ButtonText>Restore Purchase</ButtonText>
|
||||||
|
<ButtonIcon icon={Plus} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{data && (
|
||||||
|
<Text>{JSON.stringify(data, null, 2)}</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</CenteredView>
|
||||||
|
</Layout.Content>
|
||||||
</Layout.Screen>
|
</Layout.Screen>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -69,27 +97,21 @@ export function Subscriptions(_props: ScreenProps) {
|
|||||||
function CoreSubscriptions(props: { subscriptions: Subscription[] }) {
|
function CoreSubscriptions(props: { subscriptions: Subscription[] }) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_, i18n} = useLingui()
|
const {_, i18n} = useLingui()
|
||||||
const { format } = useCurrencyFormatter()
|
|
||||||
const {mutateAsync: manageSubscription} = useManageSubscription()
|
const {mutateAsync: manageSubscription} = useManageSubscription()
|
||||||
|
|
||||||
return props.subscriptions.map(sub => (
|
return props.subscriptions.map(sub => (
|
||||||
<View style={[a.p_lg, a.rounded_md, a.border, a.gap_xs, t.atoms.border_contrast_low]}>
|
<View style={[a.p_lg, a.rounded_md, a.border, a.gap_xs, t.atoms.border_contrast_low]}>
|
||||||
<Text style={[a.text_lg, a.font_heavy]}>Bluesky+</Text>
|
<Text style={[a.text_lg, a.font_heavy]}>Bluesky+</Text>
|
||||||
|
|
||||||
<Text style={[a.text_sm]}>
|
{sub.renews ? (
|
||||||
<Text style={[a.text_sm, a.font_bold, t.atoms.text_contrast_medium]}><Trans>Status:</Trans></Text>{' '}
|
|
||||||
<Text style={[a.text_sm]}>{sub.status}</Text>
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
{sub.renewalStatus === 'will_renew' ? (
|
|
||||||
<Text style={[a.text_sm]}>
|
<Text style={[a.text_sm]}>
|
||||||
<Text style={[a.text_sm, a.font_bold, t.atoms.text_contrast_medium]}><Trans>Renews:</Trans></Text>{' '}
|
<Text style={[a.text_sm, a.font_bold, t.atoms.text_contrast_medium]}><Trans>Renews:</Trans></Text>{' '}
|
||||||
<Text style={[a.text_sm]}>{i18n.date(new Date(sub.endsAt), { dateStyle: "medium", timeStyle: "medium" })}</Text>
|
<Text style={[a.text_sm]}>{i18n.date(new Date(sub.periodEndsAt), { dateStyle: "medium", timeStyle: "medium" })}</Text>
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<Text style={[a.text_sm]}>
|
<Text style={[a.text_sm]}>
|
||||||
<Text style={[a.text_sm, a.font_bold, t.atoms.text_contrast_medium]}><Trans>Ends:</Trans></Text>{' '}
|
<Text style={[a.text_sm, a.font_bold, t.atoms.text_contrast_medium]}><Trans>Ends:</Trans></Text>{' '}
|
||||||
<Text style={[a.text_sm]}>{i18n.date(new Date(sub.endsAt), { dateStyle: "medium" })}</Text>
|
<Text style={[a.text_sm]}>{i18n.date(new Date(sub.periodEndsAt), { dateStyle: "medium" })}</Text>
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ export type Offering =
|
|||||||
export type Subscription = {
|
export type Subscription = {
|
||||||
group: SubscriptionGroupId
|
group: SubscriptionGroupId
|
||||||
platform: PlatformId
|
platform: PlatformId
|
||||||
status: string
|
renews: boolean
|
||||||
startsAt: number
|
periodDtartsAt: string
|
||||||
endsAt: number
|
periodEndsAt: string
|
||||||
renewalStatus: string
|
purchasedAt: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {useQuery, useMutation} from '@tanstack/react-query'
|
import {useQuery, useMutation} from '@tanstack/react-query'
|
||||||
import Purchases from 'react-native-purchases'
|
import Purchases, { PURCHASES_ERROR_CODE } from 'react-native-purchases'
|
||||||
|
|
||||||
import {isIOS} from '#/platform/detection'
|
import {isIOS} from '#/platform/detection'
|
||||||
import {api} from '#/state/purchases/subscriptions/api'
|
import {api} from '#/state/purchases/subscriptions/api'
|
||||||
@@ -52,9 +52,17 @@ export function usePurchaseOffering() {
|
|||||||
throw new Error('Unsupported platform')
|
throw new Error('Unsupported platform')
|
||||||
}
|
}
|
||||||
|
|
||||||
Purchases.logIn(did)
|
try {
|
||||||
Purchases.setEmail(email)
|
await Purchases.logIn(did)
|
||||||
Purchases.purchaseStoreProduct(offering.package)
|
await Purchases.setEmail(email)
|
||||||
|
await Purchases.purchaseStoreProduct(offering.package)
|
||||||
|
} catch (e: any) {
|
||||||
|
if (e.code === PURCHASES_ERROR_CODE.RECEIPT_ALREADY_IN_USE_ERROR) {
|
||||||
|
console.log('recipt error', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import {useMutation} from '@tanstack/react-query'
|
||||||
|
import Purchases, { PURCHASES_ERROR_CODE } from 'react-native-purchases'
|
||||||
|
|
||||||
|
import {useSession} from '#/state/session'
|
||||||
|
|
||||||
|
export function useSyncPurchases() {
|
||||||
|
const {currentAccount} = useSession()
|
||||||
|
return useMutation({
|
||||||
|
async mutationFn() {
|
||||||
|
if (!currentAccount) {
|
||||||
|
throw new Error('Not logged in')
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Purchases.logIn(currentAccount.did)
|
||||||
|
return await Purchases.restorePurchases()
|
||||||
|
} catch (e: any) {
|
||||||
|
if (e.code === PURCHASES_ERROR_CODE.RECEIPT_ALREADY_IN_USE_ERROR) {
|
||||||
|
console.log('recipt error', e)
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user