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:
@@ -3,6 +3,7 @@ import { useFocusEffect } from '@react-navigation/native';
|
||||
import dayjs from 'dayjs';
|
||||
import isBetween from 'dayjs/plugin/isBetween';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { useLocalSearchParams } from 'expo-router';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
@@ -274,6 +275,7 @@ function groupWorkouts(workouts: WorkoutData[]): WorkoutSection[] {
|
||||
|
||||
export default function WorkoutHistoryScreen() {
|
||||
const { t } = useI18n();
|
||||
const { workoutId: workoutIdParam } = useLocalSearchParams<{ workoutId?: string | string[] }>();
|
||||
const [sections, setSections] = useState<WorkoutSection[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -285,9 +287,20 @@ export default function WorkoutHistoryScreen() {
|
||||
const [selectedIntensity, setSelectedIntensity] = useState<IntensityBadge | null>(null);
|
||||
const [monthOccurrenceText, setMonthOccurrenceText] = useState<string | null>(null);
|
||||
const [monthlyStats, setMonthlyStats] = useState<MonthlyStatsInfo | null>(null);
|
||||
const [pendingWorkoutId, setPendingWorkoutId] = useState<string | null>(null);
|
||||
|
||||
const safeAreaTop = useSafeAreaTop();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!workoutIdParam) {
|
||||
return;
|
||||
}
|
||||
const idParam = Array.isArray(workoutIdParam) ? workoutIdParam[0] : workoutIdParam;
|
||||
if (idParam) {
|
||||
setPendingWorkoutId(idParam);
|
||||
}
|
||||
}, [workoutIdParam]);
|
||||
|
||||
const loadHistory = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
@@ -484,6 +497,22 @@ export default function WorkoutHistoryScreen() {
|
||||
loadWorkoutDetail(workout);
|
||||
}, [computeMonthlyOccurrenceText, loadWorkoutDetail]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!pendingWorkoutId || isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
const allWorkouts = sections.flatMap((section) => section.data);
|
||||
const targetWorkout = allWorkouts.find((workout) => workout.id === pendingWorkoutId);
|
||||
|
||||
if (targetWorkout) {
|
||||
handleWorkoutPress(targetWorkout);
|
||||
}
|
||||
|
||||
// 清理待处理状态,避免重复触发
|
||||
setPendingWorkoutId(null);
|
||||
}, [pendingWorkoutId, isLoading, sections, handleWorkoutPress]);
|
||||
|
||||
const handleRetryDetail = useCallback(() => {
|
||||
if (selectedWorkout) {
|
||||
loadWorkoutDetail(selectedWorkout);
|
||||
|
||||
Reference in New Issue
Block a user