diff --git a/src/App.native.tsx b/src/App.native.tsx index d3b816a106..59b2ad9563 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -178,17 +178,15 @@ function App() { const [isReady, setReady] = useState(false) React.useEffect(() => { - Promise.all([initPersistedState(), ensureGeolocationResolved()]).then(() => - setReady(true), - ) + Promise.all([initPersistedState(), ensureGeolocationResolved(), Purchases.setLogLevel(Purchases.LOG_LEVEL.DEBUG)]).then(() => { + 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) - - if (isIOS) { - Purchases.configure({apiKey: RC_APPLE_PUBLIC_KEY}) - } else if (isAndroid) { - Purchases.configure({apiKey: RC_GOOGLE_PUBLIC_KEY}) - } + setReady(true) + }) }, []) if (!isReady) { diff --git a/src/screens/Subscriptions/index.tsx b/src/screens/Subscriptions/index.tsx index 557ca88593..6844af0126 100644 --- a/src/screens/Subscriptions/index.tsx +++ b/src/screens/Subscriptions/index.tsx @@ -6,6 +6,7 @@ import {NativeStackScreenProps} from '@react-navigation/native-stack' import {CommonNavigatorParams} from '#/lib/routes/types' import {useSubscriptionsState} from '#/state/purchases/subscriptions/useSubscriptionsState' +import {useSyncPurchases} from '#/state/purchases/subscriptions/useSyncPurchases' import {useManageSubscription} from '#/state/purchases/subscriptions/useManageSubscription' import {Subscription} from '#/state/purchases/subscriptions/types' 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 {Loader} from '#/components/Loader' import {Text} from '#/components/Typography' -import {useCurrencyFormatter} from '#/lib/currency' import {InlineLinkText, createStaticClick} from '#/components/Link' export type ScreenProps = NativeStackScreenProps< @@ -32,36 +32,64 @@ export function Subscriptions(_props: ScreenProps) { const isSubscribed = state?.entitlements?.some(e => e.id === 'core') const control = useDialogControl() + const {mutateAsync: syncPurchases} = useSyncPurchases() + const [data, setData] = React.useState(null) + + const sync = async () => { + try { + const data = await syncPurchases() + setData(data) + } catch (e: any) { + setData({ message: e.message }) + } + } + return ( - - - {isStateLoading ? ( - - ) : ( - <> - {isSubscribed ? ( - - ) : ( - <> - - - - )} - - )} - - + + + + {isStateLoading ? ( + + ) : ( + <> + {isSubscribed ? ( + + ) : ( + <> + + + + )} + + )} + + + + {data && ( + {JSON.stringify(data, null, 2)} + )} + + + ) } @@ -69,27 +97,21 @@ export function Subscriptions(_props: ScreenProps) { function CoreSubscriptions(props: { subscriptions: Subscription[] }) { const t = useTheme() const {_, i18n} = useLingui() - const { format } = useCurrencyFormatter() const {mutateAsync: manageSubscription} = useManageSubscription() return props.subscriptions.map(sub => ( Bluesky+ - - Status:{' '} - {sub.status} - - - {sub.renewalStatus === 'will_renew' ? ( + {sub.renews ? ( Renews:{' '} - {i18n.date(new Date(sub.endsAt), { dateStyle: "medium", timeStyle: "medium" })} + {i18n.date(new Date(sub.periodEndsAt), { dateStyle: "medium", timeStyle: "medium" })} ) : ( Ends:{' '} - {i18n.date(new Date(sub.endsAt), { dateStyle: "medium" })} + {i18n.date(new Date(sub.periodEndsAt), { dateStyle: "medium" })} )} diff --git a/src/state/purchases/subscriptions/types.ts b/src/state/purchases/subscriptions/types.ts index 8b467a43ca..1402ad23e0 100644 --- a/src/state/purchases/subscriptions/types.ts +++ b/src/state/purchases/subscriptions/types.ts @@ -38,8 +38,8 @@ export type Offering = export type Subscription = { group: SubscriptionGroupId platform: PlatformId - status: string - startsAt: number - endsAt: number - renewalStatus: string + renews: boolean + periodDtartsAt: string + periodEndsAt: string + purchasedAt: string } diff --git a/src/state/purchases/subscriptions/useSubscriptionGroup/index.ts b/src/state/purchases/subscriptions/useSubscriptionGroup/index.ts index 161962147a..bf6f43217b 100644 --- a/src/state/purchases/subscriptions/useSubscriptionGroup/index.ts +++ b/src/state/purchases/subscriptions/useSubscriptionGroup/index.ts @@ -1,6 +1,6 @@ import React from 'react' 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 {api} from '#/state/purchases/subscriptions/api' @@ -52,9 +52,17 @@ export function usePurchaseOffering() { throw new Error('Unsupported platform') } - Purchases.logIn(did) - Purchases.setEmail(email) - Purchases.purchaseStoreProduct(offering.package) + try { + await Purchases.logIn(did) + 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 + } } }) } diff --git a/src/state/purchases/subscriptions/useSyncPurchases.ts b/src/state/purchases/subscriptions/useSyncPurchases.ts new file mode 100644 index 0000000000..c68dcdb195 --- /dev/null +++ b/src/state/purchases/subscriptions/useSyncPurchases.ts @@ -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 + } + }, + }) +}