feat(notification): add foreground notification vibration feedback and Android channel setup
- Add vibration feedback when app is in foreground and notification arrives - Configure Android notification channel using expo-notifications - Integrate notification handler to respect user push/sound preferences - Add platform-specific local notification handling for Android
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
import { Platform, NativeModules } from 'react-native';
|
import { Platform, NativeModules, AppState } from 'react-native';
|
||||||
import { pushService } from './pushService';
|
import { pushService } from './pushService';
|
||||||
import * as Notifications from 'expo-notifications';
|
import * as Notifications from 'expo-notifications';
|
||||||
|
import { getNotificationPreferencesSync } from './notificationPreferences';
|
||||||
|
import { vibrateOnMessage } from '../platform/messageVibrationService';
|
||||||
|
|
||||||
type JPushNotificationMessage = {
|
type JPushNotificationMessage = {
|
||||||
messageID: string;
|
messageID: string;
|
||||||
@@ -68,6 +70,12 @@ class JPushService {
|
|||||||
|
|
||||||
JPush!.addNotificationListener((result: any) => {
|
JPush!.addNotificationListener((result: any) => {
|
||||||
console.log('[JPush] notification:', result);
|
console.log('[JPush] notification:', result);
|
||||||
|
if (result.notificationEventType === 'notificationArrived') {
|
||||||
|
const prefs = getNotificationPreferencesSync();
|
||||||
|
if (prefs.vibrationEnabled && AppState.currentState === 'active') {
|
||||||
|
vibrateOnMessage('notification').catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
if (this.notificationCallback) {
|
if (this.notificationCallback) {
|
||||||
this.notificationCallback({
|
this.notificationCallback({
|
||||||
messageID: result.messageID || '',
|
messageID: result.messageID || '',
|
||||||
@@ -111,6 +119,10 @@ class JPushService {
|
|||||||
production: !__DEV__,
|
production: !__DEV__,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (Platform.OS === 'android') {
|
||||||
|
this._setupAndroidNotificationChannel();
|
||||||
|
}
|
||||||
|
|
||||||
console.log('[JPush] init called, waiting for registration...');
|
console.log('[JPush] init called, waiting for registration...');
|
||||||
|
|
||||||
// 轮询获取 RegistrationID,间隔逐渐增大
|
// 轮询获取 RegistrationID,间隔逐渐增大
|
||||||
@@ -136,11 +148,37 @@ class JPushService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _onConnected(): void {
|
private _onConnected(): void {
|
||||||
if (this.registrationID) return; // Already have registrationID, skip
|
if (this.registrationID) return;
|
||||||
console.log('[JPush] connected, fetching registrationID...');
|
console.log('[JPush] connected, fetching registrationID...');
|
||||||
this._fetchRegistrationID();
|
this._fetchRegistrationID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _setupAndroidNotificationChannel(): void {
|
||||||
|
if (!JPush) return;
|
||||||
|
try {
|
||||||
|
(JPush as any).setChannelAndSound({
|
||||||
|
channel: '推送通知',
|
||||||
|
channelId: 'jpush_notification',
|
||||||
|
});
|
||||||
|
console.log('[JPush] Android notification channel configured');
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[JPush] setChannelAndSound failed:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Notifications.setNotificationChannelAsync('jpush_notification', {
|
||||||
|
name: '推送通知',
|
||||||
|
importance: Notifications.AndroidImportance.HIGH,
|
||||||
|
vibrationPattern: [0, 250, 250, 250],
|
||||||
|
sound: 'default',
|
||||||
|
enableLights: true,
|
||||||
|
showBadge: true,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[JPush] expo-notifications channel setup failed:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _fetchRegistrationID(): void {
|
private _fetchRegistrationID(): void {
|
||||||
if (!JPush) return;
|
if (!JPush) return;
|
||||||
JPush.getRegistrationID((result: any) => {
|
JPush.getRegistrationID((result: any) => {
|
||||||
@@ -282,7 +320,15 @@ class JPushService {
|
|||||||
}): void {
|
}): void {
|
||||||
if (!JPush || !this.isInitialized) return;
|
if (!JPush || !this.isInitialized) return;
|
||||||
try {
|
try {
|
||||||
JPush.addLocalNotification(params);
|
if (Platform.OS === 'android') {
|
||||||
|
const localParams = {
|
||||||
|
...params,
|
||||||
|
priority: 1,
|
||||||
|
};
|
||||||
|
JPush.addLocalNotification(localParams);
|
||||||
|
} else {
|
||||||
|
JPush.addLocalNotification(params);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[JPush] addLocalNotification failed:', error);
|
console.error('[JPush] addLocalNotification failed:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
|
import { Platform } from 'react-native';
|
||||||
|
import * as Notifications from 'expo-notifications';
|
||||||
import { setVibrationEnabled } from '../platform/messageVibrationService';
|
import { setVibrationEnabled } from '../platform/messageVibrationService';
|
||||||
|
|
||||||
export const NOTIFICATION_PREF_KEYS = {
|
export const NOTIFICATION_PREF_KEYS = {
|
||||||
@@ -70,6 +72,18 @@ export async function setSoundPreference(enabled: boolean): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function registerNotificationPresentationHandler(): void {
|
export function registerNotificationPresentationHandler(): void {
|
||||||
// JPush handles notification presentation natively.
|
if (Platform.OS === 'web') return;
|
||||||
// Preferences are read from getNotificationPreferencesSync() when needed.
|
|
||||||
|
Notifications.setNotificationHandler({
|
||||||
|
handleNotification: async () => {
|
||||||
|
const prefs = getNotificationPreferencesSync();
|
||||||
|
return {
|
||||||
|
shouldShowAlert: prefs.pushEnabled,
|
||||||
|
shouldPlaySound: prefs.soundEnabled,
|
||||||
|
shouldSetBadge: true,
|
||||||
|
shouldShowBanner: prefs.pushEnabled,
|
||||||
|
shouldShowList: prefs.pushEnabled,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,6 +191,18 @@ class SystemNotificationService {
|
|||||||
vibrateOnMessage('notification').catch(() => {});
|
vibrateOnMessage('notification').catch(() => {});
|
||||||
await this.showWSNotification(notifMsg);
|
await this.showWSNotification(notifMsg);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// App is in foreground: trigger vibration feedback
|
||||||
|
if ('segments' in message) {
|
||||||
|
const chatMsg = message as WSChatMessage;
|
||||||
|
const conversationId = normalizeConversationId(chatMsg.conversation_id);
|
||||||
|
const isNotificationMuted = useMessageStore.getState().isNotificationMuted(conversationId);
|
||||||
|
if (!isNotificationMuted) {
|
||||||
|
vibrateOnMessage('chat').catch(() => {});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vibrateOnMessage('notification').catch(() => {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user