feat(challenges): 添加自定义挑战功能和多语言支持
- 新增自定义挑战创建页面,支持设置挑战类型、时间范围、目标值等 - 实现挑战邀请码系统,支持通过邀请码加入自定义挑战 - 完善挑战详情页面的多语言翻译支持 - 优化用户认证状态检查逻辑,使用token作为主要判断依据 - 添加阿里字体文件支持,提升UI显示效果 - 改进确认弹窗组件,支持Liquid Glass效果和自定义内容 - 优化应用启动流程,直接读取onboarding状态而非预加载用户数据
This commit is contained in:
@@ -5,65 +5,51 @@ import AsyncStorage from '@/utils/kvStore';
|
||||
import { createAsyncThunk, createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
// 预加载的用户数据存储
|
||||
let preloadedUserData: {
|
||||
token: string | null;
|
||||
profile: UserProfile;
|
||||
privacyAgreed: boolean;
|
||||
onboardingCompleted: boolean;
|
||||
} | null = null;
|
||||
|
||||
// 预加载用户数据的函数
|
||||
export async function preloadUserData() {
|
||||
/**
|
||||
* 同步加载用户数据(在 Redux store 初始化时立即执行)
|
||||
* 使用 getItemSync 确保数据在 store 创建前就已加载
|
||||
*/
|
||||
function loadUserDataSync() {
|
||||
try {
|
||||
const [profileStr, privacyAgreedStr, token, onboardingCompletedStr] = await Promise.all([
|
||||
AsyncStorage.getItem(STORAGE_KEYS.userProfile),
|
||||
AsyncStorage.getItem(STORAGE_KEYS.privacyAgreed),
|
||||
AsyncStorage.getItem(STORAGE_KEYS.authToken),
|
||||
AsyncStorage.getItem(STORAGE_KEYS.onboardingCompleted),
|
||||
]);
|
||||
const profileStr = AsyncStorage.getItemSync(STORAGE_KEYS.userProfile);
|
||||
const token = AsyncStorage.getItemSync(STORAGE_KEYS.authToken);
|
||||
const onboardingCompletedStr = AsyncStorage.getItemSync(STORAGE_KEYS.onboardingCompleted);
|
||||
|
||||
let profile: UserProfile = {
|
||||
memberNumber: 0
|
||||
};
|
||||
|
||||
if (profileStr) {
|
||||
try {
|
||||
profile = JSON.parse(profileStr) as UserProfile;
|
||||
} catch {
|
||||
profile = {
|
||||
memberNumber: 0
|
||||
};
|
||||
profile = { memberNumber: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
const privacyAgreed = privacyAgreedStr === 'true';
|
||||
const onboardingCompleted = onboardingCompletedStr === 'true';
|
||||
|
||||
// 如果有 token,需要设置到 API 客户端
|
||||
// 如果有 token,需要异步设置到 API 客户端(但不阻塞初始化)
|
||||
if (token) {
|
||||
await setAuthToken(token);
|
||||
setAuthToken(token).catch(err => {
|
||||
console.error('设置 auth token 失败:', err);
|
||||
});
|
||||
}
|
||||
|
||||
preloadedUserData = { token, profile, privacyAgreed, onboardingCompleted };
|
||||
return preloadedUserData;
|
||||
return { token, profile, onboardingCompleted };
|
||||
} catch (error) {
|
||||
console.error('预加载用户数据失败:', error);
|
||||
preloadedUserData = {
|
||||
console.error('同步加载用户数据失败:', error);
|
||||
return {
|
||||
token: null,
|
||||
profile: {
|
||||
memberNumber: 0
|
||||
},
|
||||
privacyAgreed: false,
|
||||
profile: { memberNumber: 0 },
|
||||
onboardingCompleted: false
|
||||
};
|
||||
return preloadedUserData;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取预加载的用户数据
|
||||
function getPreloadedUserData() {
|
||||
return preloadedUserData || { token: null, profile: {}, privacyAgreed: false, onboardingCompleted: false };
|
||||
}
|
||||
// 在模块加载时立即同步加载用户数据
|
||||
const preloadedUserData = loadUserDataSync();
|
||||
|
||||
|
||||
export type Gender = 'male' | 'female' | '';
|
||||
|
||||
@@ -120,22 +106,23 @@ export type UserState = {
|
||||
export const DEFAULT_MEMBER_NAME = '朋友';
|
||||
|
||||
const getInitialState = (): UserState => {
|
||||
const preloaded = getPreloadedUserData();
|
||||
// 使用模块加载时同步加载的数据
|
||||
console.log('初始化 Redux state,使用预加载数据:', preloadedUserData);
|
||||
|
||||
return {
|
||||
token: preloaded.token,
|
||||
token: preloadedUserData.token,
|
||||
profile: {
|
||||
name: DEFAULT_MEMBER_NAME,
|
||||
isVip: false,
|
||||
freeUsageCount: 3,
|
||||
memberNumber: 0,
|
||||
maxUsageCount: 5,
|
||||
...preloaded.profile, // 合并预加载的用户资料
|
||||
...preloadedUserData.profile, // 合并预加载的用户资料(包含 memberNumber)
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
weightHistory: [],
|
||||
activityHistory: [],
|
||||
onboardingCompleted: preloaded.onboardingCompleted, // 引导完成状态
|
||||
onboardingCompleted: preloadedUserData.onboardingCompleted, // 引导完成状态
|
||||
};
|
||||
};
|
||||
|
||||
@@ -198,8 +185,11 @@ export const login = createAsyncThunk(
|
||||
|
||||
if (!token) throw new Error('登录响应缺少 token');
|
||||
|
||||
// 先持久化到本地存储
|
||||
await AsyncStorage.setItem(STORAGE_KEYS.authToken, token);
|
||||
await AsyncStorage.setItem(STORAGE_KEYS.userProfile, JSON.stringify(profile ?? {}));
|
||||
|
||||
// 再设置到 API 客户端(内部会同步更新 AsyncStorage)
|
||||
await setAuthToken(token);
|
||||
|
||||
return { token, profile } as { token: string; profile: UserProfile };
|
||||
@@ -222,12 +212,15 @@ export const setOnboardingCompleted = createAsyncThunk('user/setOnboardingComple
|
||||
});
|
||||
|
||||
export const logout = createAsyncThunk('user/logout', async () => {
|
||||
// 先清除 API 客户端的 token(内部会清除 AsyncStorage)
|
||||
await setAuthToken(null);
|
||||
|
||||
// 再清除其他本地存储数据
|
||||
await Promise.all([
|
||||
AsyncStorage.removeItem(STORAGE_KEYS.authToken),
|
||||
AsyncStorage.removeItem(STORAGE_KEYS.userProfile),
|
||||
AsyncStorage.removeItem(STORAGE_KEYS.privacyAgreed),
|
||||
]);
|
||||
await setAuthToken(null);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user