From 5e4bf58811d83918e4d70de2bb8d56394600296d Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 19 Nov 2024 17:00:04 -0600 Subject: [PATCH] Hook up native --- package.json | 1 + src/App.native.tsx | 12 ++++- src/components/dialogs/BlueskyPlus/index.tsx | 10 ++-- src/env.ts | 4 ++ src/state/purchases/subscriptions/types.ts | 4 +- .../useSubscriptionGroup/index.ts | 50 +++++++++++++------ src/view/shell/desktop/RightNav.tsx | 4 +- yarn.lock | 43 ++++++---------- 8 files changed, 76 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index 47cbe7f2bb..c83b5454fe 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/App.native.tsx b/src/App.native.tsx index 0d8d7a92e6..d3b816a106 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -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) { diff --git a/src/components/dialogs/BlueskyPlus/index.tsx b/src/components/dialogs/BlueskyPlus/index.tsx index 8695b7432a..cf008f7545 100644 --- a/src/components/dialogs/BlueskyPlus/index.tsx +++ b/src/components/dialogs/BlueskyPlus/index.tsx @@ -38,7 +38,7 @@ function DialogInner() { const t = useTheme() const [offeringId, setOfferingId] = React.useState(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 ( - + - + Early bird discount! @@ -116,7 +116,7 @@ function DialogInner() { Monthly plan - $6/month + $8/month Annual plan - $60/year + $80/year ({ - 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) } }) } diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx index d497ec0e64..53465f33cf 100644 --- a/src/view/shell/desktop/RightNav.tsx +++ b/src/view/shell/desktop/RightNav.tsx @@ -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}) { - + )} diff --git a/yarn.lock b/yarn.lock index 74293833a1..cbd87e9130 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"