diff --git a/src/screens/Subscriptions/index.tsx b/src/screens/Subscriptions/index.tsx
index e71e6726df..5230ff052e 100644
--- a/src/screens/Subscriptions/index.tsx
+++ b/src/screens/Subscriptions/index.tsx
@@ -9,11 +9,14 @@ import {
useMainSubscriptions,
usePurchaseSubscription,
} from '#/state/purchases/subscriptions'
+// import {useEntitlements} from '#/state/purchases/subscriptions/useEntitlements'
+import {Subscriptions as SubscriptionsResponse} from '#/state/purchases/subscriptions/types'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
-import {atoms as a} from '#/alf'
+import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import * as Layout from '#/components/Layout'
+import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
export type ScreenProps = NativeStackScreenProps<
@@ -24,54 +27,110 @@ export type ScreenProps = NativeStackScreenProps<
export function Subscriptions(_props: ScreenProps) {
const {_} = useLingui()
- const {data: subscriptions} = useMainSubscriptions()
- const {mutateAsync: purchaseSubscription} = usePurchaseSubscription()
+ // const {data: entitlements} = useEntitlements()
+ const {data: subscriptions, isError} = useMainSubscriptions()
return (
-
+
Subs
-
- {subscriptions?.monthly.map(s => (
-
-
-
- ))}
-
-
- {subscriptions?.annual.map(s => (
-
-
-
- ))}
-
+ {isError ? (
+ Error
+ ) : !subscriptions ? (
+
+ ) : (
+
+ )}
)
}
+
+export function MainSubscription({
+ subscriptions,
+}: {
+ subscriptions: SubscriptionsResponse
+}) {
+ const t = useTheme()
+
+ const active = subscriptions.active.at(0)
+ const {mutateAsync: purchaseSubscription} = usePurchaseSubscription()
+
+ if (active) {
+ const contents = (
+
+
+ Bluesky Plus
+
+ Supporter
+
+
+
+ Managed via: {active?.platform}
+
+ )
+
+ return (
+
+ {contents}
+
+ )
+ } else {
+ return (
+
+
+ {subscriptions.available.monthly.map(s => (
+
+
+
+ ))}
+
+
+ {subscriptions.available.annual.map(s => (
+
+
+
+ ))}
+
+
+ )
+ }
+}
diff --git a/src/state/purchases/subscriptions/api/index.ts b/src/state/purchases/subscriptions/api/index.ts
index c65329b394..9be0595454 100644
--- a/src/state/purchases/subscriptions/api/index.ts
+++ b/src/state/purchases/subscriptions/api/index.ts
@@ -1,4 +1,5 @@
import {RawSubscriptionObject} from '#/state/purchases/subscriptions/api/types'
+import {EntitlementId} from '#/state/purchases/subscriptions/types'
export async function getMainSubscriptions({
did,
@@ -13,10 +14,31 @@ export async function getMainSubscriptions({
if (!res.ok) {
console.error('error fetching subscriptions', res.status, await res.text())
+ return {
+ active: [],
+ available: [],
+ }
+ }
+
+ const json = await res.json()
+
+ return json as {
+ active: RawSubscriptionObject[]
+ available: RawSubscriptionObject[]
+ }
+}
+
+export async function getEntitlements({did}: {did: string}) {
+ const res = await fetch(
+ `https://bsky-purchases.ngrok.io/entitlements?user=${did}`,
+ )
+
+ if (!res.ok) {
+ console.error('error fetching entitlements', res.status, await res.text())
return []
}
- const {subscriptions} = await res.json()
+ const {entitlements} = await res.json()
- return subscriptions as RawSubscriptionObject[]
+ return entitlements as {id: EntitlementId}[]
}
diff --git a/src/state/purchases/subscriptions/api/types.ts b/src/state/purchases/subscriptions/api/types.ts
index 306555d7a7..e4e5f19012 100644
--- a/src/state/purchases/subscriptions/api/types.ts
+++ b/src/state/purchases/subscriptions/api/types.ts
@@ -6,7 +6,15 @@ export type RawSubscriptionObjectBase = T & {
lookupKey: string
checkoutId: string
interval: 'monthly' | 'annual'
- autoRenew: boolean
+ active: boolean
+ autoRenewStatus:
+ | 'will_renew'
+ | 'will_not_renew'
+ | 'will_change_product'
+ | 'will_pause'
+ | 'requires_price_increase_consent'
+ | 'has_already_renewed'
+ | null
status:
| 'trialing'
| 'active'
diff --git a/src/state/purchases/subscriptions/index.ts b/src/state/purchases/subscriptions/index.ts
index 598ca509ae..ed3d308c1f 100644
--- a/src/state/purchases/subscriptions/index.ts
+++ b/src/state/purchases/subscriptions/index.ts
@@ -1,5 +1,5 @@
import Purchases from 'react-native-purchases'
-import {useMutation, useQuery} from '@tanstack/react-query'
+import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import {isAndroid} from '#/platform/detection'
import {getMainSubscriptions} from '#/state/purchases/subscriptions/api'
@@ -17,15 +17,12 @@ export function useMainSubscriptions() {
return useQuery({
queryKey: ['availableSubscriptions', did],
async queryFn() {
- Purchases.logIn(did)
- Purchases.setEmail(currentAccount!.email!)
-
- const platform = isAndroid ? 'android' : ('ios' as const)
+ const platform = isAndroid ? 'android' : 'ios'
const rawSubscriptions = await getMainSubscriptions({
did: currentAccount!.did,
platform,
})
- const platformSubscriptions = rawSubscriptions
+ const platformSubscriptions = rawSubscriptions.available
.filter(s => s.platform !== 'web')
.filter(s => s.platform === platform)
const lookupKeys = Array.from(
@@ -51,17 +48,29 @@ export function useMainSubscriptions() {
})
.filter(Boolean) as Subscription[]
- return organizeMainSubscriptionsByTier(subscriptions)
+ return {
+ active: rawSubscriptions.active,
+ available: organizeMainSubscriptionsByTier(subscriptions),
+ }
},
})
}
export function usePurchaseSubscription() {
+ const {currentAccount} = useSession()
+ const queryClient = useQueryClient()
+
return useMutation({
async mutationFn(product: Subscription['product']) {
if (product.platform === 'web') {
throw new Error('Cannot purchase web subscription on native')
}
+ // TODO don't do this here
+ Purchases.addCustomerInfoUpdateListener(_info => {
+ queryClient.invalidateQueries({
+ queryKey: ['availableSubscriptions', currentAccount!.did],
+ })
+ })
return Purchases.purchaseStoreProduct(product.data)
},
})
diff --git a/src/state/purchases/subscriptions/index.web.ts b/src/state/purchases/subscriptions/index.web.ts
index 90bcf1712f..233ebedb45 100644
--- a/src/state/purchases/subscriptions/index.web.ts
+++ b/src/state/purchases/subscriptions/index.web.ts
@@ -22,7 +22,7 @@ export function useMainSubscriptions() {
did: currentAccount!.did,
platform: 'web',
})
- const platformSubscriptions = rawSubscriptions.filter(
+ const platformSubscriptions = rawSubscriptions.available.filter(
s => s.platform === 'web',
)
const subscriptions = platformSubscriptions.map(sub => {
@@ -40,7 +40,10 @@ export function useMainSubscriptions() {
return subscription
})
- return organizeMainSubscriptionsByTier(subscriptions)
+ return {
+ active: rawSubscriptions.active,
+ available: organizeMainSubscriptionsByTier(subscriptions),
+ }
},
})
}
diff --git a/src/state/purchases/subscriptions/types.ts b/src/state/purchases/subscriptions/types.ts
index 6c8e33ce7a..31859490dd 100644
--- a/src/state/purchases/subscriptions/types.ts
+++ b/src/state/purchases/subscriptions/types.ts
@@ -1,6 +1,9 @@
import {PurchasesStoreProduct} from 'react-native-purchases'
-import {RawSubscriptionObjectBase} from '#/state/purchases/subscriptions/api/types'
+import {
+ RawSubscriptionObject,
+ RawSubscriptionObjectBase,
+} from '#/state/purchases/subscriptions/api/types'
export enum SubscriptionId {
Main0MonthlyAuto = 'main:0:monthly:auto',
@@ -11,6 +14,12 @@ export enum SubscriptionId {
Main2AnnualAuto = 'main:2:annual:auto',
}
+export enum EntitlementId {
+ Main0 = 'main:0',
+ Main1 = 'main:1',
+ Main2 = 'main:2',
+}
+
export type Subscription =
| RawSubscriptionObjectBase<{
platform: 'android'
@@ -50,6 +59,9 @@ export type Subscription =
}>
export type Subscriptions = {
- monthly: Subscription[]
- annual: Subscription[]
+ active: RawSubscriptionObject[]
+ available: {
+ monthly: Subscription[]
+ annual: Subscription[]
+ }
}
diff --git a/src/state/purchases/subscriptions/useEntitlements.tsx b/src/state/purchases/subscriptions/useEntitlements.tsx
new file mode 100644
index 0000000000..6d9dc821a9
--- /dev/null
+++ b/src/state/purchases/subscriptions/useEntitlements.tsx
@@ -0,0 +1,14 @@
+import {useQuery} from '@tanstack/react-query'
+
+import {getEntitlements} from '#/state/purchases/subscriptions/api'
+import {useSession} from '#/state/session'
+
+export function useEntitlements() {
+ const {currentAccount} = useSession()
+ return useQuery({
+ queryKey: ['entitlements', currentAccount!.did],
+ async queryFn() {
+ return getEntitlements({did: currentAccount!.did})
+ },
+ })
+}
diff --git a/src/state/purchases/subscriptions/util.ts b/src/state/purchases/subscriptions/util.ts
index e86c81aad9..b01e5e7890 100644
--- a/src/state/purchases/subscriptions/util.ts
+++ b/src/state/purchases/subscriptions/util.ts
@@ -2,8 +2,8 @@ import {Subscription, Subscriptions} from './types'
export function organizeMainSubscriptionsByTier(
subscriptions: Subscription[],
-): Subscriptions {
- const result: Subscriptions = {
+): Subscriptions['subscriptions'] {
+ const result: Subscriptions['subscriptions'] = {
monthly: [],
annual: [],
}
diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx
index 30b5139e64..5197bb835d 100644
--- a/src/view/shell/Drawer.tsx
+++ b/src/view/shell/Drawer.tsx
@@ -5,7 +5,6 @@ import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {StackActions, useNavigation} from '@react-navigation/native'
-import {IS_INTERNAL} from '#/lib/app-info'
import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
import {PressableScale} from '#/lib/custom-animations/PressableScale'
import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState'
@@ -276,12 +275,10 @@ let DrawerContent = ({}: {}): React.ReactNode => {
onPress={onPressProfile}
/>
- {IS_INTERNAL && (
-
- )}
+
>
) : (
<>