Files
digital-pilates/store/index.ts
richarjiang 705d921c14 feat(badges): 添加勋章系统和展示功能
实现完整的勋章系统,包括勋章列表展示、自动弹窗展示和分享功能。

- 新增勋章列表页面,支持已获得和待解锁勋章的分类展示
- 在个人中心添加勋章预览模块,显示前3个勋章和总数统计
- 实现勋章展示弹窗,支持动画效果和玻璃态UI
- 添加勋章分享功能,可生成分享卡片
- 新增 badgesSlice 管理勋章状态,包括获取、排序和计数逻辑
- 添加勋章服务 API 封装,支持获取勋章列表和标记已展示
- 完善中英文国际化文案
2025-11-14 17:17:17 +08:00

123 lines
4.1 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 { persistActiveFastingSchedule } from '@/utils/fasting';
import { configureStore, createListenerMiddleware } from '@reduxjs/toolkit';
import badgesReducer from './badgesSlice';
import challengesReducer from './challengesSlice';
import checkinReducer, { addExercise, autoSyncCheckin, removeExercise, replaceExercises, setNote, toggleExerciseCompleted } from './checkinSlice';
import circumferenceReducer from './circumferenceSlice';
import exerciseLibraryReducer from './exerciseLibrarySlice';
import fastingReducer, {
clearActiveSchedule,
completeActiveSchedule,
rescheduleActivePlan,
scheduleFastingPlan,
setRecommendedSchedule,
} from './fastingSlice';
import foodLibraryReducer from './foodLibrarySlice';
import foodRecognitionReducer from './foodRecognitionSlice';
import healthReducer from './healthSlice';
import medicationsReducer from './medicationsSlice';
import membershipReducer from './membershipSlice';
import moodReducer from './moodSlice';
import nutritionReducer from './nutritionSlice';
import scheduleExerciseReducer from './scheduleExerciseSlice';
import trainingPlanReducer from './trainingPlanSlice';
import userReducer from './userSlice';
import waterReducer from './waterSlice';
import workoutReducer from './workoutSlice';
// 创建监听器中间件来处理自动同步
const listenerMiddleware = createListenerMiddleware();
// 监听所有数据变动的 actions触发自动同步
const syncActions = [addExercise, removeExercise, replaceExercises, toggleExerciseCompleted, setNote];
syncActions.forEach(action => {
listenerMiddleware.startListening({
actionCreator: action,
effect: async (action, listenerApi) => {
const state = listenerApi.getState() as any;
const date = action.payload?.date;
if (!date) return;
// 延迟一下,避免在同一事件循环中重复触发
await new Promise(resolve => setTimeout(resolve, 100));
// 检查是否还有待同步的日期
const currentState = listenerApi.getState() as any;
const pendingSyncDates = currentState?.checkin?.pendingSyncDates || [];
if (pendingSyncDates.includes(date)) {
listenerApi.dispatch(autoSyncCheckin({ date }));
}
},
});
});
const persistFastingState = async (listenerApi: any) => {
const state = listenerApi.getState() as { fasting?: { activeSchedule?: any } };
await persistActiveFastingSchedule(state?.fasting?.activeSchedule ?? null);
};
listenerMiddleware.startListening({
actionCreator: scheduleFastingPlan,
effect: async (_, listenerApi) => {
await persistFastingState(listenerApi);
},
});
listenerMiddleware.startListening({
actionCreator: rescheduleActivePlan,
effect: async (_, listenerApi) => {
await persistFastingState(listenerApi);
},
});
listenerMiddleware.startListening({
actionCreator: completeActiveSchedule,
effect: async (_, listenerApi) => {
await persistFastingState(listenerApi);
},
});
listenerMiddleware.startListening({
actionCreator: setRecommendedSchedule,
effect: async (_, listenerApi) => {
await persistFastingState(listenerApi);
},
});
listenerMiddleware.startListening({
actionCreator: clearActiveSchedule,
effect: async () => {
await persistActiveFastingSchedule(null);
},
});
export const store = configureStore({
reducer: {
user: userReducer,
challenges: challengesReducer,
checkin: checkinReducer,
circumference: circumferenceReducer,
health: healthReducer,
mood: moodReducer,
nutrition: nutritionReducer,
trainingPlan: trainingPlanReducer,
scheduleExercise: scheduleExerciseReducer,
exerciseLibrary: exerciseLibraryReducer,
foodLibrary: foodLibraryReducer,
foodRecognition: foodRecognitionReducer,
membership: membershipReducer,
workout: workoutReducer,
water: waterReducer,
fasting: fastingReducer,
medications: medicationsReducer,
badges: badgesReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().prepend(listenerMiddleware.middleware),
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;