Files
digital-pilates/hooks/useAuthGuard.ts
richarjiang 39671ed70f feat(challenges): 添加自定义挑战功能和多语言支持
- 新增自定义挑战创建页面,支持设置挑战类型、时间范围、目标值等
- 实现挑战邀请码系统,支持通过邀请码加入自定义挑战
- 完善挑战详情页面的多语言翻译支持
- 优化用户认证状态检查逻辑,使用token作为主要判断依据
- 添加阿里字体文件支持,提升UI显示效果
- 改进确认弹窗组件,支持Liquid Glass效果和自定义内容
- 优化应用启动流程,直接读取onboarding状态而非预加载用户数据
2025-11-26 16:39:01 +08:00

157 lines
4.6 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 AsyncStorage from '@/utils/kvStore';
import { usePathname, useRouter } from 'expo-router';
import { useCallback } from 'react';
import { Alert } from 'react-native';
import { ROUTES } from '@/constants/Routes';
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
import { STORAGE_KEYS, api } from '@/services/api';
import { logout as logoutAction } from '@/store/userSlice';
type RedirectParams = Record<string, string | number | boolean | undefined>;
type EnsureOptions = {
redirectTo?: string;
redirectParams?: RedirectParams;
shouldBack?: boolean; // 登录成功后是否返回上一页而不是重定向
};
export function useAuthGuard() {
const router = useRouter();
const dispatch = useAppDispatch();
const currentPath = usePathname();
const user = useAppSelector(state => state.user);
// 判断登录状态:优先使用 token因为 token 是登录的根本凭证
// profile.id 可能在初始化时还未加载,但 token 已经从 AsyncStorage 恢复
const isLoggedIn = !!user?.token;
const ensureLoggedIn = useCallback(async (options?: EnsureOptions): Promise<boolean> => {
if (isLoggedIn) return true;
const redirectTo = options?.redirectTo ?? currentPath ?? ROUTES.TAB_STATISTICS;
const paramsJson = options?.redirectParams ? JSON.stringify(options.redirectParams) : undefined;
const shouldBack = options?.shouldBack;
router.push({
pathname: '/auth/login',
params: {
redirectTo,
...(paramsJson ? { redirectParams: paramsJson } : {}),
...(shouldBack ? { shouldBack: 'true' } : {}),
},
} as any);
return false;
}, [isLoggedIn, router, currentPath]);
const pushIfAuthedElseLogin = useCallback((pathname: string, params?: RedirectParams) => {
if (isLoggedIn) {
router.push({ pathname, params } as any);
return;
}
const paramsJson = params ? JSON.stringify(params) : undefined;
router.push({ pathname: '/auth/login', params: { redirectTo: pathname, ...(paramsJson ? { redirectParams: paramsJson } : {}) } } as any);
}, [isLoggedIn, router]);
const guardHandler = useCallback(
<T extends any[]>(fn: (...args: T) => any | Promise<any>, options?: EnsureOptions) => {
return async (...args: T) => {
const ok = await ensureLoggedIn(options);
if (!ok) return;
return fn(...args);
};
},
[ensureLoggedIn]
);
// 退出登录功能
const handleLogout = useCallback(async () => {
try {
// 调用 Redux action 清除本地状态和缓存
await dispatch(logoutAction()).unwrap();
// 跳转到登录页面
router.push('/auth/login');
} catch (error) {
console.error('退出登录失败:', error);
Alert.alert('错误', '退出登录失败,请稍后重试');
}
}, [dispatch, router]);
// 带确认对话框的退出登录
const confirmLogout = useCallback(() => {
Alert.alert(
'确认退出',
'确定要退出当前账号吗?',
[
{
text: '取消',
style: 'cancel',
},
{
text: '确定',
style: 'default',
onPress: handleLogout,
},
]
);
}, [handleLogout]);
// 注销账号功能
const handleDeleteAccount = useCallback(async () => {
try {
// 调用注销账号API
await api.delete('/api/users/delete-account');
// 清除额外的本地数据
await AsyncStorage.multiRemove(['@user_personal_info', STORAGE_KEYS.onboardingCompleted]);
// 执行退出登录逻辑
await dispatch(logoutAction()).unwrap();
Alert.alert('账号已注销', '您的账号已成功注销', [
{
text: '确定',
onPress: () => router.push('/auth/login'),
},
]);
} catch (error: any) {
console.error('注销账号失败:', error);
const message = error?.message || '注销失败,请稍后重试';
Alert.alert('注销失败', message);
}
}, [dispatch, router]);
// 带确认对话框的注销账号
const confirmDeleteAccount = useCallback(() => {
Alert.alert(
'确认注销账号',
'此操作不可恢复,将删除您的账号及相关数据。确定继续吗?',
[
{
text: '取消',
style: 'cancel',
},
{
text: '确认注销',
style: 'destructive',
onPress: handleDeleteAccount,
},
],
{ cancelable: true }
);
}, [handleDeleteAccount]);
return {
isLoggedIn,
ensureLoggedIn,
pushIfAuthedElseLogin,
guardHandler,
handleLogout,
confirmLogout,
handleDeleteAccount,
confirmDeleteAccount,
} as const;
}