feat: 更新隐私同意弹窗和应用名称
- 将应用名称修改为“每日普拉提”,提升品牌识别度 - 新增隐私同意弹窗,确保用户在使用应用前同意隐私政策 - 更新 Redux 状态管理,添加隐私同意状态的处理 - 优化用户信息页面,确保体重和身高的格式化显示 - 更新今日训练页面标题为“快速训练”,提升用户体验 - 添加开发工具函数,便于测试隐私同意功能
This commit is contained in:
2
app.json
2
app.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"expo": {
|
||||
"name": "digital-pilates",
|
||||
"name": "普拉提助手",
|
||||
"slug": "digital-pilates",
|
||||
"version": "1.0.3",
|
||||
"orientation": "portrait",
|
||||
|
||||
@@ -29,6 +29,7 @@ export default function ExploreScreen() {
|
||||
const colorTokens = Colors[theme];
|
||||
const stepGoal = useAppSelector((s) => s.user.profile?.dailyStepsGoal) ?? 2000;
|
||||
const userProfile = useAppSelector((s) => s.user.profile);
|
||||
|
||||
// 使用 dayjs:当月日期与默认选中“今天”
|
||||
const days = getMonthDaysZh();
|
||||
const [selectedIndex, setSelectedIndex] = useState(getTodayIndexInMonth());
|
||||
@@ -163,16 +164,6 @@ export default function ExploreScreen() {
|
||||
})}
|
||||
</ScrollView>
|
||||
|
||||
{/* 打卡入口 */}
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: 24, marginBottom: 8 }}>
|
||||
<Text style={styles.sectionTitle}>每日报告</Text>
|
||||
<TouchableOpacity onPress={() => router.push('/checkin/calendar')} accessibilityRole="button">
|
||||
<Text style={{ color: '#6B7280', fontWeight: '700' }}>查看打卡日历</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* 取消卡片内 loading,保持静默刷新提升体验 */}
|
||||
|
||||
{/* 指标行:左大卡(训练时间),右两小卡(消耗卡路里、步数) */}
|
||||
<View style={styles.metricsRow}>
|
||||
<View style={[styles.trainingCard, styles.metricsLeft]}>
|
||||
@@ -230,8 +221,8 @@ export default function ExploreScreen() {
|
||||
|
||||
{/* BMI 指数卡片 */}
|
||||
<BMICard
|
||||
weight={userProfile?.weight}
|
||||
height={userProfile?.height}
|
||||
weight={userProfile?.weight ? parseFloat(userProfile.weight) : undefined}
|
||||
height={userProfile?.height ? parseFloat(userProfile.height) : undefined}
|
||||
/>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
|
||||
@@ -301,7 +301,10 @@ export default function HomeScreen() {
|
||||
>
|
||||
<View style={styles.featureIconWrapper}>
|
||||
<View style={styles.featureIconPlaceholder}>
|
||||
<ThemedText style={styles.featureIconText}>💪</ThemedText>
|
||||
<Image
|
||||
source={require('@/assets/images/icons/iconPlan.png')}
|
||||
style={styles.featureIconImage}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<ThemedText style={styles.featureTitle}>计划管理</ThemedText>
|
||||
|
||||
@@ -45,12 +45,12 @@ export default function PersonalScreen() {
|
||||
// 数据格式化函数
|
||||
const formatHeight = () => {
|
||||
if (userProfile.height == null) return '--';
|
||||
return `${Math.round(userProfile.height)}cm`;
|
||||
return `${parseFloat(userProfile.height).toFixed(1)}cm`;
|
||||
};
|
||||
|
||||
const formatWeight = () => {
|
||||
if (userProfile.weight == null) return '--';
|
||||
return `${Math.round(userProfile.weight * 10) / 10}kg`;
|
||||
return `${parseFloat(userProfile.weight).toFixed(1)}kg`;
|
||||
};
|
||||
|
||||
const formatAge = () => {
|
||||
|
||||
@@ -4,22 +4,60 @@ import { Stack } from 'expo-router';
|
||||
import { StatusBar } from 'expo-status-bar';
|
||||
import 'react-native-reanimated';
|
||||
|
||||
import { useAppDispatch } from '@/hooks/redux';
|
||||
import PrivacyConsentModal from '@/components/PrivacyConsentModal';
|
||||
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||
import { clearAiCoachSessionCache } from '@/services/aiCoachSession';
|
||||
import { store } from '@/store';
|
||||
import { rehydrateUser } from '@/store/userSlice';
|
||||
import { rehydrateUser, setPrivacyAgreed } from '@/store/userSlice';
|
||||
import React from 'react';
|
||||
import RNExitApp from 'react-native-exit-app';
|
||||
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
function Bootstrapper({ children }: { children: React.ReactNode }) {
|
||||
const dispatch = useAppDispatch();
|
||||
const { privacyAgreed } = useAppSelector((state) => state.user);
|
||||
const [showPrivacyModal, setShowPrivacyModal] = React.useState(false);
|
||||
const [userDataLoaded, setUserDataLoaded] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
dispatch(rehydrateUser());
|
||||
const loadUserData = async () => {
|
||||
await dispatch(rehydrateUser());
|
||||
setUserDataLoaded(true);
|
||||
};
|
||||
|
||||
loadUserData();
|
||||
// 冷启动时清空 AI 教练会话缓存
|
||||
clearAiCoachSessionCache();
|
||||
}, [dispatch]);
|
||||
return <>{children}</>;
|
||||
|
||||
React.useEffect(() => {
|
||||
// 当用户数据加载完成后,检查是否需要显示隐私同意弹窗
|
||||
if (userDataLoaded && !privacyAgreed) {
|
||||
setShowPrivacyModal(true);
|
||||
}
|
||||
}, [userDataLoaded, privacyAgreed]);
|
||||
|
||||
const handlePrivacyAgree = () => {
|
||||
dispatch(setPrivacyAgreed());
|
||||
setShowPrivacyModal(false);
|
||||
};
|
||||
|
||||
const handlePrivacyDisagree = () => {
|
||||
RNExitApp.exitApp();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<PrivacyConsentModal
|
||||
visible={showPrivacyModal}
|
||||
onAgree={handlePrivacyAgree}
|
||||
onDisagree={handlePrivacyDisagree}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function RootLayout() {
|
||||
|
||||
@@ -427,7 +427,7 @@ export default function TodayWorkoutScreen() {
|
||||
|
||||
<SafeAreaView style={styles.contentWrapper}>
|
||||
<HeaderBar
|
||||
title="今日训练"
|
||||
title="快速训练"
|
||||
onBack={() => router.back()}
|
||||
withSafeTop={false}
|
||||
transparent={true}
|
||||
|
||||
BIN
assets/images/icons/iconPlan.png
Normal file
BIN
assets/images/icons/iconPlan.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
191
components/PrivacyConsentModal.tsx
Normal file
191
components/PrivacyConsentModal.tsx
Normal file
@@ -0,0 +1,191 @@
|
||||
import { router } from 'expo-router';
|
||||
import React from 'react';
|
||||
import {
|
||||
Dimensions,
|
||||
Modal,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
const { width } = Dimensions.get('window');
|
||||
|
||||
interface PrivacyConsentModalProps {
|
||||
visible: boolean;
|
||||
onAgree: () => void;
|
||||
onDisagree: () => void;
|
||||
}
|
||||
|
||||
export default function PrivacyConsentModal({
|
||||
visible,
|
||||
onAgree,
|
||||
onDisagree,
|
||||
}: PrivacyConsentModalProps) {
|
||||
const handleUserAgreementPress = () => {
|
||||
router.push('/legal/user-agreement');
|
||||
};
|
||||
|
||||
const handlePrivacyPolicyPress = () => {
|
||||
router.push('/legal/privacy-policy');
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
transparent
|
||||
animationType="fade"
|
||||
statusBarTranslucent
|
||||
>
|
||||
<View style={styles.overlay}>
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>欢迎来到普拉提助手</Text>
|
||||
|
||||
<View style={styles.contentContainer}>
|
||||
<Text style={styles.description}>
|
||||
点击"同意并继续"代表您已阅读并理解
|
||||
</Text>
|
||||
<View style={styles.linksContainer}>
|
||||
<TouchableOpacity onPress={handleUserAgreementPress}>
|
||||
<Text style={styles.link}>《用户协议》</Text>
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.and}> 和 </Text>
|
||||
<TouchableOpacity onPress={handlePrivacyPolicyPress}>
|
||||
<Text style={styles.link}>《隐私政策》</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<Text style={styles.description}>
|
||||
我们深知保护用户隐私信息的重要性,请认真进行阅读。
|
||||
</Text>
|
||||
<View style={styles.viewFullContainer}>
|
||||
<Text style={styles.viewFull}>查看完整版 </Text>
|
||||
<TouchableOpacity onPress={handleUserAgreementPress}>
|
||||
<Text style={styles.link}>《用户协议》</Text>
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.viewFull}> 和 </Text>
|
||||
<TouchableOpacity onPress={handlePrivacyPolicyPress}>
|
||||
<Text style={styles.link}>《隐私政策》</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity style={styles.agreeButton} onPress={onAgree}>
|
||||
<Text style={styles.agreeButtonText}>同意并继续</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity style={styles.disagreeButton} onPress={onDisagree}>
|
||||
<Text style={styles.disagreeButtonText}>不同意并退出</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 20,
|
||||
},
|
||||
container: {
|
||||
backgroundColor: 'white',
|
||||
borderRadius: 20,
|
||||
padding: 24,
|
||||
width: width * 0.85,
|
||||
maxWidth: 400,
|
||||
alignItems: 'center',
|
||||
},
|
||||
iconContainer: {
|
||||
marginBottom: 20,
|
||||
alignItems: 'center',
|
||||
},
|
||||
characterContainer: {
|
||||
position: 'relative',
|
||||
alignItems: 'center',
|
||||
},
|
||||
iconText: {
|
||||
fontSize: 48,
|
||||
marginBottom: 8,
|
||||
},
|
||||
balloons: {
|
||||
position: 'absolute',
|
||||
top: -5,
|
||||
right: -25,
|
||||
flexDirection: 'row',
|
||||
gap: 4,
|
||||
},
|
||||
balloon: {
|
||||
width: 12,
|
||||
height: 16,
|
||||
borderRadius: 6,
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
fontWeight: '600',
|
||||
color: '#1F2937',
|
||||
marginBottom: 20,
|
||||
textAlign: 'center',
|
||||
},
|
||||
contentContainer: {
|
||||
marginBottom: 24,
|
||||
},
|
||||
description: {
|
||||
fontSize: 14,
|
||||
color: '#6B7280',
|
||||
lineHeight: 20,
|
||||
textAlign: 'center',
|
||||
},
|
||||
linksContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
link: {
|
||||
fontSize: 14,
|
||||
color: '#8B5FE6',
|
||||
textDecorationLine: 'underline',
|
||||
},
|
||||
and: {
|
||||
fontSize: 14,
|
||||
color: '#6B7280',
|
||||
},
|
||||
viewFullContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap',
|
||||
marginTop: 4,
|
||||
},
|
||||
viewFull: {
|
||||
fontSize: 14,
|
||||
color: '#6B7280',
|
||||
},
|
||||
agreeButton: {
|
||||
backgroundColor: '#8B5FE6',
|
||||
borderRadius: 25,
|
||||
paddingVertical: 14,
|
||||
paddingHorizontal: 40,
|
||||
width: '100%',
|
||||
marginBottom: 12,
|
||||
},
|
||||
agreeButtonText: {
|
||||
color: 'white',
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
textAlign: 'center',
|
||||
},
|
||||
disagreeButton: {
|
||||
paddingVertical: 14,
|
||||
paddingHorizontal: 40,
|
||||
},
|
||||
disagreeButtonText: {
|
||||
color: '#9CA3AF',
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
@@ -1730,6 +1730,8 @@ PODS:
|
||||
- Yoga
|
||||
- RNDateTimePicker (8.4.4):
|
||||
- React-Core
|
||||
- RNExitApp (2.0.0):
|
||||
- React-Core
|
||||
- RNGestureHandler (2.24.0):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
@@ -2010,6 +2012,7 @@ DEPENDENCIES:
|
||||
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
|
||||
- "RNCMaskedView (from `../node_modules/@react-native-masked-view/masked-view`)"
|
||||
- "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)"
|
||||
- RNExitApp (from `../node_modules/react-native-exit-app`)
|
||||
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
||||
- RNReanimated (from `../node_modules/react-native-reanimated`)
|
||||
- RNScreens (from `../node_modules/react-native-screens`)
|
||||
@@ -2221,6 +2224,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/@react-native-masked-view/masked-view"
|
||||
RNDateTimePicker:
|
||||
:path: "../node_modules/@react-native-community/datetimepicker"
|
||||
RNExitApp:
|
||||
:path: "../node_modules/react-native-exit-app"
|
||||
RNGestureHandler:
|
||||
:path: "../node_modules/react-native-gesture-handler"
|
||||
RNReanimated:
|
||||
@@ -2334,6 +2339,7 @@ SPEC CHECKSUMS:
|
||||
RNCAsyncStorage: b44e8a4e798c3e1f56bffccd0f591f674fb9198f
|
||||
RNCMaskedView: d4644e239e65383f96d2f32c40c297f09705ac96
|
||||
RNDateTimePicker: 7d93eacf4bdf56350e4b7efd5cfc47639185e10c
|
||||
RNExitApp: 4432b9b7cc5ccec9f91c94e507849891282befd4
|
||||
RNGestureHandler: 6e640921d207f070e4bbcf79f4e6d0eabf323389
|
||||
RNReanimated: 34e90d19560aebd52a2ad583fdc2de2cf7651bbb
|
||||
RNScreens: 241cfe8fc82737f3e132dd45779f9512928075b8
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>digital-pilates</string>
|
||||
<string>普拉提助手</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
||||
7
package-lock.json
generated
7
package-lock.json
generated
@@ -38,6 +38,7 @@
|
||||
"react-dom": "19.0.0",
|
||||
"react-native": "0.79.5",
|
||||
"react-native-cos-sdk": "^1.2.1",
|
||||
"react-native-exit-app": "^2.0.0",
|
||||
"react-native-gesture-handler": "~2.24.0",
|
||||
"react-native-health": "^1.19.0",
|
||||
"react-native-image-viewing": "^0.2.2",
|
||||
@@ -10881,6 +10882,12 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-exit-app": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-exit-app/-/react-native-exit-app-2.0.0.tgz",
|
||||
"integrity": "sha512-vr9e/8jgPcKCBw6qo0QLxfeMiTwExydghbYDqpLZYAGWR+6cbgnhvOxwdYj/JWR7ZtOALrRA4GMGSvU/ayxM7w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-native-fit-image": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://mirrors.tencent.com/npm/react-native-fit-image/-/react-native-fit-image-1.5.5.tgz",
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"react-dom": "19.0.0",
|
||||
"react-native": "0.79.5",
|
||||
"react-native-cos-sdk": "^1.2.1",
|
||||
"react-native-exit-app": "^2.0.0",
|
||||
"react-native-gesture-handler": "~2.24.0",
|
||||
"react-native-health": "^1.19.0",
|
||||
"react-native-image-viewing": "^0.2.2",
|
||||
|
||||
@@ -74,6 +74,7 @@ export const api = {
|
||||
export const STORAGE_KEYS = {
|
||||
authToken: '@auth_token',
|
||||
userProfile: '@user_profile',
|
||||
privacyAgreed: '@privacy_agreed',
|
||||
} as const;
|
||||
|
||||
export async function loadPersistedToken(): Promise<string | null> {
|
||||
|
||||
@@ -9,8 +9,8 @@ export type UserProfile = {
|
||||
email?: string;
|
||||
gender?: Gender;
|
||||
birthDate?: string;
|
||||
weight?: number;
|
||||
height?: number;
|
||||
weight?: string;
|
||||
height?: string;
|
||||
avatar?: string | null;
|
||||
dailyStepsGoal?: number; // 每日步数目标(用于 Explore 页等)
|
||||
dailyCaloriesGoal?: number; // 每日卡路里消耗目标
|
||||
@@ -22,6 +22,7 @@ export type UserState = {
|
||||
profile: UserProfile;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
privacyAgreed: boolean;
|
||||
};
|
||||
|
||||
export const DEFAULT_MEMBER_NAME = '普拉提星球学员';
|
||||
@@ -33,6 +34,7 @@ const initialState: UserState = {
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
privacyAgreed: false,
|
||||
};
|
||||
|
||||
export type LoginPayload = Record<string, any> & {
|
||||
@@ -105,22 +107,30 @@ export const login = createAsyncThunk(
|
||||
);
|
||||
|
||||
export const rehydrateUser = createAsyncThunk('user/rehydrate', async () => {
|
||||
const [token, profileStr] = await Promise.all([
|
||||
const [token, profileStr, privacyAgreedStr] = await Promise.all([
|
||||
loadPersistedToken(),
|
||||
AsyncStorage.getItem(STORAGE_KEYS.userProfile),
|
||||
AsyncStorage.getItem(STORAGE_KEYS.privacyAgreed),
|
||||
]);
|
||||
await setAuthToken(token);
|
||||
let profile: UserProfile = {};
|
||||
if (profileStr) {
|
||||
try { profile = JSON.parse(profileStr) as UserProfile; } catch { profile = {}; }
|
||||
}
|
||||
return { token, profile } as { token: string | null; profile: UserProfile };
|
||||
const privacyAgreed = privacyAgreedStr === 'true';
|
||||
return { token, profile, privacyAgreed } as { token: string | null; profile: UserProfile; privacyAgreed: boolean };
|
||||
});
|
||||
|
||||
export const setPrivacyAgreed = createAsyncThunk('user/setPrivacyAgreed', async () => {
|
||||
await AsyncStorage.setItem(STORAGE_KEYS.privacyAgreed, 'true');
|
||||
return true;
|
||||
});
|
||||
|
||||
export const logout = createAsyncThunk('user/logout', async () => {
|
||||
await Promise.all([
|
||||
AsyncStorage.removeItem(STORAGE_KEYS.authToken),
|
||||
AsyncStorage.removeItem(STORAGE_KEYS.userProfile),
|
||||
AsyncStorage.removeItem(STORAGE_KEYS.privacyAgreed),
|
||||
]);
|
||||
await setAuthToken(null);
|
||||
return true;
|
||||
@@ -176,6 +186,7 @@ const userSlice = createSlice({
|
||||
.addCase(rehydrateUser.fulfilled, (state, action) => {
|
||||
state.token = action.payload.token;
|
||||
state.profile = action.payload.profile;
|
||||
state.privacyAgreed = action.payload.privacyAgreed;
|
||||
if (!state.profile?.name || !state.profile.name.trim()) {
|
||||
state.profile.name = DEFAULT_MEMBER_NAME;
|
||||
}
|
||||
@@ -193,6 +204,10 @@ const userSlice = createSlice({
|
||||
.addCase(logout.fulfilled, (state) => {
|
||||
state.token = null;
|
||||
state.profile = {};
|
||||
state.privacyAgreed = false;
|
||||
})
|
||||
.addCase(setPrivacyAgreed.fulfilled, (state) => {
|
||||
state.privacyAgreed = true;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
47
utils/devTools.ts
Normal file
47
utils/devTools.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { STORAGE_KEYS } from '@/services/api';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
/**
|
||||
* 开发工具函数 - 清除隐私同意状态
|
||||
* 用于测试隐私同意弹窗功能
|
||||
*/
|
||||
export const clearPrivacyAgreement = async (): Promise<void> => {
|
||||
try {
|
||||
await AsyncStorage.removeItem(STORAGE_KEYS.privacyAgreed);
|
||||
console.log('隐私同意状态已清除');
|
||||
} catch (error) {
|
||||
console.error('清除隐私同意状态失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 开发工具函数 - 检查隐私同意状态
|
||||
*/
|
||||
export const checkPrivacyAgreement = async (): Promise<boolean> => {
|
||||
try {
|
||||
const privacyAgreed = await AsyncStorage.getItem(STORAGE_KEYS.privacyAgreed);
|
||||
const isAgreed = privacyAgreed === 'true';
|
||||
console.log('隐私同意状态:', isAgreed);
|
||||
return isAgreed;
|
||||
} catch (error) {
|
||||
console.error('检查隐私同意状态失败:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 开发工具函数 - 清除所有用户数据
|
||||
* 用于完全重置应用状态
|
||||
*/
|
||||
export const clearAllUserData = async (): Promise<void> => {
|
||||
try {
|
||||
await Promise.all([
|
||||
AsyncStorage.removeItem(STORAGE_KEYS.authToken),
|
||||
AsyncStorage.removeItem(STORAGE_KEYS.userProfile),
|
||||
AsyncStorage.removeItem(STORAGE_KEYS.privacyAgreed),
|
||||
]);
|
||||
console.log('所有用户数据已清除');
|
||||
} catch (error) {
|
||||
console.error('清除用户数据失败:', error);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user