接入移动端原生扫码能力
新增 scanner.scanQrCode HostBridge 契约与二维码结果归一。 接入 Expo Camera 扫码 overlay 并保留 H5 浏览器扫码回退。 让 Tauri 白名单识别扫码 method 但不声明桌面扫码能力。 同步原生壳门禁、Expo 权限检查、方案文档和共享决策记录。
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
||||
normalizeHostBridgeHapticsImpactStyle,
|
||||
normalizeHostBridgeLifecycleState,
|
||||
normalizeHostBridgeLocalNotification,
|
||||
normalizeHostBridgeQrCodeValue,
|
||||
normalizeHostBridgeRequestId,
|
||||
} from './hostBridge';
|
||||
|
||||
@@ -87,6 +88,7 @@ describe('HostBridge shared contract helpers', () => {
|
||||
expect(isHostBridgeCapability('file.importText')).toBe(true);
|
||||
expect(isHostBridgeCapability('file.importImage')).toBe(true);
|
||||
expect(isHostBridgeCapability('file.captureImage')).toBe(true);
|
||||
expect(isHostBridgeCapability('scanner.scanQrCode')).toBe(true);
|
||||
expect(isHostBridgeCapability('file.importAudio')).toBe(true);
|
||||
expect(isHostBridgeCapability('file.exportAudio')).toBe(true);
|
||||
expect(isHostBridgeCapability('file.imageDropped')).toBe(true);
|
||||
@@ -140,6 +142,9 @@ describe('HostBridge shared contract helpers', () => {
|
||||
expect(HOST_BRIDGE_EXPO_MOBILE_BASE_CAPABILITIES).toContain(
|
||||
'file.captureImage',
|
||||
);
|
||||
expect(HOST_BRIDGE_EXPO_MOBILE_BASE_CAPABILITIES).toContain(
|
||||
'scanner.scanQrCode',
|
||||
);
|
||||
expect(HOST_BRIDGE_TAURI_DESKTOP_CAPABILITIES).toContain('app.setTitle');
|
||||
expect(HOST_BRIDGE_TAURI_DESKTOP_CAPABILITIES).toContain(
|
||||
'file.imageDropped',
|
||||
@@ -147,6 +152,9 @@ describe('HostBridge shared contract helpers', () => {
|
||||
expect(HOST_BRIDGE_TAURI_DESKTOP_CAPABILITIES).not.toContain(
|
||||
'file.captureImage',
|
||||
);
|
||||
expect(HOST_BRIDGE_TAURI_DESKTOP_CAPABILITIES).not.toContain(
|
||||
'scanner.scanQrCode',
|
||||
);
|
||||
expect(HOST_BRIDGE_TAURI_DESKTOP_CAPABILITIES).not.toContain(
|
||||
'haptics.impact',
|
||||
);
|
||||
@@ -170,6 +178,20 @@ describe('HostBridge shared contract helpers', () => {
|
||||
expect(normalizeHostBridgeClipboardText(null)).toBeNull();
|
||||
});
|
||||
|
||||
test('归一化宿主二维码扫码结果', () => {
|
||||
expect(normalizeHostBridgeQrCodeValue(' https://example.com/a ')).toEqual({
|
||||
value: 'https://example.com/a',
|
||||
format: 'qr_code',
|
||||
});
|
||||
expect(normalizeHostBridgeQrCodeValue('')).toBeNull();
|
||||
expect(normalizeHostBridgeQrCodeValue('bad\nvalue')).toBeNull();
|
||||
expect(normalizeHostBridgeQrCodeValue(null)).toBeNull();
|
||||
expect(normalizeHostBridgeQrCodeValue('a'.repeat(4100))).toEqual({
|
||||
value: 'a'.repeat(4096),
|
||||
format: 'qr_code',
|
||||
});
|
||||
});
|
||||
|
||||
test('归一化宿主本地通知内容', () => {
|
||||
expect(
|
||||
normalizeHostBridgeLocalNotification({
|
||||
|
||||
@@ -36,6 +36,7 @@ export const HOST_BRIDGE_METHODS = [
|
||||
'file.exportImage',
|
||||
'file.importImage',
|
||||
'file.captureImage',
|
||||
'scanner.scanQrCode',
|
||||
'file.importAudio',
|
||||
'file.exportAudio',
|
||||
'haptics.impact',
|
||||
@@ -91,6 +92,7 @@ export const HOST_BRIDGE_EXPO_MOBILE_BASE_CAPABILITIES: readonly HostBridgeCapab
|
||||
'file.exportImage',
|
||||
'file.importImage',
|
||||
'file.captureImage',
|
||||
'scanner.scanQrCode',
|
||||
'file.importAudio',
|
||||
'file.exportAudio',
|
||||
'haptics.impact',
|
||||
@@ -461,6 +463,31 @@ export type FileImportImageResult = {
|
||||
};
|
||||
};
|
||||
|
||||
export type ScannerScanQrCodeResult = {
|
||||
value: string;
|
||||
format: 'qr_code';
|
||||
};
|
||||
|
||||
export const HOST_BRIDGE_QR_CODE_VALUE_MAX_LENGTH = 4096;
|
||||
|
||||
export function normalizeHostBridgeQrCodeValue(
|
||||
rawValue: unknown,
|
||||
): ScannerScanQrCodeResult | null {
|
||||
if (typeof rawValue !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const value = rawValue.trim();
|
||||
if (!value || hasHostBridgeControlCharacter(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
value: value.slice(0, HOST_BRIDGE_QR_CODE_VALUE_MAX_LENGTH),
|
||||
format: 'qr_code',
|
||||
};
|
||||
}
|
||||
|
||||
export type HostBridgeAudioMimeType =
|
||||
| 'audio/mpeg'
|
||||
| 'audio/mp4'
|
||||
|
||||
Reference in New Issue
Block a user