接入原生壳生命周期事件
新增 app.lifecycle HostBridge 能力与 H5 订阅入口 Expo 壳通过 React Native AppState 注入真实前后台状态 Tauri 壳通过主窗口 focus 和 blur 注入真实激活状态 更新壳能力漂移检查、测试和架构文档
This commit is contained in:
@@ -29,6 +29,7 @@ import {
|
||||
setHostAppBadgeCount,
|
||||
setHostAppTitle,
|
||||
setHostShareTarget,
|
||||
subscribeHostAppLifecycle,
|
||||
subscribeHostRuntimeChange,
|
||||
writeHostClipboardText,
|
||||
} from './hostBridge';
|
||||
@@ -140,6 +141,82 @@ describe('hostBridge', () => {
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test('订阅原生 App 生命周期事件并归一化 payload', () => {
|
||||
const listener = vi.fn();
|
||||
window.history.replaceState(
|
||||
null,
|
||||
'',
|
||||
nativeAppPath(['app.lifecycle']),
|
||||
);
|
||||
window.ReactNativeWebView = {
|
||||
postMessage: vi.fn(),
|
||||
};
|
||||
|
||||
const unsubscribe = subscribeHostAppLifecycle(listener);
|
||||
window.dispatchEvent(
|
||||
new MessageEvent('message', {
|
||||
data: JSON.stringify({
|
||||
bridge: 'GenarrativeHostBridge',
|
||||
version: 1,
|
||||
event: 'app.lifecycle',
|
||||
payload: {
|
||||
state: 'extension',
|
||||
focused: true,
|
||||
nativeState: 'extension',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
unsubscribe();
|
||||
window.dispatchEvent(
|
||||
new MessageEvent('message', {
|
||||
data: JSON.stringify({
|
||||
bridge: 'GenarrativeHostBridge',
|
||||
version: 1,
|
||||
event: 'app.lifecycle',
|
||||
payload: {
|
||||
state: 'active',
|
||||
focused: true,
|
||||
nativeState: 'active',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
expect(listener).toHaveBeenCalledWith({
|
||||
state: 'inactive',
|
||||
focused: true,
|
||||
nativeState: 'extension',
|
||||
});
|
||||
});
|
||||
|
||||
test('未声明生命周期能力时不订阅原生事件', () => {
|
||||
const listener = vi.fn();
|
||||
window.history.replaceState(null, '', nativeAppPath());
|
||||
window.ReactNativeWebView = {
|
||||
postMessage: vi.fn(),
|
||||
};
|
||||
|
||||
const unsubscribe = subscribeHostAppLifecycle(listener);
|
||||
window.dispatchEvent(
|
||||
new MessageEvent('message', {
|
||||
data: JSON.stringify({
|
||||
bridge: 'GenarrativeHostBridge',
|
||||
version: 1,
|
||||
event: 'app.lifecycle',
|
||||
payload: {
|
||||
state: 'active',
|
||||
focused: true,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
unsubscribe();
|
||||
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('从真实宿主 runtime 回读能力并通知订阅者', async () => {
|
||||
const listener = vi.fn();
|
||||
const unsubscribe = subscribeHostRuntimeChange(listener);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type {
|
||||
AppearanceColorSchemeResult,
|
||||
AppLifecycleEventPayload,
|
||||
FileExportImagePayload,
|
||||
FileExportImageResult,
|
||||
FileExportTextPayload,
|
||||
@@ -17,6 +18,7 @@ import {
|
||||
normalizeHostBridgeBadgeCount,
|
||||
normalizeHostBridgeColorScheme,
|
||||
normalizeHostBridgeExternalUrl,
|
||||
normalizeHostBridgeLifecycleState,
|
||||
} from '../../../packages/shared/src/contracts/hostBridge';
|
||||
import type {
|
||||
WechatMiniProgramPayParams,
|
||||
@@ -25,6 +27,7 @@ import type {
|
||||
import {
|
||||
canUseNativeAppHostBridge,
|
||||
requestNativeAppHostBridge,
|
||||
subscribeNativeAppHostBridgeEvent,
|
||||
} from './nativeAppHostBridge';
|
||||
|
||||
const WECHAT_JS_SDK_URL = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js';
|
||||
@@ -99,6 +102,8 @@ export type HostExternalUrlRequest = OpenExternalUrlPayload;
|
||||
|
||||
export type HostAppearanceColorSchemeSnapshot = AppearanceColorSchemeResult;
|
||||
|
||||
export type HostAppLifecycleSnapshot = AppLifecycleEventPayload;
|
||||
|
||||
const HOST_RUNTIME_REFRESH_TIMEOUT_MS = 3000;
|
||||
|
||||
let cachedNativeHostRuntime: HostBridgeRuntimeResult | null = null;
|
||||
@@ -786,3 +791,27 @@ export async function getHostAppearanceColorScheme() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function subscribeHostAppLifecycle(
|
||||
listener: (payload: HostAppLifecycleSnapshot) => void,
|
||||
) {
|
||||
if (!canUseNativeHostCapability('app.lifecycle')) {
|
||||
return () => undefined;
|
||||
}
|
||||
|
||||
return subscribeNativeAppHostBridgeEvent<AppLifecycleEventPayload>(
|
||||
'app.lifecycle',
|
||||
(payload) => {
|
||||
const state = normalizeHostBridgeLifecycleState(payload?.state);
|
||||
listener({
|
||||
state,
|
||||
focused: typeof payload?.focused === 'boolean'
|
||||
? payload.focused
|
||||
: state === 'active',
|
||||
...(typeof payload?.nativeState === 'string'
|
||||
? { nativeState: payload.nativeState }
|
||||
: {}),
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user