feat(app): 新增生理周期记录功能与首页卡片自定义支持
- 新增生理周期追踪页面及相关算法逻辑,支持经期记录与预测 - 新增首页统计卡片自定义页面,支持VIP用户调整卡片显示状态与顺序 - 重构首页统计页面布局逻辑,支持动态渲染与混合布局 - 引入 react-native-draggable-flatlist 用于实现拖拽排序功能 - 添加相关多语言配置及用户偏好设置存储接口
This commit is contained in:
260
utils/menstrualCycle.ts
Normal file
260
utils/menstrualCycle.ts
Normal file
@@ -0,0 +1,260 @@
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
|
||||
export type MenstrualDayStatus = 'period' | 'predicted-period' | 'fertile' | 'ovulation-day';
|
||||
|
||||
export type CycleRecord = {
|
||||
startDate: string;
|
||||
periodLength?: number;
|
||||
cycleLength?: number;
|
||||
source?: 'healthkit' | 'manual';
|
||||
};
|
||||
|
||||
export type MenstrualDayInfo = {
|
||||
date: Dayjs;
|
||||
status: MenstrualDayStatus;
|
||||
confirmed: boolean;
|
||||
dayOfCycle?: number;
|
||||
};
|
||||
|
||||
export type MenstrualDayCell =
|
||||
| {
|
||||
type: 'placeholder';
|
||||
key: string;
|
||||
}
|
||||
| {
|
||||
type: 'day';
|
||||
key: string;
|
||||
label: number;
|
||||
date: Dayjs;
|
||||
info?: MenstrualDayInfo;
|
||||
isToday: boolean;
|
||||
};
|
||||
|
||||
export type MenstrualMonth = {
|
||||
id: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
cells: MenstrualDayCell[];
|
||||
};
|
||||
|
||||
export type MenstrualTimeline = {
|
||||
months: MenstrualMonth[];
|
||||
dayMap: Record<string, MenstrualDayInfo>;
|
||||
cycleLength: number;
|
||||
periodLength: number;
|
||||
todayInfo?: MenstrualDayInfo;
|
||||
};
|
||||
|
||||
const STATUS_PRIORITY: Record<MenstrualDayStatus, number> = {
|
||||
'ovulation-day': 4,
|
||||
period: 3,
|
||||
'predicted-period': 2,
|
||||
fertile: 1,
|
||||
};
|
||||
|
||||
export const DEFAULT_CYCLE_LENGTH = 28;
|
||||
export const DEFAULT_PERIOD_LENGTH = 5;
|
||||
|
||||
export const createDefaultRecords = (): CycleRecord[] => {
|
||||
const today = dayjs();
|
||||
const latestStart = today.subtract(4, 'day'); // 默认让今天处于经期第5天
|
||||
const previousStart = latestStart.subtract(DEFAULT_CYCLE_LENGTH, 'day');
|
||||
const olderStart = previousStart.subtract(DEFAULT_CYCLE_LENGTH, 'day');
|
||||
|
||||
return [
|
||||
{ startDate: olderStart.format('YYYY-MM-DD'), periodLength: DEFAULT_PERIOD_LENGTH },
|
||||
{ startDate: previousStart.format('YYYY-MM-DD'), periodLength: DEFAULT_PERIOD_LENGTH },
|
||||
{ startDate: latestStart.format('YYYY-MM-DD'), periodLength: DEFAULT_PERIOD_LENGTH },
|
||||
];
|
||||
};
|
||||
|
||||
const calcAverageCycleLength = (records: CycleRecord[], fallback = DEFAULT_CYCLE_LENGTH) => {
|
||||
if (records.length < 2) return fallback;
|
||||
const sorted = [...records].sort(
|
||||
(a, b) => dayjs(a.startDate).valueOf() - dayjs(b.startDate).valueOf()
|
||||
);
|
||||
const intervals: number[] = [];
|
||||
for (let i = 1; i < sorted.length; i += 1) {
|
||||
const diff = dayjs(sorted[i].startDate).diff(dayjs(sorted[i - 1].startDate), 'day');
|
||||
if (diff > 0) {
|
||||
intervals.push(diff);
|
||||
}
|
||||
}
|
||||
if (!intervals.length) return fallback;
|
||||
const avg = intervals.reduce((sum, cur) => sum + cur, 0) / intervals.length;
|
||||
return Math.round(avg);
|
||||
};
|
||||
|
||||
const calcAveragePeriodLength = (records: CycleRecord[], fallback = DEFAULT_PERIOD_LENGTH) => {
|
||||
const lengths = records
|
||||
.map((r) => r.periodLength)
|
||||
.filter((l): l is number => typeof l === 'number' && l > 0);
|
||||
if (!lengths.length) return fallback;
|
||||
const avg = lengths.reduce((sum, cur) => sum + cur, 0) / lengths.length;
|
||||
return Math.round(avg);
|
||||
};
|
||||
|
||||
const addDayInfo = (
|
||||
dayMap: Record<string, MenstrualDayInfo>,
|
||||
date: Dayjs,
|
||||
info: MenstrualDayInfo
|
||||
) => {
|
||||
const key = date.format('YYYY-MM-DD');
|
||||
const existing = dayMap[key];
|
||||
|
||||
if (existing && STATUS_PRIORITY[existing.status] >= STATUS_PRIORITY[info.status]) {
|
||||
return;
|
||||
}
|
||||
|
||||
dayMap[key] = info;
|
||||
};
|
||||
|
||||
const getOvulationDay = (cycleStart: Dayjs, cycleLength: number) => {
|
||||
// 默认排卵日位于周期的中间偏后,兼容短/长周期
|
||||
const daysFromStart = Math.max(12, Math.round(cycleLength / 2));
|
||||
return cycleStart.add(daysFromStart, 'day');
|
||||
};
|
||||
|
||||
export const buildMenstrualTimeline = (options?: {
|
||||
records?: CycleRecord[];
|
||||
monthsBefore?: number;
|
||||
monthsAfter?: number;
|
||||
defaultCycleLength?: number;
|
||||
defaultPeriodLength?: number;
|
||||
}): MenstrualTimeline => {
|
||||
const today = dayjs();
|
||||
const monthsBefore = options?.monthsBefore ?? 2;
|
||||
const monthsAfter = options?.monthsAfter ?? 3;
|
||||
const startMonth = today.subtract(monthsBefore, 'month').startOf('month');
|
||||
const endMonth = today.add(monthsAfter, 'month').endOf('month');
|
||||
|
||||
const records = (options?.records ?? []).sort(
|
||||
(a, b) => dayjs(a.startDate).valueOf() - dayjs(b.startDate).valueOf()
|
||||
);
|
||||
|
||||
const avgCycleLength =
|
||||
options?.defaultCycleLength ?? calcAverageCycleLength(records, DEFAULT_CYCLE_LENGTH);
|
||||
const avgPeriodLength =
|
||||
options?.defaultPeriodLength ?? calcAveragePeriodLength(records, DEFAULT_PERIOD_LENGTH);
|
||||
|
||||
const cycles = records.map((record) => ({
|
||||
start: dayjs(record.startDate),
|
||||
confirmed: true,
|
||||
periodLength: record.periodLength ?? avgPeriodLength,
|
||||
cycleLength: record.cycleLength ?? avgCycleLength,
|
||||
}));
|
||||
|
||||
// 只有当存在历史记录时,才进行后续预测
|
||||
if (cycles.length > 0) {
|
||||
const lastConfirmed = cycles[cycles.length - 1];
|
||||
let cursorStart = lastConfirmed.start;
|
||||
|
||||
while (cursorStart.isBefore(endMonth)) {
|
||||
cursorStart = cursorStart.add(avgCycleLength, 'day');
|
||||
cycles.push({
|
||||
start: cursorStart,
|
||||
confirmed: false,
|
||||
periodLength: avgPeriodLength,
|
||||
cycleLength: avgCycleLength,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const dayMap: Record<string, MenstrualDayInfo> = {};
|
||||
|
||||
cycles.forEach((cycle) => {
|
||||
const ovulationDay = getOvulationDay(cycle.start, cycle.cycleLength);
|
||||
const fertileStart = ovulationDay.subtract(5, 'day');
|
||||
|
||||
for (let i = 0; i < cycle.periodLength; i += 1) {
|
||||
const date = cycle.start.add(i, 'day');
|
||||
if (date.isBefore(startMonth) || date.isAfter(endMonth)) continue;
|
||||
addDayInfo(dayMap, date, {
|
||||
date,
|
||||
status: cycle.confirmed ? 'period' : 'predicted-period',
|
||||
confirmed: cycle.confirmed,
|
||||
dayOfCycle: i + 1,
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
const date = fertileStart.add(i, 'day');
|
||||
if (date.isBefore(startMonth) || date.isAfter(endMonth)) continue;
|
||||
addDayInfo(dayMap, date, {
|
||||
date,
|
||||
status: 'fertile',
|
||||
confirmed: cycle.confirmed,
|
||||
});
|
||||
}
|
||||
|
||||
if (!ovulationDay.isBefore(startMonth) && !ovulationDay.isAfter(endMonth)) {
|
||||
addDayInfo(dayMap, ovulationDay, {
|
||||
date: ovulationDay,
|
||||
status: 'ovulation-day',
|
||||
confirmed: cycle.confirmed,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const months: MenstrualMonth[] = [];
|
||||
let monthCursor = startMonth.startOf('month');
|
||||
|
||||
while (monthCursor.isBefore(endMonth) || monthCursor.isSame(endMonth, 'month')) {
|
||||
const firstDay = monthCursor.startOf('month');
|
||||
const daysInMonth = firstDay.daysInMonth();
|
||||
// 以周一为周首,符合设计稿呈现
|
||||
const firstWeekday = (firstDay.day() + 6) % 7; // 0(一) - 6(日)
|
||||
const cells: MenstrualDayCell[] = [];
|
||||
|
||||
for (let i = 0; i < firstWeekday; i += 1) {
|
||||
cells.push({ type: 'placeholder', key: `${firstDay.format('YYYY-MM')}-p-${i}` });
|
||||
}
|
||||
|
||||
for (let day = 1; day <= daysInMonth; day += 1) {
|
||||
const date = firstDay.date(day);
|
||||
const key = date.format('YYYY-MM-DD');
|
||||
cells.push({
|
||||
type: 'day',
|
||||
key,
|
||||
label: day,
|
||||
date,
|
||||
info: dayMap[key],
|
||||
isToday: date.isSame(today, 'day'),
|
||||
});
|
||||
}
|
||||
|
||||
while (cells.length % 7 !== 0) {
|
||||
cells.push({
|
||||
type: 'placeholder',
|
||||
key: `${firstDay.format('YYYY-MM')}-t-${cells.length}`,
|
||||
});
|
||||
}
|
||||
|
||||
months.push({
|
||||
id: firstDay.format('YYYY-MM'),
|
||||
title: firstDay.format('M月'),
|
||||
subtitle: firstDay.format('YYYY年'),
|
||||
cells,
|
||||
});
|
||||
|
||||
monthCursor = monthCursor.add(1, 'month');
|
||||
}
|
||||
|
||||
const todayKey = today.format('YYYY-MM-DD');
|
||||
|
||||
return {
|
||||
months,
|
||||
dayMap,
|
||||
cycleLength: avgCycleLength,
|
||||
periodLength: avgPeriodLength,
|
||||
todayInfo: dayMap[todayKey],
|
||||
};
|
||||
};
|
||||
|
||||
export const getMenstrualSummaryForDate = (
|
||||
date: Dayjs,
|
||||
dayMap: Record<string, MenstrualDayInfo>
|
||||
) => {
|
||||
const key = date.format('YYYY-MM-DD');
|
||||
return dayMap[key];
|
||||
};
|
||||
@@ -15,10 +15,57 @@ const PREFERENCES_KEYS = {
|
||||
NUTRITION_REMINDER_ENABLED: 'user_preference_nutrition_reminder_enabled',
|
||||
MOOD_REMINDER_ENABLED: 'user_preference_mood_reminder_enabled',
|
||||
HRV_REMINDER_ENABLED: 'user_preference_hrv_reminder_enabled',
|
||||
|
||||
// 首页身体指标卡片显示设置
|
||||
SHOW_MOOD_CARD: 'user_preference_show_mood_card',
|
||||
SHOW_STEPS_CARD: 'user_preference_show_steps_card',
|
||||
SHOW_STRESS_CARD: 'user_preference_show_stress_card',
|
||||
SHOW_SLEEP_CARD: 'user_preference_show_sleep_card',
|
||||
SHOW_FITNESS_RINGS_CARD: 'user_preference_show_fitness_rings_card',
|
||||
SHOW_WATER_CARD: 'user_preference_show_water_card',
|
||||
SHOW_BASAL_METABOLISM_CARD: 'user_preference_show_basal_metabolism_card',
|
||||
SHOW_OXYGEN_SATURATION_CARD: 'user_preference_show_oxygen_saturation_card',
|
||||
SHOW_MENSTRUAL_CYCLE_CARD: 'user_preference_show_menstrual_cycle_card',
|
||||
SHOW_WEIGHT_CARD: 'user_preference_show_weight_card',
|
||||
SHOW_CIRCUMFERENCE_CARD: 'user_preference_show_circumference_card',
|
||||
|
||||
// 首页身体指标卡片排序设置
|
||||
STATISTICS_CARD_ORDER: 'user_preference_statistics_card_order',
|
||||
} as const;
|
||||
|
||||
// 首页身体指标卡片显示设置接口
|
||||
export interface StatisticsCardsVisibility {
|
||||
showMood: boolean;
|
||||
showSteps: boolean;
|
||||
showStress: boolean;
|
||||
showSleep: boolean;
|
||||
showFitnessRings: boolean;
|
||||
showWater: boolean;
|
||||
showBasalMetabolism: boolean;
|
||||
showOxygenSaturation: boolean;
|
||||
showMenstrualCycle: boolean;
|
||||
showWeight: boolean;
|
||||
showCircumference: boolean;
|
||||
}
|
||||
|
||||
// 默认卡片顺序
|
||||
export const DEFAULT_CARD_ORDER: string[] = [
|
||||
'mood',
|
||||
'steps',
|
||||
'stress',
|
||||
'sleep',
|
||||
'fitness',
|
||||
'water',
|
||||
'metabolism',
|
||||
'oxygen',
|
||||
'menstrual',
|
||||
'weight',
|
||||
'circumference',
|
||||
];
|
||||
|
||||
// 用户偏好设置接口
|
||||
export interface UserPreferences {
|
||||
export interface UserPreferences extends StatisticsCardsVisibility {
|
||||
cardOrder: string[];
|
||||
quickWaterAmount: number;
|
||||
waterGoal: number;
|
||||
notificationEnabled: boolean;
|
||||
@@ -49,6 +96,22 @@ const DEFAULT_PREFERENCES: UserPreferences = {
|
||||
nutritionReminderEnabled: true, // 默认开启营养提醒
|
||||
moodReminderEnabled: true, // 默认开启心情提醒
|
||||
hrvReminderEnabled: true, // 默认开启 HRV 压力提醒
|
||||
|
||||
// 默认显示所有卡片
|
||||
showMood: true,
|
||||
showSteps: true,
|
||||
showStress: true,
|
||||
showSleep: true,
|
||||
showFitnessRings: true,
|
||||
showWater: true,
|
||||
showBasalMetabolism: true,
|
||||
showOxygenSaturation: true,
|
||||
showMenstrualCycle: true,
|
||||
showWeight: true,
|
||||
showCircumference: true,
|
||||
|
||||
// 默认卡片顺序
|
||||
cardOrder: DEFAULT_CARD_ORDER,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -70,6 +133,21 @@ export const getUserPreferences = async (): Promise<UserPreferences> => {
|
||||
const moodReminderEnabled = await AsyncStorage.getItem(PREFERENCES_KEYS.MOOD_REMINDER_ENABLED);
|
||||
const hrvReminderEnabled = await AsyncStorage.getItem(PREFERENCES_KEYS.HRV_REMINDER_ENABLED);
|
||||
|
||||
// 获取首页卡片显示设置
|
||||
const showMood = await AsyncStorage.getItem(PREFERENCES_KEYS.SHOW_MOOD_CARD);
|
||||
const showSteps = await AsyncStorage.getItem(PREFERENCES_KEYS.SHOW_STEPS_CARD);
|
||||
const showStress = await AsyncStorage.getItem(PREFERENCES_KEYS.SHOW_STRESS_CARD);
|
||||
const showSleep = await AsyncStorage.getItem(PREFERENCES_KEYS.SHOW_SLEEP_CARD);
|
||||
const showFitnessRings = await AsyncStorage.getItem(PREFERENCES_KEYS.SHOW_FITNESS_RINGS_CARD);
|
||||
const showWater = await AsyncStorage.getItem(PREFERENCES_KEYS.SHOW_WATER_CARD);
|
||||
const showBasalMetabolism = await AsyncStorage.getItem(PREFERENCES_KEYS.SHOW_BASAL_METABOLISM_CARD);
|
||||
const showOxygenSaturation = await AsyncStorage.getItem(PREFERENCES_KEYS.SHOW_OXYGEN_SATURATION_CARD);
|
||||
const showMenstrualCycle = await AsyncStorage.getItem(PREFERENCES_KEYS.SHOW_MENSTRUAL_CYCLE_CARD);
|
||||
const showWeight = await AsyncStorage.getItem(PREFERENCES_KEYS.SHOW_WEIGHT_CARD);
|
||||
const showCircumference = await AsyncStorage.getItem(PREFERENCES_KEYS.SHOW_CIRCUMFERENCE_CARD);
|
||||
const cardOrderStr = await AsyncStorage.getItem(PREFERENCES_KEYS.STATISTICS_CARD_ORDER);
|
||||
const cardOrder = cardOrderStr ? JSON.parse(cardOrderStr) : DEFAULT_PREFERENCES.cardOrder;
|
||||
|
||||
return {
|
||||
quickWaterAmount: quickWaterAmount ? parseInt(quickWaterAmount, 10) : DEFAULT_PREFERENCES.quickWaterAmount,
|
||||
waterGoal: waterGoal ? parseInt(waterGoal, 10) : DEFAULT_PREFERENCES.waterGoal,
|
||||
@@ -84,6 +162,19 @@ export const getUserPreferences = async (): Promise<UserPreferences> => {
|
||||
nutritionReminderEnabled: nutritionReminderEnabled !== null ? nutritionReminderEnabled === 'true' : DEFAULT_PREFERENCES.nutritionReminderEnabled,
|
||||
moodReminderEnabled: moodReminderEnabled !== null ? moodReminderEnabled === 'true' : DEFAULT_PREFERENCES.moodReminderEnabled,
|
||||
hrvReminderEnabled: hrvReminderEnabled !== null ? hrvReminderEnabled === 'true' : DEFAULT_PREFERENCES.hrvReminderEnabled,
|
||||
|
||||
showMood: showMood !== null ? showMood === 'true' : DEFAULT_PREFERENCES.showMood,
|
||||
showSteps: showSteps !== null ? showSteps === 'true' : DEFAULT_PREFERENCES.showSteps,
|
||||
showStress: showStress !== null ? showStress === 'true' : DEFAULT_PREFERENCES.showStress,
|
||||
showSleep: showSleep !== null ? showSleep === 'true' : DEFAULT_PREFERENCES.showSleep,
|
||||
showFitnessRings: showFitnessRings !== null ? showFitnessRings === 'true' : DEFAULT_PREFERENCES.showFitnessRings,
|
||||
showWater: showWater !== null ? showWater === 'true' : DEFAULT_PREFERENCES.showWater,
|
||||
showBasalMetabolism: showBasalMetabolism !== null ? showBasalMetabolism === 'true' : DEFAULT_PREFERENCES.showBasalMetabolism,
|
||||
showOxygenSaturation: showOxygenSaturation !== null ? showOxygenSaturation === 'true' : DEFAULT_PREFERENCES.showOxygenSaturation,
|
||||
showMenstrualCycle: showMenstrualCycle !== null ? showMenstrualCycle === 'true' : DEFAULT_PREFERENCES.showMenstrualCycle,
|
||||
showWeight: showWeight !== null ? showWeight === 'true' : DEFAULT_PREFERENCES.showWeight,
|
||||
showCircumference: showCircumference !== null ? showCircumference === 'true' : DEFAULT_PREFERENCES.showCircumference,
|
||||
cardOrder,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取用户偏好设置失败:', error);
|
||||
@@ -501,3 +592,92 @@ export const getHRVReminderEnabled = async (): Promise<boolean> => {
|
||||
return DEFAULT_PREFERENCES.hrvReminderEnabled;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取首页卡片显示设置
|
||||
*/
|
||||
export const getStatisticsCardsVisibility = async (): Promise<StatisticsCardsVisibility> => {
|
||||
try {
|
||||
const userPreferences = await getUserPreferences();
|
||||
return {
|
||||
showMood: userPreferences.showMood,
|
||||
showSteps: userPreferences.showSteps,
|
||||
showStress: userPreferences.showStress,
|
||||
showSleep: userPreferences.showSleep,
|
||||
showFitnessRings: userPreferences.showFitnessRings,
|
||||
showWater: userPreferences.showWater,
|
||||
showBasalMetabolism: userPreferences.showBasalMetabolism,
|
||||
showOxygenSaturation: userPreferences.showOxygenSaturation,
|
||||
showMenstrualCycle: userPreferences.showMenstrualCycle,
|
||||
showWeight: userPreferences.showWeight,
|
||||
showCircumference: userPreferences.showCircumference,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取首页卡片显示设置失败:', error);
|
||||
return {
|
||||
showMood: DEFAULT_PREFERENCES.showMood,
|
||||
showSteps: DEFAULT_PREFERENCES.showSteps,
|
||||
showStress: DEFAULT_PREFERENCES.showStress,
|
||||
showSleep: DEFAULT_PREFERENCES.showSleep,
|
||||
showFitnessRings: DEFAULT_PREFERENCES.showFitnessRings,
|
||||
showWater: DEFAULT_PREFERENCES.showWater,
|
||||
showBasalMetabolism: DEFAULT_PREFERENCES.showBasalMetabolism,
|
||||
showOxygenSaturation: DEFAULT_PREFERENCES.showOxygenSaturation,
|
||||
showMenstrualCycle: DEFAULT_PREFERENCES.showMenstrualCycle,
|
||||
showWeight: DEFAULT_PREFERENCES.showWeight,
|
||||
showCircumference: DEFAULT_PREFERENCES.showCircumference,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取首页卡片顺序
|
||||
*/
|
||||
export const getStatisticsCardOrder = async (): Promise<string[]> => {
|
||||
try {
|
||||
const orderStr = await AsyncStorage.getItem(PREFERENCES_KEYS.STATISTICS_CARD_ORDER);
|
||||
return orderStr ? JSON.parse(orderStr) : DEFAULT_CARD_ORDER;
|
||||
} catch (error) {
|
||||
console.error('获取首页卡片顺序失败:', error);
|
||||
return DEFAULT_CARD_ORDER;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置首页卡片顺序
|
||||
*/
|
||||
export const setStatisticsCardOrder = async (order: string[]): Promise<void> => {
|
||||
try {
|
||||
await AsyncStorage.setItem(PREFERENCES_KEYS.STATISTICS_CARD_ORDER, JSON.stringify(order));
|
||||
} catch (error) {
|
||||
console.error('设置首页卡片顺序失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置首页卡片显示设置
|
||||
*/
|
||||
export const setStatisticsCardVisibility = async (key: keyof StatisticsCardsVisibility, value: boolean): Promise<void> => {
|
||||
try {
|
||||
let storageKey: string;
|
||||
switch (key) {
|
||||
case 'showMood': storageKey = PREFERENCES_KEYS.SHOW_MOOD_CARD; break;
|
||||
case 'showSteps': storageKey = PREFERENCES_KEYS.SHOW_STEPS_CARD; break;
|
||||
case 'showStress': storageKey = PREFERENCES_KEYS.SHOW_STRESS_CARD; break;
|
||||
case 'showSleep': storageKey = PREFERENCES_KEYS.SHOW_SLEEP_CARD; break;
|
||||
case 'showFitnessRings': storageKey = PREFERENCES_KEYS.SHOW_FITNESS_RINGS_CARD; break;
|
||||
case 'showWater': storageKey = PREFERENCES_KEYS.SHOW_WATER_CARD; break;
|
||||
case 'showBasalMetabolism': storageKey = PREFERENCES_KEYS.SHOW_BASAL_METABOLISM_CARD; break;
|
||||
case 'showOxygenSaturation': storageKey = PREFERENCES_KEYS.SHOW_OXYGEN_SATURATION_CARD; break;
|
||||
case 'showMenstrualCycle': storageKey = PREFERENCES_KEYS.SHOW_MENSTRUAL_CYCLE_CARD; break;
|
||||
case 'showWeight': storageKey = PREFERENCES_KEYS.SHOW_WEIGHT_CARD; break;
|
||||
case 'showCircumference': storageKey = PREFERENCES_KEYS.SHOW_CIRCUMFERENCE_CARD; break;
|
||||
default: return;
|
||||
}
|
||||
await AsyncStorage.setItem(storageKey, value.toString());
|
||||
} catch (error) {
|
||||
console.error(`设置首页卡片显示失败 (${key}):`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user