Files
Genarrative/apps/mobile-shell/src/host-bridge/haptics.ts
T
kdletters 071faa482c 统一 Rust 与 TypeScript 格式化门禁
纳入 AGC Cargo workspace 的统一 rustfmt 检查与格式化入口

完成项目 TypeScript/Prettier 与 Rust 全量格式化

修复 Pingora expected executable 门禁的空白敏感误报

同步开发运维文档与 AGC skill pack 格式化忽略规则
2026-09-01 16:28:34 +08:00

54 lines
1.5 KiB
TypeScript

import * as Haptics from 'expo-haptics';
import {
type HapticsImpactPayload,
type HostBridgeError,
type HostBridgeHapticsImpactStyle,
type HostBridgeRequest,
normalizeHostBridgeHapticsImpactStyle,
} from '../../../../packages/shared/src/contracts/hostBridge';
import { invalidRequest, ok } from './protocol';
function toExpoImpactStyle(style: HostBridgeHapticsImpactStyle) {
return style === 'heavy'
? Haptics.ImpactFeedbackStyle.Heavy
: style === 'medium'
? Haptics.ImpactFeedbackStyle.Medium
: Haptics.ImpactFeedbackStyle.Light;
}
function logMobileHapticsFailure(label: string, _error: unknown) {
console.warn(`mobile haptics failed for ${label}`);
}
export async function runMobileHapticsImpact(rawStyle: unknown) {
const style = normalizeHostBridgeHapticsImpactStyle(rawStyle);
if (!style) {
return false;
}
try {
await Haptics.impactAsync(toExpoImpactStyle(style));
} catch (error) {
logMobileHapticsFailure('impact.dispatch', error);
throw {
code: 'host_error',
message: 'haptics impact unavailable',
} satisfies HostBridgeError;
}
return true;
}
export async function runMobileHostBridgeHapticsImpact(
request: HostBridgeRequest,
) {
const style = (request.payload as HapticsImpactPayload | undefined)?.style;
if (!(await runMobileHapticsImpact(style))) {
throw invalidRequest(
'haptics impact style must be light, medium, or heavy',
);
}
return ok(request, true);
}