From d39a32c0d89f19abff6bdde8c2d5711f0c08981e Mon Sep 17 00:00:00 2001 From: richarjiang Date: Wed, 15 Oct 2025 19:06:18 +0800 Subject: [PATCH] feat(fasting): add auto-renewal and reset functionality for fasting plans - Implement auto-renewal logic for completed fasting cycles using dayjs - Add reset button with information modal in FastingOverviewCard - Configure iOS push notifications for production environment - Add expo-media-library and react-native-view-shot dependencies - Update FastingScheduleOrigin type to include 'auto' origin --- app/(tabs)/fasting.tsx | 62 ++++- components/fasting/FastingOverviewCard.tsx | 267 ++++++++++++++------ ios/OutLive.xcodeproj/project.pbxproj | 2 + ios/OutLive/Info.plist | 1 + ios/OutLive/OutLive.entitlements | 2 +- ios/Podfile.lock | 67 +++-- package-lock.json | 280 +++++++++++++++++---- package.json | 20 +- store/fastingSlice.ts | 2 +- 9 files changed, 548 insertions(+), 155 deletions(-) diff --git a/app/(tabs)/fasting.tsx b/app/(tabs)/fasting.tsx index c511f7a..ad30790 100644 --- a/app/(tabs)/fasting.tsx +++ b/app/(tabs)/fasting.tsx @@ -24,6 +24,7 @@ import { } from '@/utils/fasting'; import { useFocusEffect } from '@react-navigation/native'; import { useRouter } from 'expo-router'; +import dayjs from 'dayjs'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { ScrollView, StyleSheet, Text, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -154,6 +155,53 @@ export default function FastingTabScreen() { } }, [notificationsReady, notificationsLoading, notificationError, notificationIds, lastSyncTime, activeSchedule?.startISO, currentPlan?.id]); + useEffect(() => { + if (!activeSchedule || !currentPlan) return; + if (phase !== 'completed') return; + + const start = dayjs(activeSchedule.startISO); + const end = dayjs(activeSchedule.endISO); + if (!start.isValid() || !end.isValid()) return; + + const now = dayjs(); + if (now.isBefore(end)) return; + + const fastingHours = currentPlan.fastingHours; + const eatingHours = currentPlan.eatingHours; + const cycleHours = fastingHours + eatingHours; + + if (fastingHours <= 0 || cycleHours <= 0) return; + + let nextStart = start; + let nextEnd = end; + let iterations = 0; + const maxIterations = 60; + + while (!now.isBefore(nextEnd)) { + nextStart = nextStart.add(cycleHours, 'hour'); + nextEnd = nextStart.add(fastingHours, 'hour'); + iterations += 1; + + if (iterations >= maxIterations) { + if (__DEV__) { + console.warn('自动续订断食周期失败: 超出最大迭代次数', { + start: activeSchedule.startISO, + end: activeSchedule.endISO, + planId: currentPlan.id, + }); + } + return; + } + } + + if (iterations === 0) return; + + dispatch(rescheduleActivePlan({ + start: nextStart.toDate().toISOString(), + origin: 'auto', + })); + }, [dispatch, activeSchedule, currentPlan, phase]); + const handleAdjustStart = () => { if (!currentPlan) return; setShowPicker(true); @@ -213,6 +261,7 @@ export default function FastingTabScreen() { endTimeLabel={displayWindow.endTimeLabel} onAdjustStartPress={handleAdjustStart} onViewMealsPress={handleViewMeal} + onResetPress={handleResetPlan} progress={progress} /> )} @@ -233,9 +282,6 @@ export default function FastingTabScreen() { 如果计划与作息不符,可重新选择方案或调整开始时间。 - - 重置 - )} @@ -325,20 +371,10 @@ const styles = StyleSheet.create({ lineHeight: 20, }, resetRow: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', marginTop: 16, }, resetHint: { - flex: 1, fontSize: 12, color: '#8A96A3', - marginRight: 12, - }, - resetAction: { - fontSize: 12, - fontWeight: '600', - color: '#6366F1', }, }); diff --git a/components/fasting/FastingOverviewCard.tsx b/components/fasting/FastingOverviewCard.tsx index faf5f8e..6aa087a 100644 --- a/components/fasting/FastingOverviewCard.tsx +++ b/components/fasting/FastingOverviewCard.tsx @@ -2,9 +2,13 @@ import { CircularRing } from '@/components/CircularRing'; import { Colors } from '@/constants/Colors'; import type { FastingPlan } from '@/constants/Fasting'; import { useColorScheme } from '@/hooks/useColorScheme'; +import { Ionicons } from '@expo/vector-icons'; +import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; import { LinearGradient } from 'expo-linear-gradient'; -import React from 'react'; +import React, { useState } from 'react'; import { + Modal, + Pressable, StyleSheet, Text, TouchableOpacity, @@ -22,6 +26,7 @@ type FastingOverviewCardProps = { endTimeLabel: string; onAdjustStartPress: () => void; onViewMealsPress: () => void; + onResetPress: () => void; progress: number; }; @@ -36,93 +41,149 @@ export function FastingOverviewCard({ endTimeLabel, onAdjustStartPress, onViewMealsPress, + onResetPress, progress, }: FastingOverviewCardProps) { const theme = useColorScheme() ?? 'light'; const colors = Colors[theme]; const themeColors = plan?.theme; + const [showResetInfo, setShowResetInfo] = useState(false); + const isGlassAvailable = isLiquidGlassAvailable(); return ( - - - - 轻断食计划 - {plan?.id && ( - - - {plan.id} + <> + + + + 轻断食计划 + {plan?.id && ( + + + {plan.id} + + + )} + + {plan?.badge && ( + + + {plan.badge} )} - {plan?.badge && ( - - - {plan.badge} + + + + 断食开始时间 + {startDayLabel} + {startTimeLabel} + + + + 断食结束时间 + {endDayLabel} + {endTimeLabel} + + + + + + + + {phaseLabel} + {countdownLabel} + {countdownValue} + + + + + + + + 提前开始断食 - - )} - + - - - 断食开始时间 - {startDayLabel} - {startTimeLabel} - - - - 断食结束时间 - {endDayLabel} - {endTimeLabel} - - + + + 重置 + + - - - - - {phaseLabel} - {countdownLabel} - {countdownValue} - + {isGlassAvailable ? ( + + setShowResetInfo(true)} style={styles.infoButtonInner}> + + + + ) : ( + setShowResetInfo(true)} + style={[styles.infoButton, styles.fallbackInfoButton]} + > + + + )} - + - - setShowResetInfo(false)} + > + setShowResetInfo(false)} > - - 提前开始断食 - - - {/* - 查看食谱 - */} - - + { }}> + + 重置断食计划 + + 重置将清除当前活跃的断食计划和时间安排。您可以重新选择适合的计划或调整开始时间。 + + + 如果计划与您的作息不符,建议先尝试调整开始时间,而不是完全重置。 + + setShowResetInfo(false)} + > + 我知道了 + + + + + ); } @@ -258,6 +319,70 @@ const styles = StyleSheet.create({ fontSize: 15, fontWeight: '600', }, + infoButton: { + width: 36, + height: 36, + borderRadius: 18, + alignItems: 'center', + justifyContent: 'center', + marginLeft: 8, + }, + infoButtonInner: { + width: '100%', + height: '100%', + alignItems: 'center', + justifyContent: 'center', + }, + fallbackInfoButton: { + backgroundColor: 'rgba(255,255,255,0.3)', + }, + infoModalOverlay: { + flex: 1, + backgroundColor: 'rgba(0,0,0,0.5)', + justifyContent: 'flex-end', + }, + infoModalContent: { + backgroundColor: 'white', + borderTopLeftRadius: 24, + borderTopRightRadius: 24, + paddingVertical: 24, + paddingHorizontal: 20, + paddingBottom: 40, + }, + infoModalHandle: { + width: 36, + height: 4, + backgroundColor: '#E5E7EB', + borderRadius: 2, + alignSelf: 'center', + marginBottom: 16, + }, + infoModalTitle: { + fontSize: 18, + fontWeight: '700', + color: '#2E3142', + marginBottom: 16, + textAlign: 'center', + }, + infoModalText: { + fontSize: 15, + color: '#4A5460', + lineHeight: 22, + marginBottom: 12, + }, + infoModalButton: { + paddingVertical: 14, + paddingHorizontal: 24, + borderRadius: 24, + alignItems: 'center', + justifyContent: 'center', + marginTop: 8, + }, + infoModalButtonText: { + fontSize: 15, + fontWeight: '600', + color: '#FFFFFF', + }, primaryButton: { flex: 1, borderRadius: 24, diff --git a/ios/OutLive.xcodeproj/project.pbxproj b/ios/OutLive.xcodeproj/project.pbxproj index 75e5ec8..9a52c3e 100644 --- a/ios/OutLive.xcodeproj/project.pbxproj +++ b/ios/OutLive.xcodeproj/project.pbxproj @@ -261,6 +261,7 @@ "${PODS_CONFIGURATION_BUILD_DIR}/EXNotifications/ExpoNotifications_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXTaskManager/ExpoTaskManager_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoMediaLibrary/ExpoMediaLibrary_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/PurchasesHybridCommon/PurchasesHybridCommon.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", @@ -282,6 +283,7 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoNotifications_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoTaskManager_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoMediaLibrary_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/PurchasesHybridCommon.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", diff --git a/ios/OutLive/Info.plist b/ios/OutLive/Info.plist index 1fe19a4..c1c0fe3 100644 --- a/ios/OutLive/Info.plist +++ b/ios/OutLive/Info.plist @@ -80,6 +80,7 @@ UIBackgroundModes fetch + remote-notification UILaunchStoryboardName SplashScreen diff --git a/ios/OutLive/OutLive.entitlements b/ios/OutLive/OutLive.entitlements index 3ab3686..0eaca8a 100644 --- a/ios/OutLive/OutLive.entitlements +++ b/ios/OutLive/OutLive.entitlements @@ -3,7 +3,7 @@ aps-environment - development + production com.apple.developer.applesignin Default diff --git a/ios/Podfile.lock b/ios/Podfile.lock index ec8d926..52cd5df 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -71,6 +71,9 @@ PODS: - ExpoModulesCore - ExpoLinking (8.0.8): - ExpoModulesCore + - ExpoMediaLibrary (18.2.0): + - ExpoModulesCore + - React-Core - ExpoModulesCore (3.0.21): - hermes-engine - RCTRequired @@ -104,7 +107,7 @@ PODS: - ExpoModulesCore - ExpoSystemUI (6.0.7): - ExpoModulesCore - - ExpoUI (0.2.0-beta.4): + - ExpoUI (0.2.0-beta.7): - ExpoModulesCore - ExpoWebBrowser (15.0.8): - ExpoModulesCore @@ -1508,9 +1511,31 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga + - react-native-view-shot (4.0.3): + - hermes-engine + - RCTRequired + - RCTTypeSafety + - React-Core + - React-Core-prebuilt + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - ReactNativeDependencies + - Yoga - react-native-voice (3.2.4): - React-Core - - react-native-webview (13.16.0): + - react-native-webview (13.15.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1927,7 +1952,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - RNCPicker (2.11.2): + - RNCPicker (2.11.1): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1949,7 +1974,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - RNDateTimePicker (8.4.5): + - RNDateTimePicker (8.4.4): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2119,7 +2144,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - RNSentry (7.1.0): + - RNSentry (7.2.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2141,9 +2166,9 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - - Sentry/HybridSDK (= 8.56.0) + - Sentry/HybridSDK (= 8.56.1) - Yoga - - RNSVG (15.14.0): + - RNSVG (15.12.1): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2164,9 +2189,9 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - - RNSVG/common (= 15.14.0) + - RNSVG/common (= 15.12.1) - Yoga - - RNSVG/common (15.14.0): + - RNSVG/common (15.12.1): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2270,7 +2295,7 @@ PODS: - SDWebImageWebPCoder (0.14.6): - libwebp (~> 1.0) - SDWebImage/Core (~> 5.17) - - Sentry/HybridSDK (8.56.0) + - Sentry/HybridSDK (8.56.1) - UMAppLoader (6.0.7) - Yoga (0.0.0) - ZXingObjC/Core (3.6.9) @@ -2300,6 +2325,7 @@ DEPENDENCIES: - ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`) - ExpoLinearGradient (from `../node_modules/expo-linear-gradient/ios`) - ExpoLinking (from `../node_modules/expo-linking/ios`) + - ExpoMediaLibrary (from `../node_modules/expo-media-library/ios`) - ExpoModulesCore (from `../node_modules/expo-modules-core`) - ExpoQuickActions (from `../node_modules/expo-quick-actions/ios`) - ExpoSplashScreen (from `../node_modules/expo-splash-screen/ios`) @@ -2348,6 +2374,7 @@ DEPENDENCIES: - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) - react-native-render-html (from `../node_modules/react-native-render-html`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) + - react-native-view-shot (from `../node_modules/react-native-view-shot`) - "react-native-voice (from `../node_modules/@react-native-voice/voice`)" - react-native-webview (from `../node_modules/react-native-webview`) - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) @@ -2452,6 +2479,8 @@ EXTERNAL SOURCES: :path: "../node_modules/expo-linear-gradient/ios" ExpoLinking: :path: "../node_modules/expo-linking/ios" + ExpoMediaLibrary: + :path: "../node_modules/expo-media-library/ios" ExpoModulesCore: :path: "../node_modules/expo-modules-core" ExpoQuickActions: @@ -2547,6 +2576,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-render-html" react-native-safe-area-context: :path: "../node_modules/react-native-safe-area-context" + react-native-view-shot: + :path: "../node_modules/react-native-view-shot" react-native-voice: :path: "../node_modules/@react-native-voice/voice" react-native-webview: @@ -2663,13 +2694,14 @@ SPEC CHECKSUMS: ExpoKeepAwake: 1a2e820692e933c94a565ec3fbbe38ac31658ffe ExpoLinearGradient: a464898cb95153125e3b81894fd479bcb1c7dd27 ExpoLinking: f051f28e50ea9269ff539317c166adec81d9342d + ExpoMediaLibrary: 641a6952299b395159ccd459bd8f5f6764bf55fe ExpoModulesCore: 3a6eb12a5f4d67b2f5fc7d0bc4777b18348f2d7a ExpoQuickActions: 31a70aa6a606128de4416a4830e09cfabfe6667f ExpoSplashScreen: cbb839de72110dea1851dd3e85080b7923af2540 ExpoSQLite: 7fa091ba5562474093fef09be644161a65e11b3f ExpoSymbols: 1ae04ce686de719b9720453b988d8bc5bf776c68 ExpoSystemUI: 6cd74248a2282adf6dec488a75fa532d69dee314 - ExpoUI: 5e44b62e2589b7bc8a6123943105a230c693d000 + ExpoUI: b99a1d1ef5352a60bebf4f4fd3a50d2f896ae804 ExpoWebBrowser: d04a0d6247a0bea4519fbc2ea816610019ad83e0 EXTaskManager: cf225704fab8de8794a6f57f7fa41a90c0e2cd47 FBLazyVector: 9e0cd874afd81d9a4d36679daca991b58b260d42 @@ -2715,8 +2747,9 @@ SPEC CHECKSUMS: React-microtasksnativemodule: 76905804171d8ccbe69329fc84c57eb7934add7f react-native-render-html: 5afc4751f1a98621b3009432ef84c47019dcb2bd react-native-safe-area-context: 42a1b4f8774b577d03b53de7326e3d5757fe9513 + react-native-view-shot: fb3c0774edb448f42705491802a455beac1502a2 react-native-voice: 908a0eba96c8c3d643e4f98b7232c6557d0a6f9c - react-native-webview: 3e303e80cadb5f17118c8c1502aa398e9287e415 + react-native-webview: b29007f4723bca10872028067b07abacfa1cb35a React-NativeModulesApple: a9464983ccc0f66f45e93558671f60fc7536e438 React-oscompat: 73db7dbc80edef36a9d6ed3c6c4e1724ead4236d React-perflogger: 123272debf907cc423962adafcf4513320e43757 @@ -2751,21 +2784,21 @@ SPEC CHECKSUMS: RevenueCat: a51003d4cb33820cc504cf177c627832b462a98e RNCAsyncStorage: 3a4f5e2777dae1688b781a487923a08569e27fe4 RNCMaskedView: d2578d41c59b936db122b2798ba37e4722d21035 - RNCPicker: ddce382c4b42ea2ee36dd588066f0c6d5a240707 - RNDateTimePicker: 7dda2673bd2a6022ea8888fe669d735b2eac0b2d + RNCPicker: a7170edbcbf8288de8edb2502e08e7fc757fa755 + RNDateTimePicker: be0e44bcb9ed0607c7c5f47dbedd88cf091f6791 RNDeviceInfo: bcce8752b5043a623fe3c26789679b473f705d3c RNGestureHandler: 2914750df066d89bf9d8f48a10ad5f0051108ac3 RNPurchases: 2569675abdc1dbc739f2eec0fa564a112cf860de RNReanimated: 3895a29fdf77bbe2a627e1ed599a5e5d1df76c29 RNScreens: d8d6f1792f6e7ac12b0190d33d8d390efc0c1845 - RNSentry: dbee413744aec703b8763b620b14ed7a1e2db095 - RNSVG: 6c534e37eaaefe882b3f55294d0d607de20562dc + RNSentry: 41979b419908128847ef662cc130a400b7576fa9 + RNSVG: 31d6639663c249b7d5abc9728dde2041eb2a3c34 RNWorklets: 54d8dffb7f645873a58484658ddfd4bd1a9a0bc1 SDWebImage: 16309af6d214ba3f77a7c6f6fdda888cb313a50a SDWebImageAVIFCoder: afe194a084e851f70228e4be35ef651df0fc5c57 SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 - Sentry: 3d82977434c80381cae856c40b99c39e4be6bc11 + Sentry: b3ec44d01708fce73f99b544beb57e890eca4406 UMAppLoader: e1234c45d2b7da239e9e90fc4bbeacee12afd5b6 Yoga: 051f086b5ccf465ff2ed38a2cf5a558ae01aaaa1 ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 diff --git a/package-lock.json b/package-lock.json index db8f8f6..453f276 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,18 +9,18 @@ "version": "1.0.2", "dependencies": { "@expo/metro-runtime": "~6.1.2", - "@expo/ui": "~0.2.0-beta.4", + "@expo/ui": "~0.2.0-beta.7", "@expo/vector-icons": "^15.0.2", "@react-native-async-storage/async-storage": "^2.2.0", - "@react-native-community/datetimepicker": "8.4.5", + "@react-native-community/datetimepicker": "8.4.4", "@react-native-masked-view/masked-view": "^0.3.2", - "@react-native-picker/picker": "2.11.2", + "@react-native-picker/picker": "2.11.1", "@react-native-voice/voice": "^3.2.4", - "@react-navigation/bottom-tabs": "^7.4.7", + "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.4", - "@react-navigation/native": "^7.1.17", + "@react-navigation/native": "^7.1.8", "@reduxjs/toolkit": "^2.9.0", - "@sentry/react-native": "~7.1.0", + "@sentry/react-native": "~7.2.0", "@types/lodash": "^4.17.20", "dayjs": "^1.11.18", "expo": "^54.0.13", @@ -36,6 +36,7 @@ "expo-image-picker": "~17.0.8", "expo-linear-gradient": "~15.0.7", "expo-linking": "~8.0.8", + "expo-media-library": "^18.2.0", "expo-notifications": "~0.32.12", "expo-quick-actions": "^6.0.0", "expo-router": "~6.0.12", @@ -63,10 +64,11 @@ "react-native-render-html": "^6.3.4", "react-native-safe-area-context": "~5.6.1", "react-native-screens": "~4.16.0", - "react-native-svg": "^15.13.0", + "react-native-svg": "15.12.1", "react-native-toast-message": "^2.3.3", + "react-native-view-shot": "^4.0.3", "react-native-web": "^0.21.1", - "react-native-webview": "13.16.0", + "react-native-webview": "13.15.0", "react-native-wheel-picker-expo": "^0.5.4", "react-redux": "^9.2.0" }, @@ -2224,9 +2226,9 @@ "license": "MIT" }, "node_modules/@expo/ui": { - "version": "0.2.0-beta.4", - "resolved": "https://mirrors.tencent.com/npm/@expo/ui/-/ui-0.2.0-beta.4.tgz", - "integrity": "sha512-49DoNCQ5jyLFvnTZpVU1aMSbfgRtX4QQtnX7WM97A7dJhocvaF7g7MA3LBeoZ07MMVKuuwfcIBM2F/f52S+6zw==", + "version": "0.2.0-beta.7", + "resolved": "https://mirrors.tencent.com/npm/@expo/ui/-/ui-0.2.0-beta.7.tgz", + "integrity": "sha512-oz2HEpwll+yMFUKbryZ84IgxjLx7RPxxMDVKpCEsK0OhETrLF5NxHlpCkKjdLuQL3QiVSvj5kn6hBFknco3aCw==", "license": "MIT", "dependencies": { "sf-symbols-typescript": "^2.1.0" @@ -3031,9 +3033,9 @@ } }, "node_modules/@react-native-community/datetimepicker": { - "version": "8.4.5", - "resolved": "https://mirrors.tencent.com/npm/@react-native-community/datetimepicker/-/datetimepicker-8.4.5.tgz", - "integrity": "sha512-vvVOJAHjU8TFBzTUjQzANCL6C3pZSE2zjfutCATk790uz7ASEc2tOBD+EIG4BTelWtP2G9jqvXp2L7XGdhEBRg==", + "version": "8.4.4", + "resolved": "https://mirrors.tencent.com/npm/@react-native-community/datetimepicker/-/datetimepicker-8.4.4.tgz", + "integrity": "sha512-bc4ZixEHxZC9/qf5gbdYvIJiLZ5CLmEsC3j+Yhe1D1KC/3QhaIfGDVdUcid0PdlSoGOSEq4VlB93AWyetEyBSQ==", "license": "MIT", "dependencies": { "invariant": "^2.2.4" @@ -3064,9 +3066,9 @@ } }, "node_modules/@react-native-picker/picker": { - "version": "2.11.2", - "resolved": "https://mirrors.tencent.com/npm/@react-native-picker/picker/-/picker-2.11.2.tgz", - "integrity": "sha512-2zyFdW4jgHjF+NeuDZ4nl3hJ+8suey69bI3yljqhNyowfklW2NwNrdDUaJ2iwtPCpk2pt7834aPF8TI6iyZRhA==", + "version": "2.11.1", + "resolved": "https://mirrors.tencent.com/npm/@react-native-picker/picker/-/picker-2.11.1.tgz", + "integrity": "sha512-ThklnkK4fV3yynnIIRBkxxjxR4IFbdMNJVF6tlLdOJ/zEFUEFUEdXY0KmH0iYzMwY8W4/InWsLiA7AkpAbnexA==", "license": "MIT", "workspaces": [ "example" @@ -3717,9 +3719,9 @@ } }, "node_modules/@sentry/cli": { - "version": "2.53.0", - "resolved": "https://mirrors.tencent.com/npm/@sentry/cli/-/cli-2.53.0.tgz", - "integrity": "sha512-n2ZNb+5Z6AZKQSI0SusQ7ZzFL637mfw3Xh4C3PEyVSn9LiF683fX0TTq8OeGmNZQS4maYfS95IFD+XpydU0dEA==", + "version": "2.55.0", + "resolved": "https://mirrors.tencent.com/npm/@sentry/cli/-/cli-2.55.0.tgz", + "integrity": "sha512-cynvcIM2xL8ddwELyFRSpZQw4UtFZzoM2rId2l9vg7+wDREPDocMJB9lEQpBIo3eqhp9JswqUT037yjO6iJ5Sw==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -3736,20 +3738,20 @@ "node": ">= 10" }, "optionalDependencies": { - "@sentry/cli-darwin": "2.53.0", - "@sentry/cli-linux-arm": "2.53.0", - "@sentry/cli-linux-arm64": "2.53.0", - "@sentry/cli-linux-i686": "2.53.0", - "@sentry/cli-linux-x64": "2.53.0", - "@sentry/cli-win32-arm64": "2.53.0", - "@sentry/cli-win32-i686": "2.53.0", - "@sentry/cli-win32-x64": "2.53.0" + "@sentry/cli-darwin": "2.55.0", + "@sentry/cli-linux-arm": "2.55.0", + "@sentry/cli-linux-arm64": "2.55.0", + "@sentry/cli-linux-i686": "2.55.0", + "@sentry/cli-linux-x64": "2.55.0", + "@sentry/cli-win32-arm64": "2.55.0", + "@sentry/cli-win32-i686": "2.55.0", + "@sentry/cli-win32-x64": "2.55.0" } }, "node_modules/@sentry/cli-darwin": { - "version": "2.53.0", - "resolved": "https://mirrors.tencent.com/npm/@sentry/cli-darwin/-/cli-darwin-2.53.0.tgz", - "integrity": "sha512-NNPfpILMwKgpHiyJubHHuauMKltkrgLQ5tvMdxNpxY60jBNdo5VJtpESp4XmXlnidzV4j1z61V4ozU6ttDgt5Q==", + "version": "2.55.0", + "resolved": "https://mirrors.tencent.com/npm/@sentry/cli-darwin/-/cli-darwin-2.55.0.tgz", + "integrity": "sha512-jGHE7SHHzqXUmnsmRLgorVH6nmMmTjQQXdPZbSL5tRtH8d3OIYrVNr5D72DSgD26XAPBDMV0ibqOQ9NKoiSpfA==", "license": "BSD-3-Clause", "optional": true, "os": [ @@ -3759,6 +3761,128 @@ "node": ">=10" } }, + "node_modules/@sentry/cli-linux-arm": { + "version": "2.55.0", + "resolved": "https://mirrors.tencent.com/npm/@sentry/cli-linux-arm/-/cli-linux-arm-2.55.0.tgz", + "integrity": "sha512-ATjU0PsiWADSPLF/kZroLZ7FPKd5W9TDWHVkKNwIUNTei702LFgTjNeRwOIzTgSvG3yTmVEqtwFQfFN/7hnVXQ==", + "cpu": [ + "arm" + ], + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "linux", + "freebsd", + "android" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-linux-arm64": { + "version": "2.55.0", + "resolved": "https://mirrors.tencent.com/npm/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.55.0.tgz", + "integrity": "sha512-jNB/0/gFcOuDCaY/TqeuEpsy/k52dwyk1SOV3s1ku4DUsln6govTppeAGRewY3T1Rj9B2vgIWTrnB8KVh9+Rgg==", + "cpu": [ + "arm64" + ], + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "linux", + "freebsd", + "android" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-linux-i686": { + "version": "2.55.0", + "resolved": "https://mirrors.tencent.com/npm/@sentry/cli-linux-i686/-/cli-linux-i686-2.55.0.tgz", + "integrity": "sha512-8LZjo6PncTM6bWdaggscNOi5r7F/fqRREsCwvd51dcjGj7Kp1plqo9feEzYQ+jq+KUzVCiWfHrUjddFmYyZJrg==", + "cpu": [ + "x86", + "ia32" + ], + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "linux", + "freebsd", + "android" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-linux-x64": { + "version": "2.55.0", + "resolved": "https://mirrors.tencent.com/npm/@sentry/cli-linux-x64/-/cli-linux-x64-2.55.0.tgz", + "integrity": "sha512-5LUVvq74Yj2cZZy5g5o/54dcWEaX4rf3myTHy73AKhRj1PABtOkfexOLbF9xSrZy95WXWaXyeH+k5n5z/vtHfA==", + "cpu": [ + "x64" + ], + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "linux", + "freebsd", + "android" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-win32-arm64": { + "version": "2.55.0", + "resolved": "https://mirrors.tencent.com/npm/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.55.0.tgz", + "integrity": "sha512-cWIQdzm1pfLwPARsV6dUb8TVd6Y3V1A2VWxjTons3Ift6GvtVmiAe0OWL8t2Yt95i8v61kTD/6Tq21OAaogqzA==", + "cpu": [ + "arm64" + ], + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-win32-i686": { + "version": "2.55.0", + "resolved": "https://mirrors.tencent.com/npm/@sentry/cli-win32-i686/-/cli-win32-i686-2.55.0.tgz", + "integrity": "sha512-ldepCn2t9r4I0wvgk7NRaA7coJyy4rTQAzM66u9j5nTEsUldf66xym6esd5ZZRAaJUjffqvHqUIr/lrieTIrVg==", + "cpu": [ + "x86", + "ia32" + ], + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-win32-x64": { + "version": "2.55.0", + "resolved": "https://mirrors.tencent.com/npm/@sentry/cli-win32-x64/-/cli-win32-x64-2.55.0.tgz", + "integrity": "sha512-4hPc/I/9tXx+HLTdTGwlagtAfDSIa2AoTUP30tl32NAYQhx9a6niUbPAemK2qfxesiufJ7D2djX83rCw6WnJVA==", + "cpu": [ + "x64" + ], + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, "node_modules/@sentry/cli/node_modules/agent-base": { "version": "6.0.2", "resolved": "https://mirrors.tencent.com/npm/agent-base/-/agent-base-6.0.2.tgz", @@ -3811,14 +3935,14 @@ } }, "node_modules/@sentry/react-native": { - "version": "7.1.0", - "resolved": "https://mirrors.tencent.com/npm/@sentry/react-native/-/react-native-7.1.0.tgz", - "integrity": "sha512-Nhs/1j+w7cl9Q+FmaBl0+nByeAKpZttWFz1R0YkZJsg01b+4g63pepI3WMwUSq2QrvYIAu/5PiUoTa2dx9HK6g==", + "version": "7.2.0", + "resolved": "https://mirrors.tencent.com/npm/@sentry/react-native/-/react-native-7.2.0.tgz", + "integrity": "sha512-rjqYgEjntPz1sPysud78wi4B9ui7LBVPsG6qr8s/htLMYho9GPGFA5dF+eqsQWqMX8NDReAxNkLTC4+gCNklLQ==", "license": "MIT", "dependencies": { "@sentry/babel-plugin-component-annotate": "4.3.0", "@sentry/browser": "10.12.0", - "@sentry/cli": "2.53.0", + "@sentry/cli": "2.55.0", "@sentry/core": "10.12.0", "@sentry/react": "10.12.0", "@sentry/types": "10.12.0" @@ -5052,6 +5176,15 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://mirrors.tencent.com/npm/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -5819,6 +5952,14 @@ "hyphenate-style-name": "^1.0.3" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://mirrors.tencent.com/npm/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/css-select": { "version": "5.2.2", "resolved": "https://mirrors.tencent.com/npm/css-select/-/css-select-5.2.2.tgz", @@ -7174,6 +7315,16 @@ "react-native": "*" } }, + "node_modules/expo-media-library": { + "version": "18.2.0", + "resolved": "https://mirrors.tencent.com/npm/expo-media-library/-/expo-media-library-18.2.0.tgz", + "integrity": "sha512-aIYLIqmU8LFWrQcfZdwg9f/iWm0wC8uhZ7HiUiTnrigtxf417cVvNokX9afXpIOKBHAHRjVIbcs1nN8KZDE2Fw==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react-native": "*" + } + }, "node_modules/expo-modules-autolinking": { "version": "3.0.15", "resolved": "https://mirrors.tencent.com/npm/expo-modules-autolinking/-/expo-modules-autolinking-3.0.15.tgz", @@ -8487,6 +8638,19 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://mirrors.tencent.com/npm/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/htmlparser2": { "version": "7.2.0", "resolved": "https://mirrors.tencent.com/npm/htmlparser2/-/htmlparser2-7.2.0.tgz", @@ -11526,8 +11690,7 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://mirrors.tencent.com/npm/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/punycode": { "version": "2.3.1", @@ -11952,9 +12115,9 @@ } }, "node_modules/react-native-svg": { - "version": "15.14.0", - "resolved": "https://mirrors.tencent.com/npm/react-native-svg/-/react-native-svg-15.14.0.tgz", - "integrity": "sha512-B3gYc7WztcOT4N54AtUutbe0Nuqqh/nkresY0fAXzUHYLsWuIu/yGiCCD3DKfAs6GLv5LFtWTu7N333Q+e3bkg==", + "version": "15.12.1", + "resolved": "https://mirrors.tencent.com/npm/react-native-svg/-/react-native-svg-15.12.1.tgz", + "integrity": "sha512-vCuZJDf8a5aNC2dlMovEv4Z0jjEUET53lm/iILFnFewa15b4atjVxU6Wirm6O9y6dEsdjDZVD7Q3QM4T1wlI8g==", "license": "MIT", "dependencies": { "css-select": "^5.1.0", @@ -11976,6 +12139,19 @@ "react-native": "*" } }, + "node_modules/react-native-view-shot": { + "version": "4.0.3", + "resolved": "https://mirrors.tencent.com/npm/react-native-view-shot/-/react-native-view-shot-4.0.3.tgz", + "integrity": "sha512-USNjYmED7C0me02c1DxKA0074Hw+y/nxo+xJKlffMvfUWWzL5ELh/TJA/pTnVqFurIrzthZDPtDM7aBFJuhrHQ==", + "license": "MIT", + "dependencies": { + "html2canvas": "^1.4.1" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-web": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.1.tgz", @@ -12009,9 +12185,9 @@ "license": "MIT" }, "node_modules/react-native-webview": { - "version": "13.16.0", - "resolved": "https://mirrors.tencent.com/npm/react-native-webview/-/react-native-webview-13.16.0.tgz", - "integrity": "sha512-Nh13xKZWW35C0dbOskD7OX01nQQavOzHbCw9XoZmar4eXCo7AvrYJ0jlUfRVVIJzqINxHlpECYLdmAdFsl9xDA==", + "version": "13.15.0", + "resolved": "https://mirrors.tencent.com/npm/react-native-webview/-/react-native-webview-13.15.0.tgz", + "integrity": "sha512-Vzjgy8mmxa/JO6l5KZrsTC7YemSdq+qB01diA0FqjUTaWGAGwuykpJ73MDj3+mzBSlaDxAEugHzTtkUQkQEQeQ==", "license": "MIT", "dependencies": { "escape-string-regexp": "^4.0.0", @@ -13634,6 +13810,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://mirrors.tencent.com/npm/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -14210,6 +14395,15 @@ "node": ">= 0.4.0" } }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://mirrors.tencent.com/npm/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/uuid": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", diff --git a/package.json b/package.json index 3fb5c2e..e37e7a7 100644 --- a/package.json +++ b/package.json @@ -10,18 +10,18 @@ }, "dependencies": { "@expo/metro-runtime": "~6.1.2", - "@expo/ui": "~0.2.0-beta.4", + "@expo/ui": "~0.2.0-beta.7", "@expo/vector-icons": "^15.0.2", "@react-native-async-storage/async-storage": "^2.2.0", - "@react-native-community/datetimepicker": "8.4.5", + "@react-native-community/datetimepicker": "8.4.4", "@react-native-masked-view/masked-view": "^0.3.2", - "@react-native-picker/picker": "2.11.2", + "@react-native-picker/picker": "2.11.1", "@react-native-voice/voice": "^3.2.4", - "@react-navigation/bottom-tabs": "^7.4.7", + "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.4", - "@react-navigation/native": "^7.1.17", + "@react-navigation/native": "^7.1.8", "@reduxjs/toolkit": "^2.9.0", - "@sentry/react-native": "~7.1.0", + "@sentry/react-native": "~7.2.0", "@types/lodash": "^4.17.20", "dayjs": "^1.11.18", "expo": "^54.0.13", @@ -37,6 +37,7 @@ "expo-image-picker": "~17.0.8", "expo-linear-gradient": "~15.0.7", "expo-linking": "~8.0.8", + "expo-media-library": "^18.2.0", "expo-notifications": "~0.32.12", "expo-quick-actions": "^6.0.0", "expo-router": "~6.0.12", @@ -64,10 +65,11 @@ "react-native-render-html": "^6.3.4", "react-native-safe-area-context": "~5.6.1", "react-native-screens": "~4.16.0", - "react-native-svg": "^15.13.0", + "react-native-svg": "15.12.1", "react-native-toast-message": "^2.3.3", + "react-native-view-shot": "^4.0.3", "react-native-web": "^0.21.1", - "react-native-webview": "13.16.0", + "react-native-webview": "13.15.0", "react-native-wheel-picker-expo": "^0.5.4", "react-redux": "^9.2.0" }, @@ -79,4 +81,4 @@ "typescript": "~5.9.2" }, "private": true -} \ No newline at end of file +} diff --git a/store/fastingSlice.ts b/store/fastingSlice.ts index 1be2f8b..b7a17d5 100644 --- a/store/fastingSlice.ts +++ b/store/fastingSlice.ts @@ -4,7 +4,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import dayjs from 'dayjs'; import type { RootState } from './index'; -export type FastingScheduleOrigin = 'manual' | 'recommended' | 'quick-start'; +export type FastingScheduleOrigin = 'manual' | 'recommended' | 'quick-start' | 'auto'; export type FastingSchedule = { planId: string;