import AsyncStorage from '@react-native-async-storage/async-storage'; import { NativeModules } from 'react-native'; // Widget数据同步服务 // 用于在主App和iOS Widget之间同步饮水数据 // App Group标识符 const APP_GROUP_ID = 'group.com.anonymous.digitalpilates'; // Widget数据存储键 const WIDGET_DATA_KEYS = { CURRENT_WATER_INTAKE: 'widget_current_water_intake', DAILY_WATER_GOAL: 'widget_daily_water_goal', QUICK_ADD_AMOUNT: 'widget_quick_add_amount', LAST_SYNC_TIME: 'widget_last_sync_time', PENDING_WATER_RECORDS: 'widget_pending_water_records', } as const; export interface WidgetWaterData { currentIntake: number; dailyGoal: number; quickAddAmount: number; lastSyncTime: string; } // 默认数据 const DEFAULT_WIDGET_DATA: WidgetWaterData = { currentIntake: 0, dailyGoal: 2000, quickAddAmount: 150, lastSyncTime: new Date().toISOString(), }; /** * 创建Native Module来访问App Group UserDefaults * 这需要在iOS原生代码中实现 */ interface AppGroupUserDefaults { setString: (groupId: string, key: string, value: string) => Promise; getString: (groupId: string, key: string) => Promise; setNumber: (groupId: string, key: string, value: number) => Promise; getNumber: (groupId: string, key: string) => Promise; removeKey: (groupId: string, key: string) => Promise; setArray: (groupId: string, key: string, value: any[]) => Promise; getArray: (groupId: string, key: string) => Promise; } // Widget待同步水记录接口 export interface PendingWaterRecord { amount: number; recordedAt: string; source: string; widgetAdded: boolean; } // 尝试获取原生模块,如果不存在则使用fallback const AppGroupDefaults: AppGroupUserDefaults | null = NativeModules.AppGroupUserDefaults || null; /** * 将饮水数据同步到Widget */ export const syncWaterDataToWidget = async (data: Partial): Promise => { try { if (!AppGroupDefaults) { console.warn('AppGroupUserDefaults native module not available, falling back to AsyncStorage'); // Fallback到AsyncStorage (仅主App可用) const currentData = await getWidgetDataFromAsyncStorage(); const updatedData = { ...currentData, ...data, lastSyncTime: new Date().toISOString() }; await saveWidgetDataToAsyncStorage(updatedData); return; } // 使用App Group UserDefaults if (data.currentIntake !== undefined) { await AppGroupDefaults.setNumber(APP_GROUP_ID, WIDGET_DATA_KEYS.CURRENT_WATER_INTAKE, data.currentIntake); } if (data.dailyGoal !== undefined) { await AppGroupDefaults.setNumber(APP_GROUP_ID, WIDGET_DATA_KEYS.DAILY_WATER_GOAL, data.dailyGoal); } if (data.quickAddAmount !== undefined) { await AppGroupDefaults.setNumber(APP_GROUP_ID, WIDGET_DATA_KEYS.QUICK_ADD_AMOUNT, data.quickAddAmount); } // 更新同步时间 await AppGroupDefaults.setString(APP_GROUP_ID, WIDGET_DATA_KEYS.LAST_SYNC_TIME, new Date().toISOString()); console.log('Widget data synced successfully:', data); } catch (error) { console.error('Failed to sync data to widget:', error); throw error; } }; /** * 从Widget获取饮水数据 */ export const getWidgetWaterData = async (): Promise => { try { if (!AppGroupDefaults) { console.warn('AppGroupUserDefaults native module not available, falling back to AsyncStorage'); return await getWidgetDataFromAsyncStorage(); } // 从App Group UserDefaults读取数据 const currentIntake = await AppGroupDefaults.getNumber(APP_GROUP_ID, WIDGET_DATA_KEYS.CURRENT_WATER_INTAKE); const dailyGoal = await AppGroupDefaults.getNumber(APP_GROUP_ID, WIDGET_DATA_KEYS.DAILY_WATER_GOAL); const quickAddAmount = await AppGroupDefaults.getNumber(APP_GROUP_ID, WIDGET_DATA_KEYS.QUICK_ADD_AMOUNT); const lastSyncTime = await AppGroupDefaults.getString(APP_GROUP_ID, WIDGET_DATA_KEYS.LAST_SYNC_TIME); return { currentIntake: currentIntake || DEFAULT_WIDGET_DATA.currentIntake, dailyGoal: dailyGoal || DEFAULT_WIDGET_DATA.dailyGoal, quickAddAmount: quickAddAmount || DEFAULT_WIDGET_DATA.quickAddAmount, lastSyncTime: lastSyncTime || DEFAULT_WIDGET_DATA.lastSyncTime, }; } catch (error) { console.error('Failed to get widget data:', error); return DEFAULT_WIDGET_DATA; } }; /** * 清除Widget数据 */ export const clearWidgetData = async (): Promise => { try { if (!AppGroupDefaults) { await AsyncStorage.multiRemove(Object.values(WIDGET_DATA_KEYS)); return; } // 清除App Group UserDefaults中的数据 await Promise.all([ AppGroupDefaults.removeKey(APP_GROUP_ID, WIDGET_DATA_KEYS.CURRENT_WATER_INTAKE), AppGroupDefaults.removeKey(APP_GROUP_ID, WIDGET_DATA_KEYS.DAILY_WATER_GOAL), AppGroupDefaults.removeKey(APP_GROUP_ID, WIDGET_DATA_KEYS.QUICK_ADD_AMOUNT), AppGroupDefaults.removeKey(APP_GROUP_ID, WIDGET_DATA_KEYS.LAST_SYNC_TIME), ]); console.log('Widget data cleared successfully'); } catch (error) { console.error('Failed to clear widget data:', error); throw error; } }; /** * Fallback: 使用AsyncStorage存储Widget数据 */ const saveWidgetDataToAsyncStorage = async (data: WidgetWaterData): Promise => { const dataToStore: [string, string][] = [ [WIDGET_DATA_KEYS.CURRENT_WATER_INTAKE, data.currentIntake.toString()], [WIDGET_DATA_KEYS.DAILY_WATER_GOAL, data.dailyGoal.toString()], [WIDGET_DATA_KEYS.QUICK_ADD_AMOUNT, data.quickAddAmount.toString()], [WIDGET_DATA_KEYS.LAST_SYNC_TIME, data.lastSyncTime], ]; await AsyncStorage.multiSet(dataToStore); }; /** * Fallback: 从AsyncStorage读取Widget数据 */ const getWidgetDataFromAsyncStorage = async (): Promise => { const keys = Object.values(WIDGET_DATA_KEYS); const values = await AsyncStorage.multiGet(keys); const data: any = {}; values.forEach(([key, value]) => { if (value !== null) { if (key === WIDGET_DATA_KEYS.LAST_SYNC_TIME) { data[key] = value; } else { data[key] = parseInt(value, 10); } } }); return { currentIntake: data[WIDGET_DATA_KEYS.CURRENT_WATER_INTAKE] || DEFAULT_WIDGET_DATA.currentIntake, dailyGoal: data[WIDGET_DATA_KEYS.DAILY_WATER_GOAL] || DEFAULT_WIDGET_DATA.dailyGoal, quickAddAmount: data[WIDGET_DATA_KEYS.QUICK_ADD_AMOUNT] || DEFAULT_WIDGET_DATA.quickAddAmount, lastSyncTime: data[WIDGET_DATA_KEYS.LAST_SYNC_TIME] || DEFAULT_WIDGET_DATA.lastSyncTime, }; }; /** * 触发Widget刷新 * 通知iOS系统更新Widget */ export const refreshWidget = async (): Promise => { try { if (NativeModules.WidgetManager?.reloadTimelines) { await NativeModules.WidgetManager.reloadTimelines(); console.log('Widget refresh triggered'); } else { console.warn('WidgetManager native module not available'); } } catch (error) { console.error('Failed to refresh widget:', error); } }; /** * 获取待同步的水记录 */ export const getPendingWaterRecords = async (): Promise => { try { if (!AppGroupDefaults) { // Fallback: 从 AsyncStorage 读取 const pendingRecordsJson = await AsyncStorage.getItem(WIDGET_DATA_KEYS.PENDING_WATER_RECORDS); return pendingRecordsJson ? JSON.parse(pendingRecordsJson) : []; } // 从 App Group UserDefaults 读取 const pendingRecords = await AppGroupDefaults.getArray(APP_GROUP_ID, WIDGET_DATA_KEYS.PENDING_WATER_RECORDS); return pendingRecords || []; } catch (error) { console.error('Failed to get pending water records:', error); return []; } }; /** * 清除待同步的水记录 */ export const clearPendingWaterRecords = async (): Promise => { try { if (!AppGroupDefaults) { // Fallback: 从 AsyncStorage 清除 await AsyncStorage.removeItem(WIDGET_DATA_KEYS.PENDING_WATER_RECORDS); return; } // 从 App Group UserDefaults 清除 await AppGroupDefaults.removeKey(APP_GROUP_ID, WIDGET_DATA_KEYS.PENDING_WATER_RECORDS); console.log('Pending water records cleared successfully'); } catch (error) { console.error('Failed to clear pending water records:', error); } }; /** * 从Widget同步待处理的数据更改到主App * 检查Widget中的数据更改并同步到主App的数据存储 */ export const syncPendingWidgetChanges = async (): Promise<{ hasPendingChanges: boolean; pendingRecords?: PendingWaterRecord[]; lastSyncTime?: string; }> => { try { // 获取待同步的水记录 const pendingRecords = await getPendingWaterRecords(); if (pendingRecords.length > 0) { console.log(`发现 ${pendingRecords.length} 条待同步的水记录`); return { hasPendingChanges: true, pendingRecords, lastSyncTime: new Date().toISOString(), }; } return { hasPendingChanges: false }; } catch (error) { console.error('Failed to sync pending widget changes:', error); return { hasPendingChanges: false }; } }; /** * 标记Widget数据已被主App同步 */ export const markWidgetDataSynced = async (): Promise => { try { const currentTime = new Date().toISOString(); await AsyncStorage.setItem('last_app_widget_sync', currentTime); } catch (error) { console.error('Failed to mark widget data as synced:', error); } };