fix(mobile): simplify iOS subscription management (#4947)
This commit is contained in:
parent
ce5e91962c
commit
d27261d76e
|
|
@ -316,7 +316,6 @@ export const PlanScreen: NavigationControllerView = () => {
|
|||
isPurchasing,
|
||||
isRestoring,
|
||||
loadSubscriptions,
|
||||
openSubscriptionManagement,
|
||||
requestSubscriptionPurchase,
|
||||
restoreSubscriptionPurchases,
|
||||
} = useAppleIAP()
|
||||
|
|
@ -485,15 +484,8 @@ export const PlanScreen: NavigationControllerView = () => {
|
|||
})
|
||||
|
||||
const handleManageSubscription = useCallback(() => {
|
||||
if (activeSubscription?.source === "apple") {
|
||||
void openSubscriptionManagement().catch(() => {
|
||||
toast.error(t("subscription.actions.manage_error"))
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
billingPortalMutation.mutate()
|
||||
}, [activeSubscription?.source, billingPortalMutation, openSubscriptionManagement, t])
|
||||
}, [billingPortalMutation])
|
||||
|
||||
if (!isPaymentEnabled || sortedPlans.length === 0) {
|
||||
return (
|
||||
|
|
@ -971,6 +963,7 @@ const PlanAction = ({
|
|||
const { t } = useTranslation("settings")
|
||||
|
||||
const canManageSubscription = !!activeSubscription?.canManage
|
||||
const isAppleSubscription = activeSubscription?.source === "apple"
|
||||
const isCanceled = activeSubscription?.status === "canceled"
|
||||
const periodEnd = activeSubscription?.trialEnd ?? activeSubscription?.periodEnd
|
||||
const effectivePeriodEnd = periodEnd ? new Date(periodEnd) : null
|
||||
|
|
@ -1025,23 +1018,29 @@ const PlanAction = ({
|
|||
</Text>
|
||||
</View>
|
||||
)}
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
onPress={onManageSubscription}
|
||||
disabled={!canManageSubscription || isManaging}
|
||||
className={cn(
|
||||
"h-11 items-center justify-center rounded-full border border-opaque-separator/60",
|
||||
!canManageSubscription && "opacity-50",
|
||||
)}
|
||||
>
|
||||
{isManaging ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<Text className="text-base font-medium text-label">
|
||||
{canManageSubscription ? t("plan.manage_subscription") : t("plan.current_plan")}
|
||||
</Text>
|
||||
)}
|
||||
</Pressable>
|
||||
{isAppleSubscription ? (
|
||||
<Text className="rounded-2xl bg-secondary-system-fill px-4 py-3 text-center text-sm leading-5 text-secondary-label">
|
||||
{t("plan.manage_subscription_hint_apple")}
|
||||
</Text>
|
||||
) : (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
onPress={onManageSubscription}
|
||||
disabled={!canManageSubscription || isManaging}
|
||||
className={cn(
|
||||
"h-11 items-center justify-center rounded-full border border-opaque-separator/60",
|
||||
!canManageSubscription && "opacity-50",
|
||||
)}
|
||||
>
|
||||
{isManaging ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<Text className="text-base font-medium text-label">
|
||||
{canManageSubscription ? t("plan.manage_subscription") : t("plan.current_plan")}
|
||||
</Text>
|
||||
)}
|
||||
</Pressable>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ import { whoamiQueryKey } from "@follow/store/user/hooks"
|
|||
import { userSyncService } from "@follow/store/user/store"
|
||||
import { requireNativeModule } from "expo"
|
||||
import type { ProductPurchase, SubscriptionProduct } from "expo-iap"
|
||||
import { getTransactionJws, showManageSubscriptions, useIAP } from "expo-iap"
|
||||
import { openURL } from "expo-linking"
|
||||
import { getTransactionJws, useIAP } from "expo-iap"
|
||||
import type { PropsWithChildren } from "react"
|
||||
import { createContext, use, useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
|
@ -15,7 +14,6 @@ import { proxyEnv } from "@/src/lib/proxy-env"
|
|||
import { queryClient } from "@/src/lib/query-client"
|
||||
import { toast } from "@/src/lib/toast"
|
||||
|
||||
const APPLE_SUBSCRIPTION_MANAGEMENT_URL = "https://apps.apple.com/account/subscriptions"
|
||||
const billingSubscriptionQueryKey = ["billingSubscription"]
|
||||
|
||||
type BillingSubscriptionResponse = {
|
||||
|
|
@ -40,12 +38,17 @@ type AppleIAPContextValue = {
|
|||
appAccountToken?: string | null
|
||||
}) => Promise<void>
|
||||
restoreSubscriptionPurchases: () => Promise<void>
|
||||
openSubscriptionManagement: () => Promise<void>
|
||||
}
|
||||
|
||||
const AppleIAPContext = createContext<AppleIAPContextValue | null>(null)
|
||||
|
||||
const noopAsync = async () => {}
|
||||
const refreshBillingState = async () => {
|
||||
await Promise.allSettled([
|
||||
userSyncService.whoami(),
|
||||
queryClient.invalidateQueries({ queryKey: whoamiQueryKey }),
|
||||
queryClient.invalidateQueries({ queryKey: billingSubscriptionQueryKey }),
|
||||
])
|
||||
}
|
||||
|
||||
export const AppleIAPProvider = ({ children }: PropsWithChildren) => {
|
||||
const { t } = useTranslation("settings")
|
||||
|
|
@ -107,14 +110,6 @@ export const AppleIAPProvider = ({ children }: PropsWithChildren) => {
|
|||
availablePurchasesRef.current = availablePurchases
|
||||
}, [availablePurchases])
|
||||
|
||||
const refreshBillingState = useCallback(async () => {
|
||||
await Promise.allSettled([
|
||||
userSyncService.whoami(),
|
||||
queryClient.invalidateQueries({ queryKey: whoamiQueryKey }),
|
||||
queryClient.invalidateQueries({ queryKey: billingSubscriptionQueryKey }),
|
||||
])
|
||||
}, [])
|
||||
|
||||
const verifyPurchase = useCallback(
|
||||
async (purchase: ProductPurchase) => {
|
||||
const productId = purchase.id
|
||||
|
|
@ -186,14 +181,7 @@ export const AppleIAPProvider = ({ children }: PropsWithChildren) => {
|
|||
setIsProcessingPurchase(false)
|
||||
}
|
||||
})()
|
||||
}, [
|
||||
currentPurchase,
|
||||
finishTransaction,
|
||||
knownSubscriptionIds,
|
||||
refreshBillingState,
|
||||
t,
|
||||
verifyPurchase,
|
||||
])
|
||||
}, [currentPurchase, finishTransaction, knownSubscriptionIds, t, verifyPurchase])
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentPurchaseError) {
|
||||
|
|
@ -267,7 +255,7 @@ export const AppleIAPProvider = ({ children }: PropsWithChildren) => {
|
|||
throw error
|
||||
}
|
||||
},
|
||||
[refreshBillingState, requestPurchase, storeKitTestHelper],
|
||||
[requestPurchase, storeKitTestHelper],
|
||||
)
|
||||
|
||||
const restoreSubscriptionPurchases = useCallback(async () => {
|
||||
|
|
@ -322,20 +310,7 @@ export const AppleIAPProvider = ({ children }: PropsWithChildren) => {
|
|||
} finally {
|
||||
setIsRestoring(false)
|
||||
}
|
||||
}, [knownSubscriptionIds, refreshBillingState, restorePurchases, t, verifyPurchase])
|
||||
|
||||
const openSubscriptionManagement = useCallback(async () => {
|
||||
if (Platform.OS !== "ios") {
|
||||
await noopAsync()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await showManageSubscriptions()
|
||||
} catch {
|
||||
await openURL(APPLE_SUBSCRIPTION_MANAGEMENT_URL)
|
||||
}
|
||||
}, [])
|
||||
}, [knownSubscriptionIds, restorePurchases, t, verifyPurchase])
|
||||
|
||||
const contextValue = useMemo<AppleIAPContextValue>(
|
||||
() => ({
|
||||
|
|
@ -347,7 +322,6 @@ export const AppleIAPProvider = ({ children }: PropsWithChildren) => {
|
|||
loadSubscriptions,
|
||||
requestSubscriptionPurchase,
|
||||
restoreSubscriptionPurchases,
|
||||
openSubscriptionManagement,
|
||||
}),
|
||||
[
|
||||
connected,
|
||||
|
|
@ -358,7 +332,6 @@ export const AppleIAPProvider = ({ children }: PropsWithChildren) => {
|
|||
loadSubscriptions,
|
||||
requestSubscriptionPurchase,
|
||||
restoreSubscriptionPurchases,
|
||||
openSubscriptionManagement,
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -615,6 +615,7 @@
|
|||
"plan.features.PRIVATE_SUBSCRIPTION": "Private Subscriptions",
|
||||
"plan.features.SECURE_IMAGE_PROXY": "Secure Image Proxy",
|
||||
"plan.manage_subscription": "Manage Subscription",
|
||||
"plan.manage_subscription_hint_apple": "Manage: Apple Account > Subscriptions",
|
||||
"plan.renews": "Renews {{date}}",
|
||||
"plan.trial_ends": "Trial ends {{date}}",
|
||||
"privacy.privacy": "Privacy",
|
||||
|
|
|
|||
|
|
@ -615,6 +615,7 @@
|
|||
"plan.features.PRIVATE_SUBSCRIPTION": "Abonnements privés",
|
||||
"plan.features.SECURE_IMAGE_PROXY": "Proxy d'image sécurisé",
|
||||
"plan.manage_subscription": "Gérer l'abonnement",
|
||||
"plan.manage_subscription_hint_apple": "Gérer : Compte Apple > Abonnements",
|
||||
"plan.renews": "Renouvellement le {{date}}",
|
||||
"plan.trial_ends": "L'essai se termine le {{date}}",
|
||||
"privacy.privacy": "Confidentialité",
|
||||
|
|
|
|||
|
|
@ -615,6 +615,7 @@
|
|||
"plan.features.PRIVATE_SUBSCRIPTION": "プライベートサブスクリプション",
|
||||
"plan.features.SECURE_IMAGE_PROXY": "セキュア画像プロキシ",
|
||||
"plan.manage_subscription": "サブスクリプションを管理",
|
||||
"plan.manage_subscription_hint_apple": "管理:Apple Account > サブスクリプション",
|
||||
"plan.renews": "更新日 {{date}}",
|
||||
"plan.trial_ends": "トライアル終了日 {{date}}",
|
||||
"privacy.privacy": "プライバシー",
|
||||
|
|
|
|||
|
|
@ -615,6 +615,7 @@
|
|||
"plan.features.PRIVATE_SUBSCRIPTION": "私有订阅",
|
||||
"plan.features.SECURE_IMAGE_PROXY": "安全图片代理",
|
||||
"plan.manage_subscription": "管理订阅",
|
||||
"plan.manage_subscription_hint_apple": "管理:Apple 账户 > 订阅",
|
||||
"plan.renews": "续订日期 {{date}}",
|
||||
"plan.trial_ends": "试用期至 {{date}}",
|
||||
"privacy.privacy": "隐私政策",
|
||||
|
|
|
|||
|
|
@ -615,6 +615,7 @@
|
|||
"plan.features.PRIVATE_SUBSCRIPTION": "私有訂閱",
|
||||
"plan.features.SECURE_IMAGE_PROXY": "安全圖片代理",
|
||||
"plan.manage_subscription": "管理訂閱",
|
||||
"plan.manage_subscription_hint_apple": "管理:Apple 帳號 > 訂閱",
|
||||
"plan.renews": "續訂日期 {{date}}",
|
||||
"plan.trial_ends": "試用期至 {{date}}",
|
||||
"privacy.privacy": "隱私政策",
|
||||
|
|
|
|||
Loading…
Reference in New Issue