Files
digital-pilates/contexts/MembershipModalContext.tsx
richarjiang fcf1be211f feat(vip): 实现VIP服务权限控制和食物识别功能限制
- 添加VIP服务权限检查hook,支持免费使用次数限制
- 为食物识别功能添加登录验证和VIP权限检查
- 优化RevenueCat用户标识同步逻辑
- 修复会员购买状态检查的类型安全问题
- 为营养成分分析添加登录验证
2025-10-29 09:44:30 +08:00

127 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import Purchases from 'react-native-purchases';
import { MembershipModal } from '@/components/model/MembershipModal';
import { useAppSelector } from '@/hooks/redux';
import { selectUserProfile } from '@/store/userSlice';
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);
// 获取用户信息用于RevenueCat用户标识
const userProfile = useAppSelector(selectUserProfile);
useEffect(() => {
// 直接使用生产环境的 API Key避免环境变量问题
const iosApiKey = 'appl_lmVvuLWFlXlrEsnvxMzTnKapqcc';
const initializeRevenueCat = async () => {
try {
// 检查是否已经配置过,避免重复配置
if (!isInitialized) {
// 如果有用户ID在配置时传入用户标识
const configOptions: any = { apiKey: iosApiKey };
if (userProfile?.id) {
configOptions.appUserID = userProfile.id;
logger.info('[MembershipModalProvider] RevenueCat SDK 初始化使用用户ID:', userProfile.id);
} else {
logger.info('[MembershipModalProvider] RevenueCat SDK 初始化未设置用户ID');
}
await Purchases.configure(configOptions);
setIsInitialized(true);
logger.info('[MembershipModalProvider] RevenueCat SDK 初始化成功');
}
} catch (error) {
logger.error('[MembershipModalProvider] RevenueCat SDK 初始化失败:', error);
// 初始化失败时不阻止应用正常运行
}
};
initializeRevenueCat();
}, [isInitialized, userProfile?.id]);
// 监听用户登录状态变化在用户登录后更新RevenueCat的用户标识
useEffect(() => {
const updateRevenueCatUser = async () => {
if (isInitialized && userProfile?.id) {
try {
// 检查当前RevenueCat的用户标识
const customerInfo = await Purchases.getCustomerInfo();
const currentAppUserID = customerInfo.originalAppUserId;
// 如果当前用户ID与RevenueCat中的用户ID不同则更新
if (currentAppUserID !== userProfile.id) {
console.log('[MembershipModalProvider] 更新RevenueCat用户标识:', userProfile.id);
await Purchases.logIn(userProfile.id);
}
} catch (error) {
console.error('[MembershipModalProvider] 更新RevenueCat用户标识失败:', error);
}
}
};
updateRevenueCatUser();
}, [userProfile?.id, 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;
}