实现完整的勋章系统,包括勋章列表展示、自动弹窗展示和分享功能。 - 新增勋章列表页面,支持已获得和待解锁勋章的分类展示 - 在个人中心添加勋章预览模块,显示前3个勋章和总数统计 - 实现勋章展示弹窗,支持动画效果和玻璃态UI - 添加勋章分享功能,可生成分享卡片 - 新增 badgesSlice 管理勋章状态,包括获取、排序和计数逻辑 - 添加勋章服务 API 封装,支持获取勋章列表和标记已展示 - 完善中英文国际化文案
123 lines
4.1 KiB
TypeScript
123 lines
4.1 KiB
TypeScript
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;
|