diff --git a/apps/mobile-shell/scripts/check-config.mjs b/apps/mobile-shell/scripts/check-config.mjs index 21ffc7032..49ce6e6c7 100644 --- a/apps/mobile-shell/scripts/check-config.mjs +++ b/apps/mobile-shell/scripts/check-config.mjs @@ -1896,7 +1896,8 @@ if (mobileCapabilitySet.has('app.setBadgeCount')) { throw new Error('Android mobile shell base capabilities must not include app.setBadgeCount'); } if ( - !dispatchSource.includes('setMobileAppBadgeCount(request.payload)') || + !dispatchSource.includes('setMobileAppBadgeCount(request)') || + dispatchSource.includes('ok(request, setMobileAppBadgeCount') || dispatchSource.includes('PushNotificationIOS') || dispatchSource.includes('normalizeHostBridgeBadgeCount') || dispatchSource.includes('HOST_BRIDGE_BADGE_COUNT_MAX') @@ -1905,8 +1906,10 @@ if ( } for (const snippet of [ 'HOST_BRIDGE_BADGE_COUNT_MAX', + 'request: HostBridgeRequest', 'normalizeHostBridgeBadgeCount', 'PushNotificationIOS.setApplicationIconBadgeNumber(count)', + 'ok(request, true)', 'app badge count is only supported on iOS mobile shell', ]) { if (!badgeSource.includes(snippet)) { diff --git a/apps/mobile-shell/src/host-bridge/badge.ts b/apps/mobile-shell/src/host-bridge/badge.ts index 4f5965471..b34fbf23c 100644 --- a/apps/mobile-shell/src/host-bridge/badge.ts +++ b/apps/mobile-shell/src/host-bridge/badge.ts @@ -3,12 +3,13 @@ import { Platform, PushNotificationIOS } from 'react-native'; import { HOST_BRIDGE_BADGE_COUNT_MAX, type HostBridgeError, + type HostBridgeRequest, normalizeHostBridgeBadgeCount, type SetBadgeCountPayload, } from '../../../../packages/shared/src/contracts/hostBridge'; -import { invalidRequest } from './protocol'; +import { invalidRequest, ok } from './protocol'; -export function setMobileAppBadgeCount(payload: unknown) { +export function setMobileAppBadgeCount(request: HostBridgeRequest) { if (Platform.OS !== 'ios') { throw { code: 'unsupported_capability', @@ -17,7 +18,7 @@ export function setMobileAppBadgeCount(payload: unknown) { } const count = normalizeHostBridgeBadgeCount( - (payload as SetBadgeCountPayload | undefined)?.count, + (request.payload as SetBadgeCountPayload | undefined)?.count, ); if (count === null) { throw invalidRequest( @@ -26,5 +27,5 @@ export function setMobileAppBadgeCount(payload: unknown) { } PushNotificationIOS.setApplicationIconBadgeNumber(count); - return true; + return ok(request, true); } diff --git a/apps/mobile-shell/src/host-bridge/dispatch.ts b/apps/mobile-shell/src/host-bridge/dispatch.ts index abaf62d38..a38df5248 100644 --- a/apps/mobile-shell/src/host-bridge/dispatch.ts +++ b/apps/mobile-shell/src/host-bridge/dispatch.ts @@ -89,7 +89,7 @@ export async function dispatchMobileHostBridgeRequest( await showMobileHostBridgeLocalNotification(request.payload), ); case 'app.setBadgeCount': - return ok(request, setMobileAppBadgeCount(request.payload)); + return setMobileAppBadgeCount(request); case 'share.open': return ok(request, await openShare(request.payload)); case 'share.setTarget': diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index f8387ce1b..c8b112aeb 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -48,7 +48,7 @@ - 2026-06-18 分享卡图片导出:新增 `file.exportImage` HostBridge capability,H5 分享卡下载在 native app 中优先把 canvas 生成的 base64 图片交给宿主导出;Expo 壳写缓存图片后交给系统分享 / 保存面板,Tauri 壳通过系统保存对话框写入图片字节。该能力只接受 `image/png` / `image/jpeg` / `image/webp`、单次 5 MiB 内图片数据,成功只返回文件名和字节数,不暴露本机绝对路径;宿主未声明时保留浏览器下载。 - 2026-06-18 应用角标能力:新增 `app.setBadgeCount` HostBridge capability,H5 只传 `0` 到共享契约 `HOST_BRIDGE_BADGE_COUNT_MAX` 之间的整数并在宿主未声明时静默 fallback;Expo 壳只在 iOS 声明并通过 React Native `PushNotificationIOS` 设置应用图标角标,Android 不声明、不伪造成功;Tauri 壳通过主窗口 `set_badge_count` 设置任务栏角标,底层平台不支持时返回真实错误。 - 2026-06-18 草稿生成未读角标:平台壳层把“可见作品架里未读的草稿生成完成更新”同步到 `app.setBadgeCount`;同一草稿的 work/profile/session 等多个恢复 ID 只计 1,已读、失败、生成中和不可见草稿不计入。该角标只消费已有 HostBridge 能力,宿主不支持或设置失败不影响 H5 红点、作品架或后端状态。 -- 2026-06-19 原生壳角标边界:Expo `app.setBadgeCount` 的 iOS 平台判定、共享上限校验和 `PushNotificationIOS.setApplicationIconBadgeNumber(...)` 调用统一收口在 `apps/mobile-shell/src/host-bridge/badge.ts`;Tauri `app.setBadgeCount` 的 payload 校验、清除语义和主窗口 `set_badge_count` 调用统一收口在 `apps/desktop-shell/src-tauri/src/host_bridge/badge.rs`。两端 `dispatch` 只负责委托对应 badge 模块和映射响应,配置检查会拒绝分发层直接导入角标底层 API 或重声明数量边界。 +- 2026-06-19 原生壳角标边界:Expo `app.setBadgeCount` 的 iOS 平台判定、共享上限校验、`PushNotificationIOS.setApplicationIconBadgeNumber(...)` 调用和 HostBridge 成功 / 失败响应映射统一收口在 `apps/mobile-shell/src/host-bridge/badge.ts`;Tauri `app.setBadgeCount` 的 payload 校验、清除语义、主窗口 `set_badge_count` 调用和 HostBridge 响应映射统一收口在 `apps/desktop-shell/src-tauri/src/host_bridge/badge.rs`。两端 `dispatch` 只负责委托对应 badge 模块,配置检查会拒绝分发层直接导入角标底层 API、重声明数量边界或包装角标成功响应。 - 2026-06-18 宿主外观只读查询:新增 `appearance.getColorScheme` HostBridge capability,Expo 壳通过 React Native `Appearance.getColorScheme()` 读取系统配色,Tauri 壳通过主窗口 `theme()` 读取窗口主题;该能力只返回 `light` / `dark` / `unknown`,不设置 H5 主题、不覆盖系统主题,也不作为强制 UI 样式入口。 - 2026-06-19 原生壳外观查询边界:Expo `appearance.getColorScheme` 的系统配色读取和 HostBridge 配色归一统一收口在 `apps/mobile-shell/src/host-bridge/appearance.ts`;Tauri `appearance.getColorScheme` 的主窗口 `theme()` 读取和 `light / dark / unknown` 映射统一收口在 `apps/desktop-shell/src-tauri/src/host_bridge/appearance.rs`。两端 `dispatch` 只负责委托对应 appearance 模块和映射响应,配置检查会拒绝分发层直接读取系统配色或窗口主题。 - 2026-06-18 原生壳生命周期事件:新增 `app.lifecycle` HostBridge capability,Expo 壳通过 React Native `AppState` 派发 `active` / `inactive` / `background`,Tauri 壳通过主窗口 focus / blur、托盘隐藏 / 恢复和页面加载重放派发统一状态;桌面隐藏到托盘或最小化都归一为 `background`,`hidden`、`minimized`、`focused`、`blurred` 只进入 `nativeState` 便于排障,不扩展共享 `state`。两端都声明 `host.events` 表示事件通过 HostBridge message 注入,但不把它作为 request method,也不开放 Tauri event 插件或 React Native 私有事件 API。H5 只通过 `subscribeHostAppLifecycle()` 订阅统一状态,后续游戏循环、音频和轮询暂停 / 恢复不得直接依赖 Expo / Tauri 平台细节。