Files
TREK/client/src/native/nativeDialogs.ts
T
Maurice d06168d94a feat(native): the app answers a finger like a native app, not like a web page
After a tap, WebKit and Chrome keep :hover on what was tapped and send a
made-up mouseover, so buttons and rows stayed tinted until the next tap.
While a finger drives the app <html> carries data-touch-input: a PostCSS
step guards every :hover rule with it (no specificity change), and the
made-up mouseover and mouseout stop before any handler. A trackpad or mouse
clears the mark and gets hover back; a browser never sets it.

A control without a press of its own dims the moment a finger lands on it,
after a 60 ms wait so a scroll does not flash rows, and only the innermost
pressed control. A focus: ring no longer stays round a tapped button.

Fields get a fitting return key ("done", "go" in a form, "search" on the
search fields), and addresses, codes and numbers are no longer capitalised
or corrected. Ticking off packing items and tasks, a picker chip, a select
option, a picked date and dragging a plan row now give the haptics a native
list gives.

Confirmations ask through the system where the binary has the new
@capacitor/action-sheet and @capacitor/dialog plugins: a sheet with the
action in red when it destroys something, an alert otherwise, also for
"discard changes" in a sheet. An older app, a browser and a question with a
list under it keep the HTML dialog, as does a system that could not ask.

iOS no longer lifts a page preview on a long press on a link.
2026-11-09 08:54:51 +01:00

63 lines
2.1 KiB
TypeScript

import { ActionSheet, ActionSheetButtonStyle } from '@capacitor/action-sheet';
import { Capacitor } from '@capacitor/core';
import { Dialog } from '@capacitor/dialog';
import { isNativeApp } from './platform';
import { reportNativeFailure } from './reportFailure';
/**
* A yes-or-no question asked the way the phone asks it.
*
* Asked in HTML, "Delete this place?" is a card drawn by the page, and it
* looks like one. In the app it goes to the system instead: an action sheet
* with the action in red when it destroys something, an alert when it does
* not. That is what iOS and Android show in their own apps for the same
* question, with the system's own animation, sound and accessibility.
*
* Only where the binary carries both plugins: an app from before them keeps
* the HTML dialog, as does a browser.
*/
export interface NativeQuestion {
title: string;
message?: string;
confirmLabel: string;
cancelLabel: string;
/** Destroys or throws something away: the action shows in red, on a sheet. */
danger: boolean;
}
export function canAskNatively(): boolean {
return isNativeApp() && Capacitor.isPluginAvailable('ActionSheet') && Capacitor.isPluginAvailable('Dialog');
}
/**
* True when the user chose the action, false when they did not. Null when the
* system could not ask: the caller then shows its HTML dialog, so a question
* is never lost and nothing is done that was not confirmed.
*/
export async function askNatively(q: NativeQuestion): Promise<boolean | null> {
try {
if (q.danger) {
const { index } = await ActionSheet.showActions({
title: q.title,
message: q.message,
options: [
{ title: q.confirmLabel, style: ActionSheetButtonStyle.Destructive },
{ title: q.cancelLabel, style: ActionSheetButtonStyle.Cancel },
],
});
return index === 0;
}
const { value } = await Dialog.confirm({
title: q.title,
message: q.message ?? '',
okButtonTitle: q.confirmLabel,
cancelButtonTitle: q.cancelLabel,
});
return value;
} catch (err) {
reportNativeFailure(err);
return null;
}
}