Files
Genarrative/apps/mobile-shell/src/host-bridge/notifications.ts
T
kdletters 05d278f8f7 收口移动壳能力诊断日志
移动壳 HostBridge 能力失败日志只记录稳定阶段标签

更新 badge notification haptics network share navigation 失败测试断言

扩展移动壳配置门禁禁止能力日志打印原生 error 对象

补充 Expo 与 Tauri 宿主壳方案中的移动诊断日志边界
2026-06-20 23:09:49 +08:00

121 lines
3.4 KiB
TypeScript

import * as Notifications from 'expo-notifications';
import { Platform } from 'react-native';
import {
HOST_BRIDGE_LOCAL_NOTIFICATION_DELIVERED_TO_SYSTEM_RESULT,
HOST_BRIDGE_MOBILE_LOCAL_NOTIFICATION_CHANNEL_ID,
type HostBridgeError,
type HostBridgeRequest,
type LocalNotificationPayload,
normalizeHostBridgeLocalNotification,
} from '../../../../packages/shared/src/contracts/hostBridge';
import { invalidRequest, ok } from './protocol';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowBanner: true,
shouldShowList: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});
function logMobileNotificationFailure(label: string, _error: unknown) {
console.warn(`mobile notification failed for ${label}`);
}
function hasNotificationPermission(
permission: Awaited<ReturnType<typeof Notifications.getPermissionsAsync>>,
) {
return (
permission.granted ||
permission.ios?.status === Notifications.IosAuthorizationStatus.PROVISIONAL
);
}
async function ensureNotificationPermission() {
let currentPermission: Awaited<
ReturnType<typeof Notifications.getPermissionsAsync>
>;
try {
currentPermission = await Notifications.getPermissionsAsync();
} catch (error) {
logMobileNotificationFailure('permission.current', error);
throw {
code: 'host_error',
message: 'notification permission unavailable',
} satisfies HostBridgeError;
}
if (hasNotificationPermission(currentPermission)) {
return;
}
let requestedPermission: Awaited<
ReturnType<typeof Notifications.requestPermissionsAsync>
>;
try {
requestedPermission = await Notifications.requestPermissionsAsync({
ios: {
allowAlert: true,
allowBadge: false,
allowSound: false,
},
});
} catch (error) {
logMobileNotificationFailure('permission.request', error);
throw {
code: 'host_error',
message: 'notification permission unavailable',
} satisfies HostBridgeError;
}
if (!hasNotificationPermission(requestedPermission)) {
throw {
code: 'host_error',
message: 'notification permission denied',
} satisfies HostBridgeError;
}
}
export async function showMobileLocalNotification(
notification: LocalNotificationPayload,
) {
await ensureNotificationPermission();
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,
});
} catch (error) {
logMobileNotificationFailure('delivery.schedule', error);
throw {
code: 'host_error',
message: 'notification delivery unavailable',
} satisfies HostBridgeError;
}
return HOST_BRIDGE_LOCAL_NOTIFICATION_DELIVERED_TO_SYSTEM_RESULT;
}
export async function showMobileHostBridgeLocalNotification(
request: HostBridgeRequest,
) {
const notification = normalizeHostBridgeLocalNotification(request.payload);
if (!notification) {
throw invalidRequest('title is required');
}
return ok(request, await showMobileLocalNotification(notification));
}