Hook up native
This commit is contained in:
+11
-1
@@ -2,6 +2,7 @@ import 'react-native-url-polyfill/auto'
|
||||
import '#/lib/sentry' // must be near top
|
||||
import '#/view/icons'
|
||||
|
||||
import Purchases from 'react-native-purchases'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {GestureHandlerRootView} from 'react-native-gesture-handler'
|
||||
import {RootSiblingParent} from 'react-native-root-siblings'
|
||||
@@ -15,6 +16,8 @@ import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
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 {
|
||||
initialize,
|
||||
@@ -25,7 +28,6 @@ import {s} from '#/lib/styles'
|
||||
import {ThemeProvider} from '#/lib/ThemeContext'
|
||||
import I18nProvider from '#/locale/i18nProvider'
|
||||
import {logger} from '#/logger'
|
||||
import {isIOS} from '#/platform/detection'
|
||||
import {Provider as A11yProvider} from '#/state/a11y'
|
||||
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
||||
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
||||
@@ -179,6 +181,14 @@ function App() {
|
||||
Promise.all([initPersistedState(), ensureGeolocationResolved()]).then(() =>
|
||||
setReady(true),
|
||||
)
|
||||
|
||||
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})
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (!isReady) {
|
||||
|
||||
@@ -38,7 +38,7 @@ function DialogInner() {
|
||||
const t = useTheme()
|
||||
|
||||
const [offeringId, setOfferingId] = React.useState<OfferingId>(OfferingId.CoreMonthly)
|
||||
const {data: coreOffering} = useSubscriptionGroup(SubscriptionGroupId.Core)
|
||||
const {data: coreOffering, error} = useSubscriptionGroup(SubscriptionGroupId.Core)
|
||||
const {mutateAsync: purchaseOffering, isPending} = usePurchaseOffering()
|
||||
|
||||
const onPressSubscribe = async () => {
|
||||
@@ -62,7 +62,7 @@ function DialogInner() {
|
||||
|
||||
return (
|
||||
<Dialog.Inner label={_(msg`Bluesky Plus`)} style={[web({maxWidth: 1000})]}>
|
||||
<View style={[a.flex_1, a.gap_xl, gtMobile && a.flex_row]}>
|
||||
<View style={[a.gap_xl, gtMobile && a.flex_row]}>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
@@ -76,7 +76,7 @@ function DialogInner() {
|
||||
<MarketingBlurb />
|
||||
</View>
|
||||
</View>
|
||||
<View style={[a.flex_1, a.justify_end, a.gap_md]}>
|
||||
<View style={[a.justify_end, a.gap_md]}>
|
||||
<Text style={[a.font_bold, a.text_3xl]}>
|
||||
<Trans>Early bird discount!</Trans>
|
||||
</Text>
|
||||
@@ -116,7 +116,7 @@ function DialogInner() {
|
||||
<Trans>Monthly plan</Trans>
|
||||
</Text>
|
||||
<Text style={[a.text_lg, a.leading_normal, a.font_bold]}>
|
||||
<Trans>$6/month</Trans>
|
||||
<Trans>$8/month</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
@@ -160,7 +160,7 @@ function DialogInner() {
|
||||
<Trans>Annual plan</Trans>
|
||||
</Text>
|
||||
<Text style={[a.text_lg, a.leading_normal, a.font_bold]}>
|
||||
<Trans>$60/year</Trans>
|
||||
<Trans>$80/year</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
|
||||
@@ -9,3 +9,7 @@ export const LOG_LEVEL = (process.env.EXPO_PUBLIC_LOG_LEVEL || 'info') as
|
||||
| 'error'
|
||||
|
||||
export const PLUS_SERVICE_URL = process.env.EXPO_PUBLIC_PLUS_SERVICE_URL
|
||||
export const RC_GOOGLE_PUBLIC_KEY =
|
||||
process.env.EXPO_PUBLIC_RC_GOOGLE_PUBLIC_KEY || ''
|
||||
export const RC_APPLE_PUBLIC_KEY =
|
||||
process.env.EXPO_PUBLIC_RC_APPLE_PUBLIC_KEY || ''
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { PurchasesStoreProduct } from 'react-native-purchases'
|
||||
|
||||
export enum EntitlementId {
|
||||
Core = 'core',
|
||||
}
|
||||
@@ -22,7 +24,7 @@ export type Offering =
|
||||
id: OfferingId
|
||||
platform: PlatformId.Ios | PlatformId.Android
|
||||
price: number
|
||||
package: undefined
|
||||
package: PurchasesStoreProduct
|
||||
}
|
||||
| {
|
||||
id: OfferingId
|
||||
|
||||
@@ -1,28 +1,41 @@
|
||||
import React from 'react'
|
||||
import {useQuery, useMutation} from '@tanstack/react-query'
|
||||
import Purchases from 'react-native-purchases'
|
||||
|
||||
import {isIOS} from '#/platform/detection'
|
||||
import {api} from '#/state/purchases/subscriptions/api'
|
||||
import {OfferingId, PlatformId, Offering, SubscriptionGroupId} from '#/state/purchases/subscriptions/types'
|
||||
|
||||
export function useSubscriptionGroup(group: SubscriptionGroupId) {
|
||||
return useQuery<{ offerings: Offering[] }>({
|
||||
queryKey: ['subscriptions', 'core'],
|
||||
queryFn() {
|
||||
queryKey: ['subscription-group', group],
|
||||
async queryFn() {
|
||||
const platform = isIOS ? 'ios' : 'android'
|
||||
const { data, error, response } = await api(`/subscriptions/${group}?platform=${platform}`).json()
|
||||
if (error || !data) {
|
||||
console.log(error, response)
|
||||
throw new Error('Failed to fetch subscription group')
|
||||
}
|
||||
|
||||
const { offerings } = data
|
||||
const productIds = offerings.map((o: any) => {
|
||||
return isIOS ? o.productId : o.productId.split(':')[0]
|
||||
})
|
||||
// console.log({ offers: false })
|
||||
// const offers = await Purchases.getOfferings()
|
||||
// console.log({ offers })
|
||||
const products = await Purchases.getProducts(productIds)
|
||||
|
||||
return {
|
||||
offerings: [
|
||||
{
|
||||
id: OfferingId.CoreMonthly,
|
||||
offerings: offerings.map((o: any) => {
|
||||
const product = products.find((p: any) => p.identifier === o.productId)
|
||||
return {
|
||||
id: o.id as OfferingId,
|
||||
platform: isIOS ? PlatformId.Ios : PlatformId.Android,
|
||||
price: 800,
|
||||
package: undefined,
|
||||
},
|
||||
{
|
||||
id: OfferingId.CoreAnnual,
|
||||
platform: isIOS ? PlatformId.Ios : PlatformId.Android,
|
||||
price: 8000,
|
||||
package: undefined,
|
||||
},
|
||||
]
|
||||
price: product?.priceString,
|
||||
package: product,
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -35,6 +48,13 @@ export function usePurchaseOffering() {
|
||||
email,
|
||||
offering,
|
||||
}: { did: string, email: string, offering: Offering }) {
|
||||
if (offering.platform === PlatformId.Web) {
|
||||
throw new Error('Unsupported platform')
|
||||
}
|
||||
|
||||
Purchases.logIn(did)
|
||||
Purchases.setEmail(email)
|
||||
Purchases.purchaseStoreProduct(offering.package)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import {DesktopSearch} from '#/view/shell/desktop/Search'
|
||||
import {atoms as a, tokens, useTheme, web} from '#/alf'
|
||||
import {Button} from '#/components/Button'
|
||||
import {useDialogControl} from '#/components/Dialog'
|
||||
import {SubscriptionsDialog} from '#/components/dialogs/SubscriptionsDialog'
|
||||
import {BlueskyPlus} from '#/components/dialogs/BlueskyPlus'
|
||||
import {GradientFill} from '#/components/GradientFill'
|
||||
import {Logotype} from '#/components/icons/BlueskyPlus'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
@@ -99,7 +99,7 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
|
||||
</View>
|
||||
</View>
|
||||
</Button>
|
||||
<SubscriptionsDialog control={subscriptionsDialogControl} />
|
||||
<BlueskyPlus control={subscriptionsDialogControl} />
|
||||
</View>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user