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

@@ -5,12 +5,13 @@ import { getTabBarBottomPadding } from '@/constants/TabBar';
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
import { useAuthGuard } from '@/hooks/useAuthGuard';
import { useColorScheme } from '@/hooks/useColorScheme';
import { useNotifications } from '@/hooks/useNotifications';
import { DEFAULT_MEMBER_NAME, fetchActivityHistory, fetchMyProfile } from '@/store/userSlice';
import { Ionicons } from '@expo/vector-icons';
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
import { useFocusEffect } from '@react-navigation/native';
import React, { useMemo, useState } from 'react';
import { Image, Linking, SafeAreaView, ScrollView, StatusBar, StyleSheet, Switch, Text, TouchableOpacity, View } from 'react-native';
import React, { useEffect, useMemo, useState } from 'react';
import { Alert, Image, Linking, SafeAreaView, ScrollView, StatusBar, StyleSheet, Switch, Text, TouchableOpacity, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const DEFAULT_AVATAR_URL = 'https://plates-1251306435.cos.ap-guangzhou.myqcloud.com/images/avatar/avatarGirl01.jpeg';
@@ -21,7 +22,16 @@ export default function PersonalScreen() {
const insets = useSafeAreaInsets();
const tabBarHeight = useBottomTabBarHeight();
const colorScheme = useColorScheme();
const [notificationEnabled, setNotificationEnabled] = useState(true);
// 推送通知相关
const {
isInitialized,
permissionStatus,
requestPermission,
sendNotification,
} = useNotifications();
const [notificationEnabled, setNotificationEnabled] = useState(false);
// 计算底部间距
const bottomPadding = useMemo(() => {
@@ -64,6 +74,41 @@ export default function PersonalScreen() {
// 显示名称
const displayName = (userProfile.name?.trim()) ? userProfile.name : DEFAULT_MEMBER_NAME;
// 监听通知权限状态变化
useEffect(() => {
if (permissionStatus === 'granted') {
setNotificationEnabled(true);
} else {
setNotificationEnabled(false);
}
}, [permissionStatus]);
// 处理通知开关变化
const handleNotificationToggle = async (value: boolean) => {
if (value) {
try {
const status = await requestPermission();
if (status === 'granted') {
setNotificationEnabled(true);
// 发送测试通知
await sendNotification({
title: '通知已开启',
body: '您将收到运动提醒和重要通知',
sound: true,
priority: 'normal',
});
} else {
Alert.alert('权限被拒绝', '请在系统设置中开启通知权限');
}
} catch (error) {
Alert.alert('错误', '请求通知权限失败');
}
} else {
setNotificationEnabled(false);
Alert.alert('通知已关闭', '您将不会收到推送通知');
}
};
// 用户信息头部
const UserHeader = () => (
<View style={styles.sectionContainer}>
@@ -137,14 +182,8 @@ export default function PersonalScreen() {
</View>
{item.type === 'switch' ? (
<Switch
value={isLoggedIn ? notificationEnabled : false}
onValueChange={(value) => {
if (!isLoggedIn) {
pushIfAuthedElseLogin('/profile/notification-settings');
return;
}
setNotificationEnabled(value);
}}
value={item.switchValue || false}
onValueChange={item.onSwitchChange || (() => {})}
trackColor={{ false: '#E5E5E5', true: '#9370DB' }}
thumbColor="#FFFFFF"
style={styles.switch}
@@ -177,6 +216,8 @@ export default function PersonalScreen() {
icon: 'notifications-outline' as const,
title: '消息推送',
type: 'switch' as const,
switchValue: notificationEnabled,
onSwitchChange: handleNotificationToggle,
},
],
},