接入原生壳本地通知能力
新增 notification.showLocal HostBridge 契约和 H5 facade 移动端通过 expo-notifications 发送即时本地通知 桌面端通过 Tauri notification 插件发送系统通知 更新壳能力检查、测试、方案文档和共享决策记录
This commit is contained in:
@@ -31,6 +31,7 @@ import {
|
||||
setHostAppBadgeCount,
|
||||
setHostAppTitle,
|
||||
setHostShareTarget,
|
||||
showHostLocalNotification,
|
||||
subscribeHostAppLifecycle,
|
||||
subscribeHostImageDrop,
|
||||
subscribeHostNetworkStatusChange,
|
||||
@@ -623,6 +624,7 @@ describe('hostBridge', () => {
|
||||
'file.exportImage',
|
||||
'file.importImage',
|
||||
'file.imageDropped',
|
||||
'notification.showLocal',
|
||||
]),
|
||||
);
|
||||
window.__TAURI__ = {
|
||||
@@ -686,6 +688,12 @@ describe('hostBridge', () => {
|
||||
mimeType: 'image/png',
|
||||
bytes: 5,
|
||||
});
|
||||
await expect(
|
||||
showHostLocalNotification({
|
||||
title: ' 生成完成 ',
|
||||
body: ' 作品已准备好 可以试玩 ',
|
||||
}),
|
||||
).resolves.toBe(true);
|
||||
|
||||
expect(invoke).toHaveBeenCalledWith('host_bridge_request', {
|
||||
request: expect.objectContaining({
|
||||
@@ -784,6 +792,15 @@ describe('hostBridge', () => {
|
||||
timeoutMs: 30000,
|
||||
}),
|
||||
});
|
||||
expect(invoke).toHaveBeenCalledWith('host_bridge_request', {
|
||||
request: expect.objectContaining({
|
||||
method: 'notification.showLocal',
|
||||
payload: {
|
||||
title: '生成完成',
|
||||
body: '作品已准备好 可以试玩',
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
test('原生 App 宿主不支持能力时回退到 H5 路径', async () => {
|
||||
@@ -850,6 +867,11 @@ describe('hostBridge', () => {
|
||||
}),
|
||||
).resolves.toBe(false);
|
||||
await expect(importHostImageFile()).resolves.toBe(false);
|
||||
await expect(
|
||||
showHostLocalNotification({
|
||||
title: '生成完成',
|
||||
}),
|
||||
).resolves.toBe(false);
|
||||
});
|
||||
|
||||
test('普通浏览器不处理宿主文件导出', async () => {
|
||||
@@ -868,6 +890,33 @@ describe('hostBridge', () => {
|
||||
}),
|
||||
).resolves.toBe(false);
|
||||
await expect(importHostImageFile()).resolves.toBe(false);
|
||||
await expect(
|
||||
showHostLocalNotification({
|
||||
title: '生成完成',
|
||||
}),
|
||||
).resolves.toBe(false);
|
||||
});
|
||||
|
||||
test('原生 App 宿主拒绝非法本地通知 payload', async () => {
|
||||
const invoke = vi.fn();
|
||||
window.history.replaceState(
|
||||
null,
|
||||
'',
|
||||
nativeAppPath(['notification.showLocal']),
|
||||
);
|
||||
window.__TAURI__ = {
|
||||
core: {
|
||||
invoke: asTauriInvoke(invoke),
|
||||
},
|
||||
};
|
||||
|
||||
await expect(
|
||||
showHostLocalNotification({
|
||||
title: '生成\n完成',
|
||||
}),
|
||||
).resolves.toBe(false);
|
||||
|
||||
expect(invoke).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('原生 App 宿主通过 HostBridge 导出文本文件', async () => {
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
HostBridgeImageMimeType,
|
||||
HostBridgeMethod,
|
||||
HostBridgeRuntimeResult,
|
||||
LocalNotificationPayload,
|
||||
NetworkStatusResult,
|
||||
OpenExternalUrlPayload,
|
||||
SetBadgeCountPayload,
|
||||
@@ -23,6 +24,7 @@ import {
|
||||
normalizeHostBridgeConnectionType,
|
||||
normalizeHostBridgeExternalUrl,
|
||||
normalizeHostBridgeLifecycleState,
|
||||
normalizeHostBridgeLocalNotification,
|
||||
} from '../../../packages/shared/src/contracts/hostBridge';
|
||||
import type {
|
||||
WechatMiniProgramPayParams,
|
||||
@@ -96,6 +98,8 @@ export type HostClipboardWriteTextRequest = {
|
||||
|
||||
export type HostHapticsImpactRequest = HapticsImpactPayload;
|
||||
|
||||
export type HostLocalNotificationRequest = LocalNotificationPayload;
|
||||
|
||||
export type HostAppTitleRequest = {
|
||||
title: string;
|
||||
};
|
||||
@@ -818,6 +822,28 @@ export async function requestHostHapticsImpact(
|
||||
}
|
||||
}
|
||||
|
||||
export async function showHostLocalNotification(
|
||||
params: HostLocalNotificationRequest,
|
||||
) {
|
||||
if (!canUseNativeHostCapability('notification.showLocal')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const notification = normalizeHostBridgeLocalNotification(params);
|
||||
if (!notification) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return await requestNativeHostBoolean(
|
||||
'notification.showLocal',
|
||||
notification,
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function setHostAppTitle({ title }: HostAppTitleRequest) {
|
||||
const normalizedTitle = title.trim();
|
||||
if (!normalizedTitle || !canUseNativeHostCapability('app.setTitle')) {
|
||||
|
||||
Reference in New Issue
Block a user