feat: 消息推送

This commit is contained in:
jinyaqiu 2025-07-17 11:36:42 +08:00
parent 153838aec0
commit 6c270302f5

View File

@ -1,14 +1,163 @@
import { Tabs } from 'expo-router';
import React from 'react';
import { Platform } from 'react-native';
import { HapticTab } from '@/components/HapticTab'; import { HapticTab } from '@/components/HapticTab';
import TabBarBackground from '@/components/ui/TabBarBackground'; import TabBarBackground from '@/components/ui/TabBarBackground';
import { Colors } from '@/constants/Colors'; import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme'; import { useColorScheme } from '@/hooks/useColorScheme';
import { fetchApi } from '@/lib/server-api-util';
import * as Notifications from 'expo-notifications';
import { Tabs } from 'expo-router';
import * as SecureStore from 'expo-secure-store';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Platform } from 'react-native';
interface PollingData {
title: string;
id: string;
content: string;
extra: any;
}
export default function TabLayout() { export default function TabLayout() {
const colorScheme = useColorScheme(); const colorScheme = useColorScheme();
const [pollingData, setPollingData] = useState<PollingData[]>([]);
const pollingInterval = useRef<NodeJS.Timeout | number>(null);
const tokenInterval = useRef<NodeJS.Timeout | number>(null);
const isMounted = useRef(true);
const [token, setToken] = useState('');
const sendNotification = async (item: PollingData) => {
// 请求通知权限
const { status } = await Notifications.requestPermissionsAsync();
if (status !== 'granted') {
alert('请先允许通知权限');
return;
}
// 调度本地通知
await Notifications.scheduleNotificationAsync({
content: {
title: item.title,
body: item.content,
data: { screen: 'ask', extra: item.extra, id: item.id },
priority: 'high', // 关键:设置 high 或 max
},
trigger: {
seconds: 2, // 延迟2秒显示
type: Notifications.SchedulableTriggerInputTypes.TIME_INTERVAL // 添加 type 字段
}, // 延迟2秒显示
});
};
// 监听通知点击事件
useEffect(() => {
const notificationListener = Notifications.addNotificationResponseReceivedListener(response => {
const data = response.notification.request.content.data;
console.log('通知被点击,数据:', data);
pollingData?.filter((item) => item.id !== data.id);
});
// 清理监听器
return () => {
Notifications.removeNotificationSubscription(notificationListener);
};
}, []);
// 轮询获取推送消息
const startPolling = useCallback(async (interval: number = 5000) => {
// 设置轮询
pollingInterval.current = setInterval(async () => {
if (isMounted.current) {
await getMessageData();
}
}, interval);
// 返回清理函数
return () => {
if (pollingInterval.current) {
clearInterval(pollingInterval.current);
}
};
}, []);
// 获取推送消息
const getMessageData = async () => {
try {
const response = await fetchApi<PollingData[]>("/notice/push/message", {
method: "POST"
});
setPollingData((prev) => ([...prev, ...response]));
} catch (error) {
console.error('获取轮询数据时出错:', error);
}
};
// 获取认证token
const getAuthToken = async (): Promise<string> => {
let tokenValue = '';
if (Platform.OS === 'web') {
tokenValue = localStorage.getItem('token') || '';
} else {
tokenValue = (await SecureStore.getItemAsync('token')) || '';
}
setToken(tokenValue); // 只在获取到新token时更新状态
return tokenValue;
};
useEffect(() => {
const checkAuthStatus = async () => {
try {
if (token) {
// 启动轮询
startPolling(5000);
}
} catch (error) {
console.error('获取推送消息出错:', error);
}
};
checkAuthStatus();
return () => {
// 清理函数
if (pollingInterval.current) {
clearInterval(pollingInterval.current);
}
isMounted.current = false;
};
}, [token]);
// 本地推送
useEffect(() => {
pollingData?.map((item) => {
sendNotification(item)
})
}, [pollingData])
// 轮询获取token
useEffect(() => {
// 如果已经有token直接返回
if (token) {
if (tokenInterval.current) {
clearInterval(tokenInterval.current);
}
return;
}
if (!tokenInterval.current) return;
// 设置轮询
tokenInterval.current = setInterval(async () => {
if (isMounted.current) {
const currentToken = await getAuthToken();
// 如果获取到token清除定时器
if (currentToken && tokenInterval.current) {
clearInterval(tokenInterval.current);
}
}
}, 5000);
// 返回清理函数
return () => {
if (tokenInterval.current) {
clearInterval(tokenInterval.current);
}
};
}, [token]); // 添加token作为依赖
return ( return (
<Tabs <Tabs