feat: 集成 Redux 状态管理和用户目标管理功能

- 添加 Redux 状态管理,支持用户登录和个人信息的持久化
- 新增目标管理页面,允许用户设置每日卡路里和步数目标
- 更新首页,移除旧的活动展示,改为固定的热点功能卡片
- 修改布局以适应新功能的展示和交互
- 更新依赖,添加 @reduxjs/toolkit 和 react-redux 库以支持状态管理
- 新增 API 服务模块,处理与后端的交互
This commit is contained in:
2025-08-12 22:22:30 +08:00
parent c3d4630801
commit 00ddec25c5
14 changed files with 913 additions and 99 deletions

View File

@@ -2,17 +2,21 @@ import { Ionicons } from '@expo/vector-icons';
import * as AppleAuthentication from 'expo-apple-authentication';
import { useRouter } from 'expo-router';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Alert, Pressable, SafeAreaView, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { Alert, Pressable, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
import { Colors } from '@/constants/Colors';
import { useAppDispatch } from '@/hooks/redux';
import { useColorScheme } from '@/hooks/useColorScheme';
import { login } from '@/store/userSlice';
export default function LoginScreen() {
const router = useRouter();
const scheme = (useColorScheme() ?? 'light') as 'light' | 'dark';
const color = Colors[scheme];
const dispatch = useAppDispatch();
const [hasAgreed, setHasAgreed] = useState<boolean>(false);
const [appleAvailable, setAppleAvailable] = useState<boolean>(false);
@@ -40,11 +44,13 @@ export default function LoginScreen() {
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
// TODO: 将 credential 发送到后端换取应用会话。这里先直接返回上一页。
const identityToken = (credential as any)?.identityToken;
await dispatch(login({ appleIdentityToken: identityToken })).unwrap();
router.back();
} catch (err: any) {
if (err?.code === 'ERR_CANCELED') return;
Alert.alert('登录失败', '请稍后再试');
const message = err?.message || '登录失败,请稍后再试';
Alert.alert('登录失败', message);
} finally {
setLoading(false);
}
@@ -58,7 +64,7 @@ export default function LoginScreen() {
const disabledStyle = useMemo(() => ({ opacity: hasAgreed ? 1 : 0.5 }), [hasAgreed]);
return (
<SafeAreaView style={[styles.safeArea, { backgroundColor: color.background }]}>
<SafeAreaView edges={['top']} style={[styles.safeArea, { backgroundColor: color.background }]}>
<ThemedView style={styles.container}>
{/* 自定义头部,与其它页面风格一致 */}
<View style={styles.header}>
@@ -71,7 +77,7 @@ export default function LoginScreen() {
<ScrollView contentContainerStyle={styles.content} showsVerticalScrollIndicator={false}>
<View style={styles.headerWrap}>
<ThemedText style={[styles.title, { color: color.text }]}>Digital Pilates</ThemedText>
<ThemedText style={[styles.title, { color: color.text }]}></ThemedText>
<ThemedText style={[styles.subtitle, { color: color.textMuted }]}></ThemedText>
</View>