diff --git a/backend/src/services/notification.service.js b/backend/src/services/notification.service.js index b418f70..c055961 100644 --- a/backend/src/services/notification.service.js +++ b/backend/src/services/notification.service.js @@ -21,6 +21,14 @@ export const sendPushNotification = async (recipientType, recipientId, { title, if (!user?.fcm_token) return false + // Mitra app ships a branded notification sound on its own channel + // (`halobestie_chat_v1`, declared in mitra_app/lib/core/notifications/ + // notification_service.dart). Customer app keeps the legacy + // `chat_messages` channel until/unless we ship a customer sound too. + const androidChannelId = recipientType === UserType.MITRA + ? 'halobestie_chat_v1' + : 'chat_messages' + try { await admin.messaging().send({ token: user.fcm_token, @@ -33,7 +41,7 @@ export const sendPushNotification = async (recipientType, recipientId, { title, }, android: { priority: 'high', - notification: { channelId: 'chat_messages' }, + notification: { channelId: androidChannelId }, }, apns: { payload: { diff --git a/mitra_app/android/app/src/main/res/raw/halobestie_notif.ogg b/mitra_app/android/app/src/main/res/raw/halobestie_notif.ogg new file mode 100644 index 0000000..c36af26 Binary files /dev/null and b/mitra_app/android/app/src/main/res/raw/halobestie_notif.ogg differ diff --git a/mitra_app/lib/core/notifications/notification_service.dart b/mitra_app/lib/core/notifications/notification_service.dart index d0640f0..72bf5de 100644 --- a/mitra_app/lib/core/notifications/notification_service.dart +++ b/mitra_app/lib/core/notifications/notification_service.dart @@ -12,11 +12,18 @@ class NotificationService { /// Set this from the app to bridge notifications → Riverpod state. static void Function(String sessionId)? onChatRequestTapped; + // Channel ID is bumped (`chat_messages` → `halobestie_chat_v1`) because + // Android binds notification sound at channel-creation time on API 26+. An + // existing `chat_messages` channel still routes through whatever sound was + // set when it was first created (system default), so we mint a fresh ID + // for the branded sound. Backend FCM payloads target the same ID — see + // backend/src/services/notification.service.js. static const _channel = AndroidNotificationChannel( - 'chat_messages', - 'Chat Messages', - description: 'Notifications for incoming chat messages', + 'halobestie_chat_v1', + 'Chat HaloBestie', + description: 'Notifications for incoming chat messages and extension requests', importance: Importance.high, + sound: RawResourceAndroidNotificationSound('halobestie_notif'), ); static Future initialize(GoRouter router) async { @@ -64,6 +71,9 @@ class NotificationService { channelDescription: _channel.description, importance: Importance.high, priority: Priority.high, + // API 26+ ignores this in favor of the channel's sound; included for + // the API 24/25 path where channels don't exist yet. + sound: const RawResourceAndroidNotificationSound('halobestie_notif'), ), iOS: const DarwinNotificationDetails( presentAlert: true, @@ -106,6 +116,7 @@ class NotificationService { priority: Priority.high, playSound: true, enableVibration: true, + sound: const RawResourceAndroidNotificationSound('halobestie_notif'), ), iOS: const DarwinNotificationDetails( presentAlert: true,