586 lines
17 KiB
TypeScript
586 lines
17 KiB
TypeScript
import { Colors } from '@/constants/Colors';
|
|
import { useColorScheme } from '@/hooks/useColorScheme';
|
|
import { getQuickWaterAmount, getWaterReminderSettings, setQuickWaterAmount } from '@/utils/userPreferences';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { Picker } from '@react-native-picker/picker';
|
|
import { useFocusEffect } from '@react-navigation/native';
|
|
import { LinearGradient } from 'expo-linear-gradient';
|
|
import { router } from 'expo-router';
|
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
import {
|
|
Alert,
|
|
KeyboardAvoidingView,
|
|
Modal,
|
|
Platform,
|
|
Pressable,
|
|
ScrollView,
|
|
StyleSheet,
|
|
Text,
|
|
TouchableOpacity,
|
|
View
|
|
} from 'react-native';
|
|
|
|
import { HeaderBar } from '@/components/ui/HeaderBar';
|
|
import { useSafeAreaTop } from '@/hooks/useSafeAreaWithPadding';
|
|
|
|
const WaterSettings: React.FC = () => {
|
|
const safeAreaTop = useSafeAreaTop()
|
|
const theme = (useColorScheme() ?? 'light') as 'light' | 'dark';
|
|
const colorTokens = Colors[theme];
|
|
|
|
const [quickAddAmount, setQuickAddAmount] = useState<string>('250');
|
|
|
|
// 喝水提醒设置状态(用于显示当前设置)
|
|
const [waterReminderSettings, setWaterReminderSettings] = useState({
|
|
enabled: false,
|
|
startTime: '08:00',
|
|
endTime: '22:00',
|
|
interval: 60,
|
|
});
|
|
|
|
// 弹窗状态
|
|
const [goalModalVisible, setGoalModalVisible] = useState(false);
|
|
const [quickAddModalVisible, setQuickAddModalVisible] = useState(false);
|
|
|
|
// 临时选中值
|
|
const [tempGoal, setTempGoal] = useState<number>(2000);
|
|
const [tempQuickAdd, setTempQuickAdd] = useState<number>(250);
|
|
|
|
|
|
// 当前饮水目标(从本地存储获取)
|
|
const [currentWaterGoal, setCurrentWaterGoal] = useState(2000);
|
|
|
|
// 打开饮水目标弹窗时初始化临时值
|
|
const openGoalModal = () => {
|
|
setTempGoal(currentWaterGoal);
|
|
setGoalModalVisible(true);
|
|
};
|
|
|
|
// 打开快速添加弹窗时初始化临时值
|
|
const openQuickAddModal = () => {
|
|
setTempQuickAdd(parseInt(quickAddAmount));
|
|
setQuickAddModalVisible(true);
|
|
};
|
|
|
|
// 打开喝水提醒页面
|
|
const openWaterReminderSettings = () => {
|
|
router.push('/water/reminder-settings');
|
|
};
|
|
|
|
|
|
// 处理饮水目标确认
|
|
const handleGoalConfirm = async () => {
|
|
setCurrentWaterGoal(tempGoal);
|
|
setGoalModalVisible(false);
|
|
|
|
// 这里可以添加保存到本地存储或发送到后端的逻辑
|
|
Alert.alert('设置成功', `每日饮水目标已设置为 ${tempGoal}ml`);
|
|
};
|
|
|
|
// 处理快速添加默认值确认
|
|
const handleQuickAddConfirm = async () => {
|
|
setQuickAddAmount(tempQuickAdd.toString());
|
|
setQuickAddModalVisible(false);
|
|
|
|
try {
|
|
await setQuickWaterAmount(tempQuickAdd);
|
|
Alert.alert('设置成功', `快速添加默认值已设置为 ${tempQuickAdd}ml`);
|
|
} catch {
|
|
Alert.alert('设置失败', '无法保存快速添加默认值,请重试');
|
|
}
|
|
};
|
|
|
|
// 加载用户偏好设置
|
|
const loadUserPreferences = useCallback(async () => {
|
|
try {
|
|
const amount = await getQuickWaterAmount();
|
|
setQuickAddAmount(amount.toString());
|
|
setTempQuickAdd(amount);
|
|
|
|
// 加载喝水提醒设置来显示当前设置状态
|
|
const reminderSettings = await getWaterReminderSettings();
|
|
setWaterReminderSettings(reminderSettings);
|
|
} catch (error) {
|
|
console.error('加载用户偏好设置失败:', error);
|
|
}
|
|
}, []);
|
|
|
|
// 初始化加载
|
|
useEffect(() => {
|
|
loadUserPreferences();
|
|
}, [loadUserPreferences]);
|
|
|
|
// 页面聚焦时重新加载设置(从提醒设置页面返回时)
|
|
useFocusEffect(
|
|
useCallback(() => {
|
|
loadUserPreferences();
|
|
}, [loadUserPreferences])
|
|
);
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
{/* 背景渐变 */}
|
|
<LinearGradient
|
|
colors={['#f5e5fbff', '#e5fcfeff', '#eefdffff', '#e6f6fcff']}
|
|
style={styles.gradientBackground}
|
|
start={{ x: 0, y: 0 }}
|
|
end={{ x: 0, y: 1 }}
|
|
/>
|
|
|
|
{/* 装饰性圆圈 */}
|
|
<View style={styles.decorativeCircle1} />
|
|
<View style={styles.decorativeCircle2} />
|
|
|
|
<HeaderBar
|
|
title="饮水设置"
|
|
onBack={() => {
|
|
router.back();
|
|
}}
|
|
/>
|
|
|
|
<KeyboardAvoidingView
|
|
style={styles.keyboardAvoidingView}
|
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
>
|
|
<ScrollView
|
|
style={styles.scrollView}
|
|
contentContainerStyle={[styles.scrollContent, { paddingTop: safeAreaTop }]}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
{/* 设置列表 */}
|
|
<View style={styles.section}>
|
|
<View style={styles.settingsMenuContainer}>
|
|
<TouchableOpacity style={styles.settingsMenuItem} onPress={openGoalModal}>
|
|
<View style={styles.settingsMenuItemLeft}>
|
|
<View style={[styles.settingsIconContainer, { backgroundColor: 'rgba(147, 112, 219, 0.1)' }]}>
|
|
<Ionicons name="flag-outline" size={20} color="#9370DB" />
|
|
</View>
|
|
<View style={styles.settingsMenuItemContent}>
|
|
<Text style={[styles.settingsMenuItemTitle, { color: colorTokens.text }]}>每日饮水目标</Text>
|
|
<Text style={[styles.settingsMenuItemValue, { color: colorTokens.textSecondary }]}>{currentWaterGoal}ml</Text>
|
|
</View>
|
|
</View>
|
|
<Ionicons name="chevron-forward" size={20} color="#CCCCCC" />
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity style={styles.settingsMenuItem} onPress={openQuickAddModal}>
|
|
<View style={styles.settingsMenuItemLeft}>
|
|
<View style={[styles.settingsIconContainer, { backgroundColor: 'rgba(147, 112, 219, 0.1)' }]}>
|
|
<Ionicons name="add-outline" size={20} color="#9370DB" />
|
|
</View>
|
|
<View style={styles.settingsMenuItemContent}>
|
|
<Text style={[styles.settingsMenuItemTitle, { color: colorTokens.text }]}>快速添加默认值</Text>
|
|
<Text style={[styles.settingsMenuItemSubtitle, { color: colorTokens.textSecondary }]}>
|
|
设置点击"+"按钮时添加的默认饮水量
|
|
</Text>
|
|
<Text style={[styles.settingsMenuItemValue, { color: colorTokens.textSecondary }]}>{quickAddAmount}ml</Text>
|
|
</View>
|
|
</View>
|
|
<Ionicons name="chevron-forward" size={20} color="#CCCCCC" />
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity style={[styles.settingsMenuItem, { borderBottomWidth: 0 }]} onPress={openWaterReminderSettings}>
|
|
<View style={styles.settingsMenuItemLeft}>
|
|
<View style={[styles.settingsIconContainer, { backgroundColor: 'rgba(52, 152, 219, 0.1)' }]}>
|
|
<Ionicons name="notifications-outline" size={20} color="#3498DB" />
|
|
</View>
|
|
<View style={styles.settingsMenuItemContent}>
|
|
<Text style={[styles.settingsMenuItemTitle, { color: colorTokens.text }]}>喝水提醒</Text>
|
|
<Text style={[styles.settingsMenuItemSubtitle, { color: colorTokens.textSecondary }]}>
|
|
设置定时提醒您补充水分
|
|
</Text>
|
|
<Text style={[styles.settingsMenuItemValue, { color: colorTokens.textSecondary }]}>
|
|
{waterReminderSettings.enabled ? `${waterReminderSettings.startTime}-${waterReminderSettings.endTime}, 每${waterReminderSettings.interval}分钟` : '已关闭'}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
<Ionicons name="chevron-forward" size={20} color="#CCCCCC" />
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
</ScrollView>
|
|
</KeyboardAvoidingView>
|
|
|
|
{/* 饮水目标编辑弹窗 */}
|
|
<Modal
|
|
visible={goalModalVisible}
|
|
transparent
|
|
animationType="fade"
|
|
onRequestClose={() => setGoalModalVisible(false)}
|
|
>
|
|
<Pressable style={styles.modalBackdrop} onPress={() => setGoalModalVisible(false)} />
|
|
<View style={styles.modalSheet}>
|
|
<View style={styles.modalHandle} />
|
|
<Text style={[styles.modalTitle, { color: colorTokens.text }]}>每日饮水目标</Text>
|
|
<View style={styles.pickerContainer}>
|
|
<Picker
|
|
selectedValue={tempGoal}
|
|
onValueChange={(value) => setTempGoal(value)}
|
|
style={styles.picker}
|
|
>
|
|
{Array.from({ length: 96 }, (_, i) => 500 + i * 100).map(goal => (
|
|
<Picker.Item key={goal} label={`${goal}ml`} value={goal} />
|
|
))}
|
|
</Picker>
|
|
</View>
|
|
<View style={styles.modalActions}>
|
|
<Pressable
|
|
onPress={() => setGoalModalVisible(false)}
|
|
style={[styles.modalBtn, { backgroundColor: colorTokens.pageBackgroundEmphasis }]}
|
|
>
|
|
<Text style={[styles.modalBtnText, { color: colorTokens.text }]}>取消</Text>
|
|
</Pressable>
|
|
<Pressable
|
|
onPress={handleGoalConfirm}
|
|
style={[styles.modalBtn, styles.modalBtnPrimary, { backgroundColor: colorTokens.primary }]}
|
|
>
|
|
<Text style={[styles.modalBtnText, styles.modalBtnTextPrimary, { color: colorTokens.onPrimary }]}>确定</Text>
|
|
</Pressable>
|
|
</View>
|
|
</View>
|
|
</Modal>
|
|
|
|
{/* 快速添加默认值编辑弹窗 */}
|
|
<Modal
|
|
visible={quickAddModalVisible}
|
|
transparent
|
|
animationType="fade"
|
|
onRequestClose={() => setQuickAddModalVisible(false)}
|
|
>
|
|
<Pressable style={styles.modalBackdrop} onPress={() => setQuickAddModalVisible(false)} />
|
|
<View style={styles.modalSheet}>
|
|
<View style={styles.modalHandle} />
|
|
<Text style={[styles.modalTitle, { color: colorTokens.text }]}>快速添加默认值</Text>
|
|
<View style={styles.pickerContainer}>
|
|
<Picker
|
|
selectedValue={tempQuickAdd}
|
|
onValueChange={(value) => setTempQuickAdd(value)}
|
|
style={styles.picker}
|
|
>
|
|
{Array.from({ length: 41 }, (_, i) => 50 + i * 10).map(amount => (
|
|
<Picker.Item key={amount} label={`${amount}ml`} value={amount} />
|
|
))}
|
|
</Picker>
|
|
</View>
|
|
<View style={styles.modalActions}>
|
|
<Pressable
|
|
onPress={() => setQuickAddModalVisible(false)}
|
|
style={[styles.modalBtn, { backgroundColor: colorTokens.pageBackgroundEmphasis }]}
|
|
>
|
|
<Text style={[styles.modalBtnText, { color: colorTokens.text }]}>取消</Text>
|
|
</Pressable>
|
|
<Pressable
|
|
onPress={handleQuickAddConfirm}
|
|
style={[styles.modalBtn, styles.modalBtnPrimary, { backgroundColor: colorTokens.primary }]}
|
|
>
|
|
<Text style={[styles.modalBtnText, styles.modalBtnTextPrimary, { color: colorTokens.onPrimary }]}>确定</Text>
|
|
</Pressable>
|
|
</View>
|
|
</View>
|
|
</Modal>
|
|
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
},
|
|
gradientBackground: {
|
|
position: 'absolute',
|
|
left: 0,
|
|
right: 0,
|
|
top: 0,
|
|
bottom: 0,
|
|
},
|
|
decorativeCircle1: {
|
|
position: 'absolute',
|
|
top: 40,
|
|
right: 20,
|
|
width: 60,
|
|
height: 60,
|
|
borderRadius: 30,
|
|
backgroundColor: '#0EA5E9',
|
|
opacity: 0.1,
|
|
},
|
|
decorativeCircle2: {
|
|
position: 'absolute',
|
|
bottom: -15,
|
|
left: -15,
|
|
width: 40,
|
|
height: 40,
|
|
borderRadius: 20,
|
|
backgroundColor: '#0EA5E9',
|
|
opacity: 0.05,
|
|
},
|
|
keyboardAvoidingView: {
|
|
flex: 1,
|
|
},
|
|
scrollView: {
|
|
flex: 1,
|
|
},
|
|
scrollContent: {
|
|
padding: 20,
|
|
},
|
|
section: {
|
|
marginBottom: 32,
|
|
},
|
|
settingsMenuContainer: {
|
|
backgroundColor: '#FFFFFF',
|
|
borderRadius: 12,
|
|
shadowColor: '#000',
|
|
shadowOffset: { width: 0, height: 1 },
|
|
shadowOpacity: 0.05,
|
|
shadowRadius: 4,
|
|
elevation: 2,
|
|
overflow: 'hidden',
|
|
},
|
|
settingsMenuItem: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
paddingVertical: 16,
|
|
paddingHorizontal: 16,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: '#F1F3F4',
|
|
},
|
|
settingsMenuItemLeft: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
flex: 1,
|
|
},
|
|
settingsIconContainer: {
|
|
width: 32,
|
|
height: 32,
|
|
borderRadius: 6,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginRight: 12,
|
|
},
|
|
settingsMenuItemContent: {
|
|
flex: 1,
|
|
},
|
|
settingsMenuItemTitle: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
marginBottom: 4,
|
|
},
|
|
settingsMenuItemSubtitle: {
|
|
fontSize: 13,
|
|
marginBottom: 6,
|
|
},
|
|
settingsMenuItemValue: {
|
|
fontSize: 14,
|
|
fontWeight: '500',
|
|
},
|
|
modalBackdrop: {
|
|
...StyleSheet.absoluteFillObject,
|
|
backgroundColor: 'rgba(0,0,0,0.4)',
|
|
},
|
|
modalSheet: {
|
|
position: 'absolute',
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
padding: 16,
|
|
backgroundColor: '#FFFFFF',
|
|
borderTopLeftRadius: 16,
|
|
borderTopRightRadius: 16,
|
|
shadowColor: '#000000',
|
|
shadowOffset: { width: 0, height: -2 },
|
|
shadowOpacity: 0.1,
|
|
shadowRadius: 8,
|
|
elevation: 16,
|
|
},
|
|
modalHandle: {
|
|
width: 36,
|
|
height: 4,
|
|
backgroundColor: '#E0E0E0',
|
|
borderRadius: 2,
|
|
alignSelf: 'center',
|
|
marginBottom: 20,
|
|
},
|
|
modalTitle: {
|
|
fontSize: 20,
|
|
fontWeight: '600',
|
|
textAlign: 'center',
|
|
marginBottom: 20,
|
|
},
|
|
pickerContainer: {
|
|
height: 200,
|
|
marginBottom: 20,
|
|
},
|
|
picker: {
|
|
height: 200,
|
|
},
|
|
modalActions: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'flex-end',
|
|
gap: 12,
|
|
},
|
|
modalBtn: {
|
|
paddingHorizontal: 14,
|
|
paddingVertical: 10,
|
|
borderRadius: 10,
|
|
minWidth: 80,
|
|
alignItems: 'center',
|
|
},
|
|
modalBtnPrimary: {
|
|
// backgroundColor will be set dynamically
|
|
},
|
|
modalBtnText: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
},
|
|
modalBtnTextPrimary: {
|
|
// color will be set dynamically
|
|
},
|
|
// 喝水提醒配置弹窗样式
|
|
waterReminderModalSheet: {
|
|
position: 'absolute',
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
padding: 16,
|
|
backgroundColor: '#FFFFFF',
|
|
borderTopLeftRadius: 16,
|
|
borderTopRightRadius: 16,
|
|
maxHeight: '80%',
|
|
shadowColor: '#000000',
|
|
shadowOffset: { width: 0, height: -2 },
|
|
shadowOpacity: 0.1,
|
|
shadowRadius: 8,
|
|
elevation: 16,
|
|
},
|
|
waterReminderContent: {
|
|
flex: 1,
|
|
marginBottom: 20,
|
|
},
|
|
waterReminderSection: {
|
|
marginBottom: 24,
|
|
},
|
|
waterReminderSectionHeader: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
marginBottom: 8,
|
|
},
|
|
waterReminderSectionTitleContainer: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 8,
|
|
},
|
|
waterReminderSectionTitle: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
},
|
|
waterReminderSectionDesc: {
|
|
fontSize: 14,
|
|
lineHeight: 20,
|
|
marginTop: 4,
|
|
},
|
|
timeRangeContainer: {
|
|
flexDirection: 'row',
|
|
gap: 16,
|
|
marginTop: 16,
|
|
},
|
|
timePickerContainer: {
|
|
flex: 1,
|
|
},
|
|
timeLabel: {
|
|
fontSize: 14,
|
|
fontWeight: '500',
|
|
marginBottom: 8,
|
|
},
|
|
timePicker: {
|
|
paddingVertical: 12,
|
|
paddingHorizontal: 16,
|
|
borderRadius: 8,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: 8,
|
|
},
|
|
timePickerText: {
|
|
fontSize: 16,
|
|
fontWeight: '500',
|
|
},
|
|
timePickerIcon: {
|
|
opacity: 0.6,
|
|
},
|
|
intervalContainer: {
|
|
marginTop: 16,
|
|
},
|
|
intervalPickerContainer: {
|
|
backgroundColor: '#F8F9FA',
|
|
borderRadius: 8,
|
|
overflow: 'hidden',
|
|
},
|
|
intervalPicker: {
|
|
height: 120,
|
|
},
|
|
// 时间选择器弹窗样式
|
|
timePickerModalSheet: {
|
|
position: 'absolute',
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
padding: 16,
|
|
backgroundColor: '#FFFFFF',
|
|
borderTopLeftRadius: 16,
|
|
borderTopRightRadius: 16,
|
|
maxHeight: '60%',
|
|
shadowColor: '#000000',
|
|
shadowOffset: { width: 0, height: -2 },
|
|
shadowOpacity: 0.1,
|
|
shadowRadius: 8,
|
|
elevation: 16,
|
|
},
|
|
timePickerContent: {
|
|
flex: 1,
|
|
marginBottom: 20,
|
|
},
|
|
timePickerSection: {
|
|
marginBottom: 20,
|
|
},
|
|
timePickerLabel: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
marginBottom: 12,
|
|
textAlign: 'center',
|
|
},
|
|
hourPickerContainer: {
|
|
backgroundColor: '#F8F9FA',
|
|
borderRadius: 8,
|
|
overflow: 'hidden',
|
|
},
|
|
hourPicker: {
|
|
height: 160,
|
|
},
|
|
timeRangePreview: {
|
|
backgroundColor: '#F0F8FF',
|
|
borderRadius: 8,
|
|
padding: 16,
|
|
marginTop: 16,
|
|
alignItems: 'center',
|
|
},
|
|
timeRangePreviewLabel: {
|
|
fontSize: 12,
|
|
fontWeight: '500',
|
|
marginBottom: 4,
|
|
},
|
|
timeRangePreviewText: {
|
|
fontSize: 18,
|
|
fontWeight: '600',
|
|
marginBottom: 8,
|
|
},
|
|
timeRangeWarning: {
|
|
fontSize: 12,
|
|
color: '#FF6B6B',
|
|
textAlign: 'center',
|
|
lineHeight: 18,
|
|
},
|
|
});
|
|
|
|
export default WaterSettings; |