- 创建 MembershipModalContext 统一管理会员弹窗 - 优化 MembershipModal 产品套餐展示和购买流程 - 集成 RevenueCat SDK 并初始化内购功能 - 在个人中心添加会员 Banner,引导非会员用户订阅 - 修复日志工具的循环引用问题,确保错误信息正确记录 - 版本更新至 1.0.20 新增了完整的会员购买流程,包括套餐选择、购买确认、购买恢复等功能。会员 Banner 仅对非会员用户展示,已是会员的用户不会看到。同时优化了错误日志记录,避免循环引用导致的序列化失败。
89 lines
2.8 KiB
TypeScript
89 lines
2.8 KiB
TypeScript
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||
import Purchases from 'react-native-purchases';
|
||
|
||
import { MembershipModal } from '@/components/model/MembershipModal';
|
||
import { logger } from '@/utils/logger';
|
||
|
||
type MembershipModalOptions = {
|
||
onPurchaseSuccess?: () => void;
|
||
};
|
||
|
||
interface MembershipModalContextValue {
|
||
openMembershipModal: (options?: MembershipModalOptions) => void;
|
||
closeMembershipModal: () => void;
|
||
}
|
||
|
||
const MembershipModalContext = createContext<MembershipModalContextValue | null>(null);
|
||
|
||
export function MembershipModalProvider({ children }: { children: React.ReactNode }) {
|
||
const [visible, setVisible] = useState(false);
|
||
const [pendingSuccessCallback, setPendingSuccessCallback] = useState<(() => void) | undefined>();
|
||
const [isInitialized, setIsInitialized] = useState(false);
|
||
|
||
useEffect(() => {
|
||
// 直接使用生产环境的 API Key,避免环境变量问题
|
||
const iosApiKey = 'appl_lmVvuLWFlXlrEsnvxMzTnKapqcc';
|
||
|
||
const initializeRevenueCat = async () => {
|
||
try {
|
||
// 检查是否已经配置过,避免重复配置
|
||
if (!isInitialized) {
|
||
await Purchases.configure({ apiKey: iosApiKey });
|
||
setIsInitialized(true);
|
||
console.log('[MembershipModalProvider] RevenueCat SDK 初始化成功');
|
||
}
|
||
} catch (error) {
|
||
console.error('[MembershipModalProvider] RevenueCat SDK 初始化失败:', error);
|
||
// 初始化失败时不阻止应用正常运行
|
||
}
|
||
};
|
||
|
||
initializeRevenueCat();
|
||
}, [isInitialized]);
|
||
|
||
const openMembershipModal = useCallback((options?: MembershipModalOptions) => {
|
||
setPendingSuccessCallback(() => options?.onPurchaseSuccess);
|
||
setVisible(true);
|
||
}, []);
|
||
|
||
const closeMembershipModal = useCallback(() => {
|
||
setVisible(false);
|
||
setPendingSuccessCallback(undefined);
|
||
}, []);
|
||
|
||
const handlePurchaseSuccess = useCallback(() => {
|
||
pendingSuccessCallback?.();
|
||
}, [pendingSuccessCallback]);
|
||
|
||
const contextValue = useMemo(
|
||
() => ({
|
||
openMembershipModal,
|
||
closeMembershipModal,
|
||
}),
|
||
[closeMembershipModal, openMembershipModal],
|
||
);
|
||
|
||
return (
|
||
<MembershipModalContext.Provider value={contextValue}>
|
||
{children}
|
||
<MembershipModal
|
||
visible={visible}
|
||
onClose={closeMembershipModal}
|
||
onPurchaseSuccess={handlePurchaseSuccess}
|
||
/>
|
||
</MembershipModalContext.Provider>
|
||
);
|
||
}
|
||
|
||
export function useMembershipModal(): MembershipModalContextValue {
|
||
const context = useContext(MembershipModalContext);
|
||
|
||
if (!context) {
|
||
logger.error('useMembershipModal must be used within a MembershipModalProvider');
|
||
// 抛出错误而不是返回 undefined,确保类型安全
|
||
throw new Error('useMembershipModal must be used within a MembershipModalProvider');
|
||
}
|
||
|
||
return context;
|
||
}
|