feat: 优化 AI 教练聊天界面和个人信息页面

- 在 AI 教练聊天界面中添加消息自动滚动功能,提升用户体验
- 更新消息发送逻辑,确保新消息渲染后再滚动
- 在个人信息页面中集成用户资料拉取功能,支持每次聚焦时更新用户信息
- 修改登录页面,增加身份令牌验证,确保安全性
- 更新样式以适应新功能的展示和交互
This commit is contained in:
richarjiang
2025-08-13 16:51:51 +08:00
parent 321947db98
commit ebc74eb1c8
6 changed files with 207 additions and 31 deletions

View File

@@ -3,9 +3,10 @@ import { Colors } from '@/constants/Colors';
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
import { useColorScheme } from '@/hooks/useColorScheme';
import { removeExercise, setCurrentDate, toggleExerciseCompleted } from '@/store/checkinSlice';
import { Ionicons } from '@expo/vector-icons';
import { useRouter } from 'expo-router';
import React, { useEffect, useMemo } from 'react';
import { FlatList, SafeAreaView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { Alert, FlatList, SafeAreaView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
function formatDate(d: Date) {
const y = d.getFullYear();
@@ -63,10 +64,32 @@ export default function CheckinHome() {
<Text style={[styles.cardMeta, { color: colorTokens.textMuted }]}>{item.category}</Text>
<Text style={[styles.cardMeta, { color: colorTokens.textMuted }]}> {item.sets}{item.reps ? ` · 每组 ${item.reps}` : ''}{item.durationSec ? ` · 每组 ${item.durationSec}s` : ''}</Text>
</View>
<TouchableOpacity style={[styles.doneBtn, { backgroundColor: item.completed ? colorTokens.primary : colorTokens.border }]} onPress={() => dispatch(toggleExerciseCompleted({ date: today, key: item.key }))}>
<Text style={[styles.doneBtnText, { color: item.completed ? colorTokens.onPrimary : colorTokens.text, fontWeight: item.completed ? '800' : '700' }]}>{item.completed ? '已完成' : '完成'}</Text>
<TouchableOpacity
accessibilityRole="button"
accessibilityLabel={item.completed ? '已完成,点击取消完成' : '未完成,点击标记完成'}
style={styles.doneIconBtn}
onPress={() => dispatch(toggleExerciseCompleted({ date: today, key: item.key }))}
hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}
>
<Ionicons
name={item.completed ? 'checkmark-circle' : 'checkmark-circle-outline'}
size={24}
color={item.completed ? colorTokens.primary : colorTokens.textMuted}
/>
</TouchableOpacity>
<TouchableOpacity style={[styles.removeBtn, { backgroundColor: colorTokens.border }]} onPress={() => dispatch(removeExercise({ date: today, key: item.key }))}>
<TouchableOpacity
style={[styles.removeBtn, { backgroundColor: colorTokens.border }]}
onPress={() =>
Alert.alert('确认移除', '确定要移除该动作吗?', [
{ text: '取消', style: 'cancel' },
{
text: '移除',
style: 'destructive',
onPress: () => dispatch(removeExercise({ date: today, key: item.key })),
},
])
}
>
<Text style={[styles.removeBtnText, { color: colorTokens.text }]}></Text>
</TouchableOpacity>
</View>
@@ -93,17 +116,14 @@ const styles = StyleSheet.create({
actionRow: { paddingHorizontal: 20, marginTop: 8 },
primaryBtn: { backgroundColor: '#111827', paddingVertical: 10, borderRadius: 10, alignItems: 'center' },
primaryBtnText: { color: '#FFFFFF', fontWeight: '800' },
emptyBox: { marginTop: 16, backgroundColor: '#FFFFFF', borderRadius: 16, padding: 16, marginHorizontal: 20 },
emptyBox: { marginTop: 16, backgroundColor: '#FFFFFF', borderRadius: 16, padding: 16, marginHorizontal: 0 },
emptyText: { color: '#6B7280' },
card: { marginTop: 12, marginHorizontal: 20, backgroundColor: '#FFFFFF', borderRadius: 16, padding: 16, flexDirection: 'row', alignItems: 'center', gap: 12, shadowColor: '#000', shadowOpacity: 0.06, shadowRadius: 12, shadowOffset: { width: 0, height: 6 }, elevation: 3 },
card: { marginTop: 12, marginHorizontal: 0, backgroundColor: '#FFFFFF', borderRadius: 16, padding: 16, flexDirection: 'row', alignItems: 'center', gap: 12, shadowColor: '#000', shadowOpacity: 0.06, shadowRadius: 12, shadowOffset: { width: 0, height: 6 }, elevation: 3 },
cardTitle: { fontSize: 16, fontWeight: '800', color: '#111827' },
cardMeta: { marginTop: 4, fontSize: 12, color: '#6B7280' },
removeBtn: { backgroundColor: '#F3F4F6', paddingHorizontal: 10, paddingVertical: 6, borderRadius: 8 },
removeBtnText: { color: '#111827', fontWeight: '700' },
doneBtn: { backgroundColor: '#E5E7EB', paddingHorizontal: 10, paddingVertical: 6, borderRadius: 8, marginRight: 8 },
doneBtnActive: { backgroundColor: '#10B981' },
doneBtnText: { color: '#111827', fontWeight: '700' },
doneBtnTextActive: { color: '#FFFFFF', fontWeight: '800' },
doneIconBtn: { paddingHorizontal: 4, paddingVertical: 4, borderRadius: 16, marginRight: 8 },
});