feat: 集成推送通知功能及相关组件

- 在项目中引入expo-notifications库,支持本地推送通知功能
- 实现通知权限管理,用户可选择开启或关闭通知
- 新增通知发送、定时通知和重复通知功能
- 更新个人页面,集成通知开关和权限请求逻辑
- 编写推送通知功能实现文档,详细描述功能和使用方法
- 优化心情日历页面,确保数据实时刷新
This commit is contained in:
2025-08-22 22:00:05 +08:00
parent c12329bc96
commit 7d28b79d86
20 changed files with 2368 additions and 280 deletions

View File

@@ -1,12 +1,14 @@
import { HeaderBar } from '@/components/ui/HeaderBar';
import { Colors } from '@/constants/Colors';
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
import { useColorScheme } from '@/hooks/useColorScheme';
import { useMoodData } from '@/hooks/useMoodData';
import { getMoodOptions } from '@/services/moodCheckins';
import { selectLatestMoodRecordByDate } from '@/store/moodSlice';
import dayjs from 'dayjs';
import { LinearGradient } from 'expo-linear-gradient';
import { router, useLocalSearchParams } from 'expo-router';
import React, { useEffect, useState } from 'react';
import { router, useFocusEffect, useLocalSearchParams } from 'expo-router';
import React, { useCallback, useEffect, useState } from 'react';
import {
Dimensions,
SafeAreaView,
@@ -51,6 +53,7 @@ export default function MoodCalendarScreen() {
const theme = (useColorScheme() ?? 'light') as 'light' | 'dark';
const colorTokens = Colors[theme];
const params = useLocalSearchParams();
const dispatch = useAppDispatch();
const { fetchMoodRecords, fetchMoodHistoryRecords } = useMoodData();
const { selectedDate } = params;
@@ -58,8 +61,16 @@ export default function MoodCalendarScreen() {
const [currentMonth, setCurrentMonth] = useState(initialDate);
const [selectedDay, setSelectedDay] = useState<number | null>(null);
const [selectedDateMood, setSelectedDateMood] = useState<any>(null);
const [moodRecords, setMoodRecords] = useState<Record<string, any[]>>({});
// 使用 Redux store 中的数据
const moodRecords = useAppSelector(state => state.mood.moodRecords);
// 获取选中日期的数据
const selectedDateString = selectedDay ? dayjs(currentMonth).date(selectedDay).format('YYYY-MM-DD') : null;
const selectedDateMood = useAppSelector(state => {
if (!selectedDateString) return null;
return selectLatestMoodRecordByDate(selectedDateString)(state);
});
const moodOptions = getMoodOptions();
const weekDays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
@@ -68,6 +79,31 @@ export default function MoodCalendarScreen() {
// 生成当前月份的日历数据
const { calendar, today, month, year } = generateCalendarData(currentMonth);
// 加载整个月份的心情数据
const loadMonthMoodData = async (targetMonth: Date) => {
try {
const startDate = dayjs(targetMonth).startOf('month').format('YYYY-MM-DD');
const endDate = dayjs(targetMonth).endOf('month').format('YYYY-MM-DD');
const historyData = await fetchMoodHistoryRecords({ startDate, endDate });
// 历史记录已经通过 fetchMoodHistoryRecords 自动存储到 Redux store 中
// 不需要额外的处理
} catch (error) {
console.error('加载月份心情数据失败:', error);
}
};
// 加载选中日期的心情记录
const loadDailyMoodCheckins = async (dateString: string) => {
try {
await fetchMoodRecords(dateString);
// 不需要手动设置 selectedDateMood因为它现在从 Redux store 中获取
} catch (error) {
console.error('加载心情记录失败:', error);
}
};
// 初始化选中日期
useEffect(() => {
if (selectedDate) {
@@ -86,44 +122,22 @@ export default function MoodCalendarScreen() {
loadMonthMoodData(currentMonth);
}, [selectedDate]);
// 加载整个月份的心情数据
const loadMonthMoodData = async (targetMonth: Date) => {
try {
const startDate = dayjs(targetMonth).startOf('month').format('YYYY-MM-DD');
const endDate = dayjs(targetMonth).endOf('month').format('YYYY-MM-DD');
const historyData = await fetchMoodHistoryRecords({ startDate, endDate });
// 将历史记录按日期分组
const monthData: Record<string, any[]> = {};
historyData.forEach(checkin => {
const date = checkin.checkinDate;
if (!monthData[date]) {
monthData[date] = [];
// 监听页面焦点变化,当从编辑页面返回时刷新数据
useFocusEffect(
useCallback(() => {
// 当页面获得焦点时,刷新当前月份的数据和选中日期的数据
const refreshData = async () => {
if (selectedDay) {
const selectedDateString = dayjs(currentMonth).date(selectedDay).format('YYYY-MM-DD');
await fetchMoodRecords(selectedDateString);
}
monthData[date].push(checkin);
});
setMoodRecords(monthData);
} catch (error) {
console.error('加载月份心情数据失败:', error);
}
};
// 加载选中日期的心情记录
const loadDailyMoodCheckins = async (dateString: string) => {
try {
const checkins = await fetchMoodRecords(dateString);
if (checkins.length > 0) {
setSelectedDateMood(checkins[0]); // 取最新的记录
} else {
setSelectedDateMood(null);
}
} catch (error) {
console.error('加载心情记录失败:', error);
setSelectedDateMood(null);
}
};
const startDate = dayjs(currentMonth).startOf('month').format('YYYY-MM-DD');
const endDate = dayjs(currentMonth).endOf('month').format('YYYY-MM-DD');
await fetchMoodHistoryRecords({ startDate, endDate });
};
refreshData();
}, [currentMonth, selectedDay, fetchMoodRecords, fetchMoodHistoryRecords])
);
// 月份切换函数
const goToPreviousMonth = () => {
@@ -166,7 +180,7 @@ export default function MoodCalendarScreen() {
const renderMoodIcon = (day: number | null, isSelected: boolean) => {
if (!day) return null;
// 检查该日期是否有心情记录
// 检查该日期是否有心情记录 - 现在从 Redux store 中获取
const dayDateString = dayjs(currentMonth).date(day).format('YYYY-MM-DD');
const dayRecords = moodRecords[dayDateString] || [];
const moodRecord = dayRecords.length > 0 ? dayRecords[0] : null;