feat(membership): 实现会员系统和购买流程
- 创建 MembershipModalContext 统一管理会员弹窗 - 优化 MembershipModal 产品套餐展示和购买流程 - 集成 RevenueCat SDK 并初始化内购功能 - 在个人中心添加会员 Banner,引导非会员用户订阅 - 修复日志工具的循环引用问题,确保错误信息正确记录 - 版本更新至 1.0.20 新增了完整的会员购买流程,包括套餐选择、购买确认、购买恢复等功能。会员 Banner 仅对非会员用户展示,已是会员的用户不会看到。同时优化了错误日志记录,避免循环引用导致的序列化失败。
This commit is contained in:
88
contexts/MembershipModalContext.tsx
Normal file
88
contexts/MembershipModalContext.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user