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 * as Notifications from 'expo-notifications';
|
||||
import { getNotificationPreferencesSync } from './notificationPreferences';
|
||||
import { vibrateOnMessage } from '../platform/messageVibrationService';
|
||||
|
||||
type JPushNotificationMessage = {
|
||||
messageID: string;
|
||||
@@ -68,6 +70,12 @@ class JPushService {
|
||||
|
||||
JPush!.addNotificationListener((result: any) => {
|
||||
console.log('[JPush] notification:', result);
|
||||
if (result.notificationEventType === 'notificationArrived') {
|
||||
const prefs = getNotificationPreferencesSync();
|
||||
if (prefs.vibrationEnabled && AppState.currentState === 'active') {
|
||||
vibrateOnMessage('notification').catch(() => {});
|
||||
}
|
||||
}
|
||||
if (this.notificationCallback) {
|
||||
this.notificationCallback({
|
||||
messageID: result.messageID || '',
|
||||
@@ -111,6 +119,10 @@ class JPushService {
|
||||
production: !__DEV__,
|
||||
});
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
this._setupAndroidNotificationChannel();
|
||||
}
|
||||
|
||||
console.log('[JPush] init called, waiting for registration...');
|
||||
|
||||
// 轮询获取 RegistrationID,间隔逐渐增大
|
||||
@@ -136,11 +148,37 @@ class JPushService {
|
||||
}
|
||||
|
||||
private _onConnected(): void {
|
||||
if (this.registrationID) return; // Already have registrationID, skip
|
||||
if (this.registrationID) return;
|
||||
console.log('[JPush] connected, fetching registrationID...');
|
||||
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 {
|
||||
if (!JPush) return;
|
||||
JPush.getRegistrationID((result: any) => {
|
||||
@@ -282,7 +320,15 @@ class JPushService {
|
||||
}): void {
|
||||
if (!JPush || !this.isInitialized) return;
|
||||
try {
|
||||
JPush.addLocalNotification(params);
|
||||
if (Platform.OS === 'android') {
|
||||
const localParams = {
|
||||
...params,
|
||||
priority: 1,
|
||||
};
|
||||
JPush.addLocalNotification(localParams);
|
||||
} else {
|
||||
JPush.addLocalNotification(params);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[JPush] addLocalNotification failed:', error);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
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';
|
||||
|
||||
export const NOTIFICATION_PREF_KEYS = {
|
||||
@@ -70,6 +72,18 @@ export async function setSoundPreference(enabled: boolean): Promise<void> {
|
||||
}
|
||||
|
||||
export function registerNotificationPresentationHandler(): void {
|
||||
// JPush handles notification presentation natively.
|
||||
// Preferences are read from getNotificationPreferencesSync() when needed.
|
||||
if (Platform.OS === 'web') return;
|
||||
|
||||
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(() => {});
|
||||
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