Files
digital-pilates/utils/userPreferences.ts
richarjiang c1c9f22111 feat(review): 集成iOS应用内评分功能
- 新增iOS原生模块AppStoreReviewManager,封装StoreKit评分请求
- 实现appStoreReviewService服务层,管理评分请求时间间隔(14天)
- 在关键用户操作后触发评分请求:完成挑战、记录服药、记录体重、记录饮水
- 优化通知设置页面UI,改进设置项布局和视觉层次
- 调整用药卡片样式,优化状态显示和文字大小
- 新增配置检查脚本check-app-review-setup.sh
- 修改喝水提醒默认状态为关闭

评分请求策略:
- 仅iOS 14.0+支持
- 自动控制请求频率,避免过度打扰用户
- 延迟1秒执行,不阻塞主业务流程
- 所有评分请求均做错误处理,确保不影响核心功能
2025-11-24 10:06:18 +08:00

478 lines
18 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import AsyncStorage from '@/utils/kvStore';
// 用户偏好设置的存储键
const PREFERENCES_KEYS = {
QUICK_WATER_AMOUNT: 'user_preference_quick_water_amount',
WATER_GOAL: 'user_preference_water_goal',
NOTIFICATION_ENABLED: 'user_preference_notification_enabled',
FITNESS_EXERCISE_MINUTES_INFO_DISMISSED: 'user_preference_fitness_exercise_minutes_info_dismissed',
FITNESS_ACTIVE_HOURS_INFO_DISMISSED: 'user_preference_fitness_active_hours_info_dismissed',
WATER_REMINDER_ENABLED: 'user_preference_water_reminder_enabled',
WATER_REMINDER_START_TIME: 'user_preference_water_reminder_start_time',
WATER_REMINDER_END_TIME: 'user_preference_water_reminder_end_time',
WATER_REMINDER_INTERVAL: 'user_preference_water_reminder_interval',
MEDICATION_REMINDER_ENABLED: 'user_preference_medication_reminder_enabled',
NUTRITION_REMINDER_ENABLED: 'user_preference_nutrition_reminder_enabled',
MOOD_REMINDER_ENABLED: 'user_preference_mood_reminder_enabled',
} as const;
// 用户偏好设置接口
export interface UserPreferences {
quickWaterAmount: number;
waterGoal: number;
notificationEnabled: boolean;
fitnessExerciseMinutesInfoDismissed: boolean;
fitnessActiveHoursInfoDismissed: boolean;
waterReminderEnabled: boolean;
waterReminderStartTime: string; // 格式: "08:00"
waterReminderEndTime: string; // 格式: "22:00"
waterReminderInterval: number; // 分钟
medicationReminderEnabled: boolean;
nutritionReminderEnabled: boolean;
moodReminderEnabled: boolean;
}
// 默认的用户偏好设置
const DEFAULT_PREFERENCES: UserPreferences = {
quickWaterAmount: 150, // 默认快速添加饮水量为 150ml
waterGoal: 2000, // 默认每日饮水目标为 2000ml
notificationEnabled: true, // 默认开启消息推送
fitnessExerciseMinutesInfoDismissed: false, // 默认显示锻炼分钟说明
fitnessActiveHoursInfoDismissed: false, // 默认显示活动小时说明
waterReminderEnabled: false, // 默认关闭喝水提醒
waterReminderStartTime: '08:00', // 默认开始时间早上8点
waterReminderEndTime: '22:00', // 默认结束时间晚上10点
waterReminderInterval: 60, // 默认提醒间隔60分钟
medicationReminderEnabled: true, // 默认开启药品提醒
nutritionReminderEnabled: true, // 默认开启营养提醒
moodReminderEnabled: true, // 默认开启心情提醒
};
/**
* 获取用户偏好设置
*/
export const getUserPreferences = async (): Promise<UserPreferences> => {
try {
const quickWaterAmount = await AsyncStorage.getItem(PREFERENCES_KEYS.QUICK_WATER_AMOUNT);
const waterGoal = await AsyncStorage.getItem(PREFERENCES_KEYS.WATER_GOAL);
const notificationEnabled = await AsyncStorage.getItem(PREFERENCES_KEYS.NOTIFICATION_ENABLED);
const fitnessExerciseMinutesInfoDismissed = await AsyncStorage.getItem(PREFERENCES_KEYS.FITNESS_EXERCISE_MINUTES_INFO_DISMISSED);
const fitnessActiveHoursInfoDismissed = await AsyncStorage.getItem(PREFERENCES_KEYS.FITNESS_ACTIVE_HOURS_INFO_DISMISSED);
const waterReminderEnabled = await AsyncStorage.getItem(PREFERENCES_KEYS.WATER_REMINDER_ENABLED);
const waterReminderStartTime = await AsyncStorage.getItem(PREFERENCES_KEYS.WATER_REMINDER_START_TIME);
const waterReminderEndTime = await AsyncStorage.getItem(PREFERENCES_KEYS.WATER_REMINDER_END_TIME);
const waterReminderInterval = await AsyncStorage.getItem(PREFERENCES_KEYS.WATER_REMINDER_INTERVAL);
const medicationReminderEnabled = await AsyncStorage.getItem(PREFERENCES_KEYS.MEDICATION_REMINDER_ENABLED);
const nutritionReminderEnabled = await AsyncStorage.getItem(PREFERENCES_KEYS.NUTRITION_REMINDER_ENABLED);
const moodReminderEnabled = await AsyncStorage.getItem(PREFERENCES_KEYS.MOOD_REMINDER_ENABLED);
return {
quickWaterAmount: quickWaterAmount ? parseInt(quickWaterAmount, 10) : DEFAULT_PREFERENCES.quickWaterAmount,
waterGoal: waterGoal ? parseInt(waterGoal, 10) : DEFAULT_PREFERENCES.waterGoal,
notificationEnabled: notificationEnabled !== null ? notificationEnabled === 'true' : DEFAULT_PREFERENCES.notificationEnabled,
fitnessExerciseMinutesInfoDismissed: fitnessExerciseMinutesInfoDismissed !== null ? fitnessExerciseMinutesInfoDismissed === 'true' : DEFAULT_PREFERENCES.fitnessExerciseMinutesInfoDismissed,
fitnessActiveHoursInfoDismissed: fitnessActiveHoursInfoDismissed !== null ? fitnessActiveHoursInfoDismissed === 'true' : DEFAULT_PREFERENCES.fitnessActiveHoursInfoDismissed,
waterReminderEnabled: waterReminderEnabled !== null ? waterReminderEnabled === 'true' : DEFAULT_PREFERENCES.waterReminderEnabled,
waterReminderStartTime: waterReminderStartTime || DEFAULT_PREFERENCES.waterReminderStartTime,
waterReminderEndTime: waterReminderEndTime || DEFAULT_PREFERENCES.waterReminderEndTime,
waterReminderInterval: waterReminderInterval ? parseInt(waterReminderInterval, 10) : DEFAULT_PREFERENCES.waterReminderInterval,
medicationReminderEnabled: medicationReminderEnabled !== null ? medicationReminderEnabled === 'true' : DEFAULT_PREFERENCES.medicationReminderEnabled,
nutritionReminderEnabled: nutritionReminderEnabled !== null ? nutritionReminderEnabled === 'true' : DEFAULT_PREFERENCES.nutritionReminderEnabled,
moodReminderEnabled: moodReminderEnabled !== null ? moodReminderEnabled === 'true' : DEFAULT_PREFERENCES.moodReminderEnabled,
};
} catch (error) {
console.error('获取用户偏好设置失败:', error);
return DEFAULT_PREFERENCES;
}
};
/**
* 设置快速添加饮水的默认值
* @param amount 饮水量(毫升)
*/
export const setQuickWaterAmount = async (amount: number): Promise<void> => {
try {
// 确保值在合理范围内50ml - 1000ml
const validAmount = Math.max(50, Math.min(1000, amount));
await AsyncStorage.setItem(PREFERENCES_KEYS.QUICK_WATER_AMOUNT, validAmount.toString());
} catch (error) {
console.error('设置快速添加饮水默认值失败:', error);
throw error;
}
};
/**
* 获取快速添加饮水的默认值
*/
export const getQuickWaterAmount = async (): Promise<number> => {
try {
const amount = await AsyncStorage.getItem(PREFERENCES_KEYS.QUICK_WATER_AMOUNT);
return amount ? parseInt(amount, 10) : DEFAULT_PREFERENCES.quickWaterAmount;
} catch (error) {
console.error('获取快速添加饮水默认值失败:', error);
return DEFAULT_PREFERENCES.quickWaterAmount;
}
};
/**
* 设置每日饮水目标
* @param goal 饮水目标(毫升)
*/
export const setWaterGoalToStorage = async (goal: number): Promise<void> => {
try {
// 确保值在合理范围内500ml - 5000ml
const validGoal = Math.max(500, Math.min(5000, goal));
await AsyncStorage.setItem(PREFERENCES_KEYS.WATER_GOAL, validGoal.toString());
} catch (error) {
console.error('设置每日饮水目标失败:', error);
throw error;
}
};
/**
* 获取每日饮水目标
*/
export const getWaterGoalFromStorage = async (): Promise<number> => {
try {
const goal = await AsyncStorage.getItem(PREFERENCES_KEYS.WATER_GOAL);
return goal ? parseInt(goal, 10) : DEFAULT_PREFERENCES.waterGoal;
} catch (error) {
console.error('获取每日饮水目标失败:', error);
return DEFAULT_PREFERENCES.waterGoal;
}
};
/**
* 设置消息推送开关
* @param enabled 是否开启消息推送
*/
export const setNotificationEnabled = async (enabled: boolean): Promise<void> => {
try {
await AsyncStorage.setItem(PREFERENCES_KEYS.NOTIFICATION_ENABLED, enabled.toString());
} catch (error) {
console.error('设置消息推送开关失败:', error);
throw error;
}
};
/**
* 获取消息推送开关状态
*/
export const getNotificationEnabled = async (): Promise<boolean> => {
try {
const enabled = await AsyncStorage.getItem(PREFERENCES_KEYS.NOTIFICATION_ENABLED);
return enabled !== null ? enabled === 'true' : DEFAULT_PREFERENCES.notificationEnabled;
} catch (error) {
console.error('获取消息推送开关状态失败:', error);
return DEFAULT_PREFERENCES.notificationEnabled;
}
};
/**
* 设置健身锻炼分钟说明已阅读状态
* @param dismissed 是否已阅读
*/
export const setFitnessExerciseMinutesInfoDismissed = async (dismissed: boolean): Promise<void> => {
try {
await AsyncStorage.setItem(PREFERENCES_KEYS.FITNESS_EXERCISE_MINUTES_INFO_DISMISSED, dismissed.toString());
} catch (error) {
console.error('设置健身锻炼分钟说明已阅读状态失败:', error);
throw error;
}
};
/**
* 获取健身锻炼分钟说明已阅读状态
*/
export const getFitnessExerciseMinutesInfoDismissed = async (): Promise<boolean> => {
try {
const dismissed = await AsyncStorage.getItem(PREFERENCES_KEYS.FITNESS_EXERCISE_MINUTES_INFO_DISMISSED);
return dismissed !== null ? dismissed === 'true' : DEFAULT_PREFERENCES.fitnessExerciseMinutesInfoDismissed;
} catch (error) {
console.error('获取健身锻炼分钟说明已阅读状态失败:', error);
return DEFAULT_PREFERENCES.fitnessExerciseMinutesInfoDismissed;
}
};
/**
* 设置健身活动小时说明已阅读状态
* @param dismissed 是否已阅读
*/
export const setFitnessActiveHoursInfoDismissed = async (dismissed: boolean): Promise<void> => {
try {
await AsyncStorage.setItem(PREFERENCES_KEYS.FITNESS_ACTIVE_HOURS_INFO_DISMISSED, dismissed.toString());
} catch (error) {
console.error('设置健身活动小时说明已阅读状态失败:', error);
throw error;
}
};
/**
* 获取健身活动小时说明已阅读状态
*/
export const getFitnessActiveHoursInfoDismissed = async (): Promise<boolean> => {
try {
const dismissed = await AsyncStorage.getItem(PREFERENCES_KEYS.FITNESS_ACTIVE_HOURS_INFO_DISMISSED);
return dismissed !== null ? dismissed === 'true' : DEFAULT_PREFERENCES.fitnessActiveHoursInfoDismissed;
} catch (error) {
console.error('获取健身活动小时说明已阅读状态失败:', error);
return DEFAULT_PREFERENCES.fitnessActiveHoursInfoDismissed;
}
};
/**
* 设置喝水提醒开关
* @param enabled 是否开启喝水提醒
*/
export const setWaterReminderEnabled = async (enabled: boolean): Promise<void> => {
try {
await AsyncStorage.setItem(PREFERENCES_KEYS.WATER_REMINDER_ENABLED, enabled.toString());
} catch (error) {
console.error('设置喝水提醒开关失败:', error);
throw error;
}
};
/**
* 获取喝水提醒开关状态
*/
export const getWaterReminderEnabled = async (): Promise<boolean> => {
try {
const enabled = await AsyncStorage.getItem(PREFERENCES_KEYS.WATER_REMINDER_ENABLED);
return enabled !== null ? enabled === 'true' : DEFAULT_PREFERENCES.waterReminderEnabled;
} catch (error) {
console.error('获取喝水提醒开关状态失败:', error);
return DEFAULT_PREFERENCES.waterReminderEnabled;
}
};
/**
* 设置喝水提醒开始时间
* @param startTime 开始时间,格式为 "HH:mm"
*/
export const setWaterReminderStartTime = async (startTime: string): Promise<void> => {
try {
await AsyncStorage.setItem(PREFERENCES_KEYS.WATER_REMINDER_START_TIME, startTime);
} catch (error) {
console.error('设置喝水提醒开始时间失败:', error);
throw error;
}
};
/**
* 获取喝水提醒开始时间
*/
export const getWaterReminderStartTime = async (): Promise<string> => {
try {
const startTime = await AsyncStorage.getItem(PREFERENCES_KEYS.WATER_REMINDER_START_TIME);
return startTime || DEFAULT_PREFERENCES.waterReminderStartTime;
} catch (error) {
console.error('获取喝水提醒开始时间失败:', error);
return DEFAULT_PREFERENCES.waterReminderStartTime;
}
};
/**
* 设置喝水提醒结束时间
* @param endTime 结束时间,格式为 "HH:mm"
*/
export const setWaterReminderEndTime = async (endTime: string): Promise<void> => {
try {
await AsyncStorage.setItem(PREFERENCES_KEYS.WATER_REMINDER_END_TIME, endTime);
} catch (error) {
console.error('设置喝水提醒结束时间失败:', error);
throw error;
}
};
/**
* 获取喝水提醒结束时间
*/
export const getWaterReminderEndTime = async (): Promise<string> => {
try {
const endTime = await AsyncStorage.getItem(PREFERENCES_KEYS.WATER_REMINDER_END_TIME);
return endTime || DEFAULT_PREFERENCES.waterReminderEndTime;
} catch (error) {
console.error('获取喝水提醒结束时间失败:', error);
return DEFAULT_PREFERENCES.waterReminderEndTime;
}
};
/**
* 设置喝水提醒间隔时间
* @param interval 间隔时间(分钟),范围 30-180
*/
export const setWaterReminderInterval = async (interval: number): Promise<void> => {
try {
// 确保值在合理范围内30-180分钟
const validInterval = Math.max(30, Math.min(180, interval));
await AsyncStorage.setItem(PREFERENCES_KEYS.WATER_REMINDER_INTERVAL, validInterval.toString());
} catch (error) {
console.error('设置喝水提醒间隔时间失败:', error);
throw error;
}
};
/**
* 获取喝水提醒间隔时间
*/
export const getWaterReminderInterval = async (): Promise<number> => {
try {
const interval = await AsyncStorage.getItem(PREFERENCES_KEYS.WATER_REMINDER_INTERVAL);
return interval ? parseInt(interval, 10) : DEFAULT_PREFERENCES.waterReminderInterval;
} catch (error) {
console.error('获取喝水提醒间隔时间失败:', error);
return DEFAULT_PREFERENCES.waterReminderInterval;
}
};
/**
* 获取完整的喝水提醒配置
*/
export const getWaterReminderSettings = async () => {
try {
const [enabled, startTime, endTime, interval] = await Promise.all([
getWaterReminderEnabled(),
getWaterReminderStartTime(),
getWaterReminderEndTime(),
getWaterReminderInterval(),
]);
return {
enabled,
startTime,
endTime,
interval,
};
} catch (error) {
console.error('获取喝水提醒配置失败:', error);
return {
enabled: DEFAULT_PREFERENCES.waterReminderEnabled,
startTime: DEFAULT_PREFERENCES.waterReminderStartTime,
endTime: DEFAULT_PREFERENCES.waterReminderEndTime,
interval: DEFAULT_PREFERENCES.waterReminderInterval,
};
}
};
/**
* 批量设置喝水提醒配置
*/
export const setWaterReminderSettings = async (settings: {
enabled: boolean;
startTime: string;
endTime: string;
interval: number;
}): Promise<void> => {
try {
await Promise.all([
setWaterReminderEnabled(settings.enabled),
setWaterReminderStartTime(settings.startTime),
setWaterReminderEndTime(settings.endTime),
setWaterReminderInterval(settings.interval),
]);
} catch (error) {
console.error('批量设置喝水提醒配置失败:', error);
throw error;
}
};
/**
* 重置所有用户偏好设置为默认值
*/
export const resetUserPreferences = async (): Promise<void> => {
try {
await AsyncStorage.removeItem(PREFERENCES_KEYS.QUICK_WATER_AMOUNT);
await AsyncStorage.removeItem(PREFERENCES_KEYS.NOTIFICATION_ENABLED);
await AsyncStorage.removeItem(PREFERENCES_KEYS.FITNESS_EXERCISE_MINUTES_INFO_DISMISSED);
await AsyncStorage.removeItem(PREFERENCES_KEYS.FITNESS_ACTIVE_HOURS_INFO_DISMISSED);
await AsyncStorage.removeItem(PREFERENCES_KEYS.WATER_REMINDER_ENABLED);
await AsyncStorage.removeItem(PREFERENCES_KEYS.WATER_REMINDER_START_TIME);
await AsyncStorage.removeItem(PREFERENCES_KEYS.WATER_REMINDER_END_TIME);
await AsyncStorage.removeItem(PREFERENCES_KEYS.WATER_REMINDER_INTERVAL);
await AsyncStorage.removeItem(PREFERENCES_KEYS.MEDICATION_REMINDER_ENABLED);
await AsyncStorage.removeItem(PREFERENCES_KEYS.NUTRITION_REMINDER_ENABLED);
await AsyncStorage.removeItem(PREFERENCES_KEYS.MOOD_REMINDER_ENABLED);
} catch (error) {
console.error('重置用户偏好设置失败:', error);
throw error;
}
};
/**
* 设置药品提醒开关
* @param enabled 是否开启药品提醒
*/
export const setMedicationReminderEnabled = async (enabled: boolean): Promise<void> => {
try {
await AsyncStorage.setItem(PREFERENCES_KEYS.MEDICATION_REMINDER_ENABLED, enabled.toString());
} catch (error) {
console.error('设置药品提醒开关失败:', error);
throw error;
}
};
/**
* 获取药品提醒开关状态
*/
export const getMedicationReminderEnabled = async (): Promise<boolean> => {
try {
const enabled = await AsyncStorage.getItem(PREFERENCES_KEYS.MEDICATION_REMINDER_ENABLED);
return enabled !== null ? enabled === 'true' : DEFAULT_PREFERENCES.medicationReminderEnabled;
} catch (error) {
console.error('获取药品提醒开关状态失败:', error);
return DEFAULT_PREFERENCES.medicationReminderEnabled;
}
};
/**
* 设置营养提醒开关
* @param enabled 是否开启营养提醒
*/
export const setNutritionReminderEnabled = async (enabled: boolean): Promise<void> => {
try {
await AsyncStorage.setItem(PREFERENCES_KEYS.NUTRITION_REMINDER_ENABLED, enabled.toString());
} catch (error) {
console.error('设置营养提醒开关失败:', error);
throw error;
}
};
/**
* 获取营养提醒开关状态
*/
export const getNutritionReminderEnabled = async (): Promise<boolean> => {
try {
const enabled = await AsyncStorage.getItem(PREFERENCES_KEYS.NUTRITION_REMINDER_ENABLED);
return enabled !== null ? enabled === 'true' : DEFAULT_PREFERENCES.nutritionReminderEnabled;
} catch (error) {
console.error('获取营养提醒开关状态失败:', error);
return DEFAULT_PREFERENCES.nutritionReminderEnabled;
}
};
/**
* 设置心情提醒开关
* @param enabled 是否开启心情提醒
*/
export const setMoodReminderEnabled = async (enabled: boolean): Promise<void> => {
try {
await AsyncStorage.setItem(PREFERENCES_KEYS.MOOD_REMINDER_ENABLED, enabled.toString());
} catch (error) {
console.error('设置心情提醒开关失败:', error);
throw error;
}
};
/**
* 获取心情提醒开关状态
*/
export const getMoodReminderEnabled = async (): Promise<boolean> => {
try {
const enabled = await AsyncStorage.getItem(PREFERENCES_KEYS.MOOD_REMINDER_ENABLED);
return enabled !== null ? enabled === 'true' : DEFAULT_PREFERENCES.moodReminderEnabled;
} catch (error) {
console.error('获取心情提醒开关状态失败:', error);
return DEFAULT_PREFERENCES.moodReminderEnabled;
}
};