55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
/**
|
|||
|
|
* [Gitea #116] PWA install hint — pure decision logic.
|
||
|
|
*
|
||
|
|
* Chromium fires `beforeinstallprompt`; iOS Safari never prompts and only
|
||
|
|
* installs via Share → "Add to Home Screen", so people on phones simply never
|
||
|
|
* find it. The hint is shown once the user has come back at least once (second
|
||
|
|
* visit), never inside the desktop app or an already-installed PWA, and snoozes
|
||
|
|
* for 30 days after a dismissal.
|
||
|
|
*/
|
||
|
|
|
||
|
|
export const INSTALL_HINT_MIN_VISITS = 2;
|
||
|
|
export const INSTALL_HINT_SNOOZE_MS = 30 * 24 * 60 * 60 * 1000;
|
||
|
|
|
||
|
|
export type InstallHintInput = {
|
||
|
|
/** Running inside the Tauri desktop app. */
|
||
|
|
isTauri: boolean;
|
||
|
|
/** `display-mode: standalone` or iOS `navigator.standalone` — already installed. */
|
||
|
|
isStandalone: boolean;
|
||
|
|
/** Distinct app visits recorded so far, including this one. */
|
||
|
|
visits: number;
|
||
|
|
/** Epoch ms of the last dismissal, or null. */
|
||
|
|
dismissedAt: number | null;
|
||
|
|
now: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
export const shouldShowInstallHint = ({
|
||
|
|
isTauri,
|
||
|
|
isStandalone,
|
||
|
|
visits,
|
||
|
|
dismissedAt,
|
||
|
|
now,
|
||
|
|
}: InstallHintInput): boolean => {
|
||
|
|
if (isTauri || isStandalone) return false;
|
||
|
|
if (visits < INSTALL_HINT_MIN_VISITS) return false;
|
||
|
|
if (dismissedAt !== null && now - dismissedAt < INSTALL_HINT_SNOOZE_MS) return false;
|
||
|
|
return true;
|
||
|
|
};
|
||
|
|
|
||
|
|
export type InstallHintKind = 'prompt' | 'ios-share' | 'none';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Which hint to show: a real install prompt when the browser handed us a
|
||
|
|
* deferred `beforeinstallprompt`, the manual Share-sheet instructions on iOS
|
||
|
|
* Safari, or nothing (Firefox desktop and friends have no install path — a
|
||
|
|
* hint there would just be noise).
|
||
|
|
*/
|
||
|
|
export const installHintKind = (
|
||
|
|
hasDeferredPrompt: boolean,
|
||
|
|
isIosSafari: boolean,
|
||
|
|
): InstallHintKind => {
|
||
|
|
if (hasDeferredPrompt) return 'prompt';
|
||
|
|
if (isIosSafari) return 'ios-share';
|
||
|
|
return 'none';
|
||
|
|
};
|