feat: 通知权限+推送

This commit is contained in:
jinyaqiu 2025-07-14 20:12:06 +08:00
parent 0307ed0a00
commit 5b23951643
4 changed files with 202 additions and 4 deletions

View File

@ -1,5 +1,6 @@
import IP from '@/assets/icons/svg/ip.svg';
import Lottie from '@/components/lottie/lottie';
import MessagePush from '@/components/message-push';
import { useRouter } from 'expo-router';
import * as SecureStore from 'expo-secure-store';
import { useTranslation } from 'react-i18next';
@ -35,7 +36,7 @@ export default function HomeScreen() {
{"\n"}
{t('auth.welcomeAwaken.back', { ns: 'login' })}
</Text>
<MessagePush />
{/* 唤醒按钮 */}
<TouchableOpacity
className="bg-white rounded-full px-10 py-4 shadow-[0_2px_4px_rgba(0,0,0,0.1)] w-full items-center"

View File

@ -0,0 +1,76 @@
import * as Notifications from 'expo-notifications';
import { useRouter } from 'expo-router';
import React, { useEffect, useRef } from 'react';
import { Button, Text, View } from 'react-native';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false,
shouldShowBanner: true,
shouldShowList: true,
}),
});
export default function MessagePush() {
const router = useRouter();
const notificationListener = useRef<Notifications.Subscription>(null);
const responseListener = useRef<Notifications.Subscription>(null);
useEffect(() => {
// 监听通知点击事件
responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
const data = response.notification.request.content.data;
console.log('通知被点击,数据:', data);
// 根据通知数据跳转到指定页面
if (data.screen === 'ask') {
router.push('/ask');
} else if (data.screen === 'owner') {
router.push('/owner');
}
});
// 清理监听器
return () => {
if (notificationListener.current) {
Notifications.removeNotificationSubscription(notificationListener.current);
}
if (responseListener.current) {
Notifications.removeNotificationSubscription(responseListener.current);
}
};
}, []);
const sendNotification = async () => {
// 请求通知权限
const { status } = await Notifications.requestPermissionsAsync();
if (status !== 'granted') {
alert('请先允许通知权限');
return;
}
// 调度本地通知
await Notifications.scheduleNotificationAsync({
content: {
title: '你有一条新消息 🎉',
body: '点击查看详情内容',
data: { screen: 'ask' },
priority: 'high', // 关键:设置 high 或 max
},
trigger: {
seconds: 2, // 延迟2秒显示
type: Notifications.SchedulableTriggerInputTypes.TIME_INTERVAL // 添加 type 字段
}, // 延迟2秒显示
});
alert('通知将在2秒后显示');
};
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 }}>
<Text></Text>
<Button title="发送通知" onPress={sendNotification} />
</View>
);
}

View File

@ -13,7 +13,7 @@ import LcensesModal from './qualification/lcenses';
import PrivacyModal from './qualification/privacy';
import CustomSwitch from './switch';
import UserInfo from './userInfo';
import { getLocationPermission, getPermissions, requestLocationPermission, requestMediaLibraryPermission, reverseGeocode } from './utils';
import { checkNotificationPermission, getLocationPermission, getPermissions, requestLocationPermission, requestMediaLibraryPermission, requestNotificationPermission, reverseGeocode } from './utils';
const SettingModal = (props: { modalVisible: boolean, setModalVisible: (visible: boolean) => void, userInfo: User }) => {
const { modalVisible, setModalVisible, userInfo } = props;
@ -32,7 +32,17 @@ const SettingModal = (props: { modalVisible: boolean, setModalVisible: (visible:
};
// 通知消息权限开关
const [notificationsEnabled, setNotificationsEnabled] = useState(false);
const toggleNotifications = () => setNotificationsEnabled(previous => !previous);
const toggleNotifications = () => {
if (notificationsEnabled) {
// 引导去设置关闭权限
openAppSettings()
} else {
console.log('请求通知权限');
requestNotificationPermission().then((res) => {
setNotificationsEnabled(res as boolean);
})
}
};
// 相册权限
const [albumEnabled, setAlbumEnabled] = useState(false);
@ -126,7 +136,6 @@ const SettingModal = (props: { modalVisible: boolean, setModalVisible: (visible:
};
// 退出登录
const handleLogout = () => {
fetchApi("/iam/logout", {
method: "POST",
@ -154,6 +163,11 @@ const SettingModal = (props: { modalVisible: boolean, setModalVisible: (visible:
getPermissions().then((res) => {
setAlbumEnabled(res);
})
// 通知权限
checkNotificationPermission().then((res) => {
console.log('通知权限:', res);
setNotificationsEnabled(res);
})
}
}, [modalVisible])

View File

@ -1,8 +1,21 @@
// 地理位置逆编码
import * as ImagePicker from 'expo-image-picker';
import * as Location from 'expo-location';
import * as Notifications from 'expo-notifications';
import * as SecureStore from 'expo-secure-store';
import { Alert, Linking, Platform } from 'react-native';
// 配置通知处理器
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false,
shouldShowBanner: true,
shouldShowList: true,
}),
});
export const reverseGeocode = async (latitude: number, longitude: number) => {
try {
const addressResults = await Location.reverseGeocodeAsync({ latitude, longitude });
@ -192,4 +205,98 @@ export const requestMediaLibraryPermission = async (showAlert: boolean = true):
}
return false;
}
};
// 检查通知权限
export const checkNotificationPermission = async () => {
const { status } = await Notifications.getPermissionsAsync();
console.log('当前通知权限状态:', status);
return status === 'granted';
};
// 请求通知权限
export const requestNotificationPermission = async () => {
try {
// 1. 先检查当前权限状态
const { status, canAskAgain } = await Notifications.getPermissionsAsync();
console.log('当前通知权限状态:', { status, canAskAgain });
// 2. 如果已经有权限,直接返回
if (status === 'granted') {
return true;
}
// 3. 如果用户之前选择了"拒绝且不再询问"
if (status === 'denied' && !canAskAgain) {
// 显示提示,引导用户去设置
const openSettings = await new Promise(resolve => {
Alert.alert(
'需要通知权限',
'您之前拒绝了通知权限。要使用此功能,请在设置中启用通知权限。',
[
{
text: '取消',
style: 'cancel',
onPress: () => resolve(false)
},
{
text: '去设置',
onPress: () => resolve(true)
}
]
);
});
if (openSettings) {
// 打开应用设置
await Linking.openSettings();
}
return false;
}
// 4. 如果是第一次请求或可以再次询问,则请求权限
console.log('请求通知权限...');
const { status: newStatus } = await Notifications.requestPermissionsAsync();
console.log('新通知权限状态:', newStatus);
if (newStatus !== 'granted') {
Alert.alert('需要通知权限', '请允许通知以使用此功能');
return false;
}
return true;
} catch (error) {
console.error('请求通知权限时出错:', error);
Alert.alert('错误', '请求通知权限时出错');
return false;
}
};
// 发送本地通知的辅助函数
export const sendLocalNotification = async (title: string, body: string, data: Record<string, any> = {}) => {
try {
const hasPermission = await checkNotificationPermission();
if (!hasPermission) {
const granted = await requestNotificationPermission();
if (!granted) {
return false;
}
}
await Notifications.scheduleNotificationAsync({
content: {
title,
body,
data,
priority: 'high',
},
trigger: null, // 立即触发
});
return true;
} catch (error) {
console.error('发送通知时出错:', error);
return false;
}
};