Hook up native

This commit is contained in:
Eric Bailey
2024-11-19 17:00:04 -06:00
parent df9101774c
commit 5e4bf58811
8 changed files with 76 additions and 52 deletions
+1
View File
@@ -181,6 +181,7 @@
"react-native-pager-view": "6.2.3",
"react-native-picker-select": "^9.1.3",
"react-native-progress": "bluesky-social/react-native-progress",
"react-native-purchases": "^8.2.7",
"react-native-qrcode-styled": "^0.3.1",
"react-native-reanimated": "^3.16.1",
"react-native-root-siblings": "^4.1.1",
+11 -1
View File
@@ -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) {
+5 -5
View File
@@ -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
+4
View File
@@ -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 || ''
+3 -1
View File
@@ -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)
}
})
}
+2 -2
View File
@@ -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>
)}
+15 -28
View File
@@ -5931,6 +5931,11 @@
resolved "https://registry.yarnpkg.com/@remirror/core-constants/-/core-constants-3.0.0.tgz#96fdb89d25c62e7b6a5d08caf0ce5114370e3b8f"
integrity sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==
"@revenuecat/purchases-typescript-internal@13.9.0":
version "13.9.0"
resolved "https://registry.yarnpkg.com/@revenuecat/purchases-typescript-internal/-/purchases-typescript-internal-13.9.0.tgz#bd75906dadf7617fb63230a00af4b67010b15bcd"
integrity sha512-/46OD9U3U44VekfSjukQ5Ht+OKll35zO/ihSJ+OakYMjnq9sJKSWUCwC/bTHThMvSgMsmnQy2VOXK1ZR7MxC0A==
"@rnx-kit/chromium-edge-launcher@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@rnx-kit/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz#c0df8ea00a902c7a417cd9655aab06de398b939c"
@@ -16194,6 +16199,13 @@ react-native-progress@bluesky-social/react-native-progress:
dependencies:
prop-types "^15.7.2"
react-native-purchases@^8.2.7:
version "8.2.7"
resolved "https://registry.yarnpkg.com/react-native-purchases/-/react-native-purchases-8.2.7.tgz#39683c6c7faf9866364bdcb49fdaa20e26d9ebd5"
integrity sha512-kdshoP5atDXZLryl3oo2xQ8im+12EUBboE8x48nJTdnExmdmX/OOPuX4KxGfln2skOGW8k8m9juVj+eqrzHN5A==
dependencies:
"@revenuecat/purchases-typescript-internal" "13.9.0"
react-native-qrcode-styled@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/react-native-qrcode-styled/-/react-native-qrcode-styled-0.3.1.tgz#be6a0fab173511b0d3d8d71588771c2230982dbf"
@@ -17621,16 +17633,7 @@ string-natural-compare@^3.0.1:
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -17730,7 +17733,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -17744,13 +17747,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -19073,7 +19069,7 @@ wordwrap@^1.0.0:
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -19091,15 +19087,6 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"