import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; import Purchases from 'react-native-purchases'; import { extractMembershipProductsFromOfferings, getPlanMetaById, hasActiveMembership, pickActiveProductId, resolvePlanDisplayName, summarizeProducts, type MembershipPlanSummary, } from '@/services/membership'; import type { RootState } from './index'; import { logout, updateProfile } from './userSlice'; export interface MembershipState { plans: MembershipPlanSummary[]; activePlanId: string | null; activePlanName: string | null; hasActiveMembership: boolean; loading: boolean; error: string | null; lastUpdated: number | null; activeProductIds: string[]; } const initialState: MembershipState = { plans: [], activePlanId: null, activePlanName: null, hasActiveMembership: false, loading: false, error: null, lastUpdated: null, activeProductIds: [], }; export const fetchMembershipData = createAsyncThunk< { plans: MembershipPlanSummary[]; activePlanId: string | null; activePlanName: string | null; hasActiveMembership: boolean; activeProductIds: string[]; lastUpdated: number; }, void, { rejectValue: string } >('membership/fetchMembershipData', async (_, { rejectWithValue, dispatch }) => { try { const offerings = await Purchases.getOfferings(); const products = extractMembershipProductsFromOfferings(offerings); const plans = summarizeProducts(products); const customerInfo = await Purchases.getCustomerInfo(); const { activeProductId, activeProductIds } = pickActiveProductId( customerInfo, products, ); const activePlanMeta = activeProductId ? getPlanMetaById(activeProductId) : undefined; const activeProduct = activeProductId ? products.find((product) => product.identifier === activeProductId) ?? null : null; const hasActive = hasActiveMembership(customerInfo); const activePlanName = hasActive ? resolvePlanDisplayName(activeProduct, activePlanMeta) : null; dispatch( updateProfile({ vipPlanName: activePlanName ?? undefined, isVip: hasActive, }), ); return { plans, activePlanId: activeProductId, activePlanName, hasActiveMembership: hasActive, activeProductIds, lastUpdated: Date.now(), }; } catch (error: any) { const message = error?.message ?? (typeof error === 'string' ? error : '获取会员信息失败'); return rejectWithValue(message); } }); const membershipSlice = createSlice({ name: 'membership', initialState, reducers: {}, extraReducers: (builder) => { builder .addCase(fetchMembershipData.pending, (state) => { state.loading = true; state.error = null; }) .addCase(fetchMembershipData.fulfilled, (state, action) => { state.loading = false; state.error = null; state.plans = action.payload.plans; state.activePlanId = action.payload.activePlanId; state.activePlanName = action.payload.activePlanName; state.hasActiveMembership = action.payload.hasActiveMembership; state.activeProductIds = action.payload.activeProductIds; state.lastUpdated = action.payload.lastUpdated; }) .addCase(fetchMembershipData.rejected, (state, action) => { state.loading = false; state.error = (action.payload as string) ?? '获取会员信息失败,请稍后重试'; }) .addCase(logout.fulfilled, () => ({ ...initialState, plans: [], activeProductIds: [], })); }, }); export const selectMembershipState = (state: RootState): MembershipState => state.membership; export const selectMembershipPlans = (state: RootState) => state.membership.plans; export const selectActiveMembershipPlanName = (state: RootState) => state.membership.activePlanName; export default membershipSlice.reducer;