diff --git a/apps/mobile-shell/scripts/check-config.mjs b/apps/mobile-shell/scripts/check-config.mjs index f6831b93b..7787b1905 100644 --- a/apps/mobile-shell/scripts/check-config.mjs +++ b/apps/mobile-shell/scripts/check-config.mjs @@ -1952,6 +1952,7 @@ for (const notificationSnippet of [ 'normalizeHostBridgeLocalNotification(request.payload)', "invalidRequest('title is required')", "message: 'notification permission unavailable'", + "message: 'notification delivery unavailable'", 'showMobileLocalNotification(notification)', 'ok(request, await showMobileLocalNotification(notification))', 'HOST_BRIDGE_LOCAL_NOTIFICATION_DELIVERED_TO_SYSTEM_RESULT', @@ -1964,7 +1965,10 @@ for (const notificationSnippet of [ for (const snippet of [ 'rejects delivery when current permission lookup is unavailable', 'rejects delivery when permission request is unavailable', + 'maps Android channel setup failures to stable delivery errors', + 'maps native notification schedule failures to stable delivery errors', "message: 'notification permission unavailable'", + "message: 'notification delivery unavailable'", 'expect(Notifications.setNotificationChannelAsync).not.toHaveBeenCalled()', 'expect(Notifications.scheduleNotificationAsync).not.toHaveBeenCalled()', ]) { diff --git a/apps/mobile-shell/src/host-bridge/notifications.test.ts b/apps/mobile-shell/src/host-bridge/notifications.test.ts index 7c59dca6f..f004778ce 100644 --- a/apps/mobile-shell/src/host-bridge/notifications.test.ts +++ b/apps/mobile-shell/src/host-bridge/notifications.test.ts @@ -226,6 +226,35 @@ describe('mobile local notification helpers', () => { }); }); + test('maps Android channel setup failures to stable delivery errors', async () => { + setPlatformOS('android'); + vi.mocked(Notifications.setNotificationChannelAsync).mockRejectedValueOnce( + new Error('native channel failed'), + ); + + await expect( + showMobileLocalNotification({ title: '生成完成' }), + ).rejects.toMatchObject({ + code: 'host_error', + message: 'notification delivery unavailable', + }); + + expect(Notifications.scheduleNotificationAsync).not.toHaveBeenCalled(); + }); + + test('maps native notification schedule failures to stable delivery errors', async () => { + vi.mocked(Notifications.scheduleNotificationAsync).mockRejectedValueOnce( + new Error('native schedule failed'), + ); + + await expect( + showMobileLocalNotification({ title: '生成完成' }), + ).rejects.toMatchObject({ + code: 'host_error', + message: 'notification delivery unavailable', + }); + }); + test('normalizes HostBridge payload and wraps structured delivery result', async () => { const response = await showMobileHostBridgeLocalNotification( request({ diff --git a/apps/mobile-shell/src/host-bridge/notifications.ts b/apps/mobile-shell/src/host-bridge/notifications.ts index 6b6ed7526..4a0b1913e 100644 --- a/apps/mobile-shell/src/host-bridge/notifications.ts +++ b/apps/mobile-shell/src/host-bridge/notifications.ts @@ -74,23 +74,30 @@ export async function showMobileLocalNotification( notification: LocalNotificationPayload, ) { await ensureNotificationPermission(); - if (Platform.OS === 'android') { - await Notifications.setNotificationChannelAsync( - HOST_BRIDGE_MOBILE_LOCAL_NOTIFICATION_CHANNEL_ID, - { - name: 'Genarrative', - importance: Notifications.AndroidImportance.DEFAULT, - }, - ); - } + try { + if (Platform.OS === 'android') { + await Notifications.setNotificationChannelAsync( + HOST_BRIDGE_MOBILE_LOCAL_NOTIFICATION_CHANNEL_ID, + { + name: 'Genarrative', + importance: Notifications.AndroidImportance.DEFAULT, + }, + ); + } - await Notifications.scheduleNotificationAsync({ - content: notification, - trigger: - Platform.OS === 'android' - ? { channelId: HOST_BRIDGE_MOBILE_LOCAL_NOTIFICATION_CHANNEL_ID } - : null, - }); + await Notifications.scheduleNotificationAsync({ + content: notification, + trigger: + Platform.OS === 'android' + ? { channelId: HOST_BRIDGE_MOBILE_LOCAL_NOTIFICATION_CHANNEL_ID } + : null, + }); + } catch { + throw { + code: 'host_error', + message: 'notification delivery unavailable', + } satisfies HostBridgeError; + } return HOST_BRIDGE_LOCAL_NOTIFICATION_DELIVERED_TO_SYSTEM_RESULT; }