feat(ai报告): 新增AI健康报告画廊功能,支持报告生成、保存与分享
This commit is contained in:
@@ -14,17 +14,19 @@ import { WorkoutSummaryCard } from '@/components/WorkoutSummaryCard';
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
||||
import { useAuthGuard } from '@/hooks/useAuthGuard';
|
||||
import { BackgroundTaskManager } from '@/services/backgroundTaskManagerV2';
|
||||
import { syncHealthKitToServer } from '@/services/healthKitSync';
|
||||
import { setHealthData } from '@/store/healthSlice';
|
||||
import { fetchDailyMoodCheckins, selectLatestMoodRecordByDate } from '@/store/moodSlice';
|
||||
import { updateUserProfile } from '@/store/userSlice';
|
||||
import { fetchTodayWaterStats } from '@/store/waterSlice';
|
||||
import { getMonthDaysZh, getTodayIndexInMonth } from '@/utils/date';
|
||||
import { fetchHealthDataForDate, testHRVDataFetch } from '@/utils/health';
|
||||
import { fetchHealthDataForDate } from '@/utils/health';
|
||||
import { logger } from '@/utils/logger';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import dayjs from 'dayjs';
|
||||
import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { debounce } from 'lodash';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -63,8 +65,8 @@ export default function ExploreScreen() {
|
||||
const stepGoal = useAppSelector((s) => s.user.profile?.dailyStepsGoal) ?? 2000;
|
||||
const userProfile = useAppSelector((s) => s.user.profile);
|
||||
|
||||
const { pushIfAuthedElseLogin, isLoggedIn } = useAuthGuard();
|
||||
|
||||
const { pushIfAuthedElseLogin, isLoggedIn, ensureLoggedIn } = useAuthGuard();
|
||||
const router = useRouter();
|
||||
|
||||
// 使用 dayjs:当月日期与默认选中"今天"
|
||||
const [selectedIndex, setSelectedIndex] = useState(getTodayIndexInMonth());
|
||||
@@ -80,7 +82,11 @@ export default function ExploreScreen() {
|
||||
return dayjs(currentSelectedDate).format('YYYY-MM-DD');
|
||||
}, [currentSelectedDate]);
|
||||
|
||||
|
||||
const handleOpenGallery = React.useCallback(async () => {
|
||||
const ok = await ensureLoggedIn();
|
||||
if (!ok) return;
|
||||
router.push('/gallery');
|
||||
}, [ensureLoggedIn, router]);
|
||||
|
||||
// 用于触发动画重置的 token(当日期或数据变化时更新)
|
||||
const [animToken, setAnimToken] = useState(0);
|
||||
@@ -384,42 +390,41 @@ export default function ExploreScreen() {
|
||||
{/* 顶部信息栏 */}
|
||||
<View style={styles.headerContainer}>
|
||||
<View style={styles.headerContent}>
|
||||
{/* 左边logo */}
|
||||
<Image
|
||||
source={require('@/assets/machine.png')}
|
||||
style={styles.logoImage}
|
||||
resizeMode="cover"
|
||||
/>
|
||||
<View style={styles.headerLeft}>
|
||||
<Image
|
||||
source={require('@/assets/machine.png')}
|
||||
style={styles.logoImage}
|
||||
resizeMode="cover"
|
||||
/>
|
||||
|
||||
{/* 右边文字区域 */}
|
||||
<View style={styles.headerTextContainer}>
|
||||
<Text style={styles.headerTitle}>{t('statistics.title')}</Text>
|
||||
{/* 右边文字区域 */}
|
||||
<View style={styles.headerTextContainer}>
|
||||
<Text style={styles.headerTitle}>{t('statistics.title')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 开发环境调试按钮 */}
|
||||
{__DEV__ && (
|
||||
<View style={styles.debugButtonsContainer}>
|
||||
<TouchableOpacity
|
||||
style={styles.debugButton}
|
||||
onPress={async () => {
|
||||
console.log('🔧 Manual background task test...');
|
||||
await BackgroundTaskManager.getInstance().triggerTaskForTesting();
|
||||
}}
|
||||
>
|
||||
<Text style={styles.debugButtonText}>🔧</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.headerActions}>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.85}
|
||||
onPress={handleOpenGallery}
|
||||
>
|
||||
{isLiquidGlassAvailable() ? (
|
||||
<GlassView
|
||||
style={styles.liquidGlassButton}
|
||||
glassEffectStyle="regular"
|
||||
tintColor="rgba(255, 255, 255, 0.3)"
|
||||
isInteractive={true}
|
||||
>
|
||||
<Ionicons name="sparkles-outline" size={18} color="#0F172A" />
|
||||
</GlassView>
|
||||
) : (
|
||||
<View style={[styles.liquidGlassButton, styles.liquidGlassFallback]}>
|
||||
<Ionicons name="sparkles-outline" size={18} color="#0F172A" />
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.debugButton, styles.hrvTestButton]}
|
||||
onPress={async () => {
|
||||
console.log('🫀 Testing HRV data fetch...');
|
||||
await testHRVDataFetch();
|
||||
}}
|
||||
>
|
||||
<Text style={styles.debugButtonText}>🫀</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -537,6 +542,7 @@ export default function ExploreScreen() {
|
||||
{/* 围度数据卡片 - 占满底部一行 */}
|
||||
<CircumferenceCard style={styles.circumferenceCard} />
|
||||
</ScrollView>
|
||||
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -585,6 +591,13 @@ const styles = StyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 12,
|
||||
},
|
||||
headerLeft: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
},
|
||||
logoImage: {
|
||||
width: 28,
|
||||
@@ -921,6 +934,53 @@ const styles = StyleSheet.create({
|
||||
textAlign: 'left',
|
||||
fontFamily: 'AliBold',
|
||||
},
|
||||
headerActions: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
},
|
||||
reportButton: {
|
||||
height: 36,
|
||||
borderRadius: 18,
|
||||
paddingHorizontal: 12,
|
||||
backgroundColor: '#F6F7FB',
|
||||
borderWidth: 1,
|
||||
borderColor: '#E5E7EB',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
},
|
||||
reportIconWrapper: {
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 14,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
reportButtonLabel: {
|
||||
fontSize: 14,
|
||||
fontFamily: 'AliBold',
|
||||
color: '#0F172A',
|
||||
},
|
||||
// Liquid Glass 风格按钮
|
||||
liquidGlassButton: {
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 20,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
liquidGlassFallback: {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.6)',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(255, 255, 255, 0.8)',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 8,
|
||||
elevation: 4,
|
||||
},
|
||||
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user