feat(membership): 重构会员系统架构并优化VIP卡片显示
- 创建独立的会员服务模块 services/membership.ts,统一管理会员计划元数据和工具函数 - 新增 membershipSlice Redux状态管理,集中处理会员数据和状态 - 重构个人中心VIP会员卡片,支持动态显示会员计划和有效期 - 优化会员购买弹窗,使用统一的会员计划配置 - 改进会员数据获取流程,确保状态同步和一致性
This commit is contained in:
138
store/membershipSlice.ts
Normal file
138
store/membershipSlice.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user