feat(app): add version check system and enhance internationalization support

Add comprehensive app update checking functionality with:
- New VersionCheckContext for managing update detection and notifications
- VersionUpdateModal UI component for presenting update information
- Version service API integration with platform-specific update URLs
- Version check menu item in personal settings with manual/automatic checking

Enhance internationalization across workout features:
- Complete workout type translations for English and Chinese
- Localized workout detail modal with proper date/time formatting
- Locale-aware date formatting in fitness rings detail
- Workout notification improvements with deep linking to specific workout details

Improve UI/UX with better chart rendering, sizing fixes, and enhanced navigation flow. Update app version to 1.1.3 and include app version in API headers for better tracking.
This commit is contained in:
2025-11-29 20:47:16 +08:00
parent 83b77615cf
commit a309123b35
19 changed files with 1132 additions and 159 deletions

View File

@@ -1,5 +1,6 @@
import { fetchRecentWorkouts, WorkoutData } from '@/utils/health';
import AsyncStorage from '@/utils/kvStore';
import { logger } from '@/utils/logger';
import { NativeEventEmitter, NativeModules } from 'react-native';
import { analyzeWorkoutAndSendNotification } from './workoutNotificationService';
@@ -94,7 +95,7 @@ class WorkoutMonitorService {
}
private async handleWorkoutUpdate(event: any): Promise<void> {
console.log('收到锻炼更新事件:', event);
logger.info('收到锻炼更新事件:', event);
// 防抖处理,避免短时间内重复处理
if (this.processingTimeout) {
@@ -105,14 +106,14 @@ class WorkoutMonitorService {
try {
await this.checkForNewWorkouts();
} catch (error) {
console.error('检查新锻炼失败:', error);
logger.error('检查新锻炼失败:', error);
}
}, 5000); // 5秒延迟确保 HealthKit 数据已完全更新
}
private async checkForNewWorkouts(): Promise<void> {
try {
console.log('检查新的锻炼记录...');
logger.info('检查新的锻炼记录...');
const lookbackWindowMs = this.lastProcessedWorkoutId
? DEFAULT_LOOKBACK_WINDOW_MS
@@ -120,7 +121,7 @@ class WorkoutMonitorService {
const startDate = new Date(Date.now() - lookbackWindowMs);
const endDate = new Date();
console.log(
logger.info(
`锻炼查询窗口: ${Math.round(lookbackWindowMs / (1000 * 60 * 60))} 小时 (${startDate.toISOString()} - ${endDate.toISOString()})`
);
@@ -130,7 +131,7 @@ class WorkoutMonitorService {
limit: 10
});
console.log(`找到 ${recentWorkouts.length} 条最近的锻炼记录`);
logger.info(`找到 ${recentWorkouts.length} 条最近的锻炼记录`);
if (this.lastProcessedWorkoutId && !recentWorkouts.some(workout => workout.id === this.lastProcessedWorkoutId)) {
console.warn('上次处理的锻炼记录不在当前查询窗口内,可能存在漏报风险');
@@ -145,15 +146,15 @@ class WorkoutMonitorService {
}
if (newWorkouts.length === 0) {
console.log('没有检测到新的锻炼记录');
logger.info('没有检测到新的锻炼记录');
return;
}
console.log(`检测到 ${newWorkouts.length} 条新的锻炼记录,将按时间顺序处理`);
logger.info(`检测到 ${newWorkouts.length} 条新的锻炼记录,将按时间顺序处理`);
// 先处理最旧的锻炼,确保通知顺序正确
for (const workout of newWorkouts.reverse()) {
console.log('处理新锻炼:', {
logger.info('处理新锻炼:', {
id: workout.id,
type: workout.workoutActivityTypeString,
duration: workout.duration,
@@ -164,22 +165,22 @@ class WorkoutMonitorService {
}
await this.saveLastProcessedWorkoutId(newWorkouts[0].id);
console.log('锻炼处理完成最新处理的锻炼ID:', newWorkouts[0].id);
logger.info('锻炼处理完成最新处理的锻炼ID:', newWorkouts[0].id);
} catch (error) {
console.error('检查新锻炼失败:', error);
logger.error('检查新锻炼失败:', error);
}
}
private async processNewWorkout(workout: WorkoutData): Promise<void> {
try {
console.log('开始处理新锻炼:', workout.id);
logger.info('开始处理新锻炼:', workout.id);
// 分析锻炼并发送通知
await analyzeWorkoutAndSendNotification(workout);
console.log('新锻炼处理完成:', workout.id);
logger.info('新锻炼处理完成:', workout.id);
} catch (error) {
console.error('处理新锻炼失败:', error);
logger.error('处理新锻炼失败:', error);
}
}