Files
paseo/packages/app/src/utils/confirm-dialog.ts
T
Mohamed Boudra 0f7fa55a0f Centralize platform gating and fix iPad/tablet support
Add `packages/app/src/constants/platform.ts` with canonical gates
(isWeb, isNative, getIsElectron) and refactor all ~100 ad-hoc
Platform.OS checks across 69 files to use shared imports.

Fix sidebar hover crash on native (onPointerEnter → onHoverIn),
fix tooltip to use breakpoint instead of platform detection, make
sidebar action buttons always visible on native where hover doesn't
work, and document the platform gating decision matrix in CLAUDE.md.
2026-04-13 13:24:00 +07:00

123 lines
3.0 KiB
TypeScript

import { Alert } from "react-native";
import { getDesktopHost, type DesktopDialogAskOptions } from "@/desktop/host";
import { isNative } from "@/constants/platform";
export interface ConfirmDialogInput {
title: string;
message: string;
confirmLabel?: string;
cancelLabel?: string;
destructive?: boolean;
}
interface ConfirmButtonConfig {
confirmLabel: string;
cancelLabel: string;
}
function resolveButtonLabels(input: ConfirmDialogInput): ConfirmButtonConfig {
return {
confirmLabel: input.confirmLabel ?? "Confirm",
cancelLabel: input.cancelLabel ?? "Cancel",
};
}
async function showNativeConfirmDialog(input: ConfirmDialogInput): Promise<boolean> {
const labels = resolveButtonLabels(input);
return new Promise<boolean>((resolve) => {
Alert.alert(
input.title,
input.message,
[
{
text: labels.cancelLabel,
style: "cancel",
onPress: () => resolve(false),
},
{
text: labels.confirmLabel,
style: input.destructive ? "destructive" : "default",
onPress: () => resolve(true),
},
],
{
cancelable: true,
onDismiss: () => resolve(false),
},
);
});
}
function getDesktopApi() {
if (isNative) {
return null;
}
return getDesktopHost();
}
function buildDesktopAskOptions(input: ConfirmDialogInput): DesktopDialogAskOptions {
const labels = resolveButtonLabels(input);
return {
title: input.title,
okLabel: labels.confirmLabel,
cancelLabel: labels.cancelLabel,
kind: input.destructive ? "warning" : "info",
};
}
function blurActiveWebElement(): void {
if (isNative) {
return;
}
const activeElement = (globalThis as { document?: Document }).document?.activeElement;
(activeElement as HTMLElement | null)?.blur?.();
}
async function showDesktopConfirmDialog(input: ConfirmDialogInput): Promise<boolean | null> {
const desktopApi = getDesktopApi();
if (!desktopApi) {
return null;
}
blurActiveWebElement();
const options = buildDesktopAskOptions(input);
const desktopAsk = desktopApi.dialog?.ask;
if (typeof desktopAsk === "function") {
return Boolean(await desktopAsk(input.message, options));
}
return null;
}
function showWebConfirmDialog(input: ConfirmDialogInput): boolean {
const browserConfirm = (globalThis as { confirm?: (message?: string) => boolean }).confirm;
if (typeof browserConfirm !== "function") {
throw new Error("[ConfirmDialog] No web confirmation backend is available.");
}
blurActiveWebElement();
const promptMessage = `${input.title}\n\n${input.message}`;
return browserConfirm(promptMessage);
}
export async function confirmDialog(input: ConfirmDialogInput): Promise<boolean> {
if (isNative) {
return showNativeConfirmDialog(input);
}
const desktopResult = await showDesktopConfirmDialog(input);
if (desktopResult !== null) {
return desktopResult;
}
return showWebConfirmDialog(input);
}
export const __private__ = {
blurActiveWebElement,
buildDesktopAskOptions,
};