Files
fotospiel-app/resources/js/admin/mobile/hooks/useInstallPrompt.ts
Codex Agent d5f038d098 Added onboarding + a lightweight install banner to both the mobile login screen and the settings screen, with Android/Chromium
install prompt support and iOS “Share → Add to Home Screen” guidance. Also added a small helper + tests to decide
  when/which banner variant should show, and shared copy in common.json.
2025-12-28 18:26:17 +01:00

92 lines
2.6 KiB
TypeScript

import React from 'react';
import {
BeforeInstallPromptEvent,
getStandaloneStatus,
isIosDevice,
type InstallOutcome,
} from '../lib/installPrompt';
type InstallPromptState = {
canInstall: boolean;
isInstalled: boolean;
isStandalone: boolean;
isIos: boolean;
outcome: InstallOutcome | null;
promptInstall: () => Promise<InstallOutcome | null>;
};
export function useInstallPrompt(): InstallPromptState {
const [deferredPrompt, setDeferredPrompt] = React.useState<BeforeInstallPromptEvent | null>(null);
const [isInstalled, setIsInstalled] = React.useState(false);
const [isStandalone, setIsStandalone] = React.useState(false);
const [isIos, setIsIos] = React.useState(false);
const [outcome, setOutcome] = React.useState<InstallOutcome | null>(null);
React.useEffect(() => {
if (typeof window === 'undefined') {
return undefined;
}
const updateStandalone = () => {
const standalone = getStandaloneStatus();
setIsStandalone(standalone);
if (standalone) {
setIsInstalled(true);
}
};
setIsIos(isIosDevice(navigator.userAgent ?? ''));
updateStandalone();
const handleBeforeInstallPrompt = (event: Event) => {
event.preventDefault();
setDeferredPrompt(event as BeforeInstallPromptEvent);
};
const handleAppInstalled = () => {
setIsInstalled(true);
setDeferredPrompt(null);
setOutcome('accepted');
};
const mediaQuery = window.matchMedia?.('(display-mode: standalone)');
const handleDisplayModeChange = () => updateStandalone();
window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
window.addEventListener('appinstalled', handleAppInstalled);
mediaQuery?.addEventListener?.('change', handleDisplayModeChange);
return () => {
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
window.removeEventListener('appinstalled', handleAppInstalled);
mediaQuery?.removeEventListener?.('change', handleDisplayModeChange);
};
}, []);
const promptInstall = React.useCallback(async () => {
if (!deferredPrompt) {
return null;
}
await deferredPrompt.prompt();
const choice = await deferredPrompt.userChoice;
setOutcome(choice.outcome ?? 'unknown');
if (choice.outcome === 'accepted') {
setIsInstalled(true);
setDeferredPrompt(null);
}
return choice.outcome ?? 'unknown';
}, [deferredPrompt]);
return {
canInstall: Boolean(deferredPrompt),
isInstalled,
isStandalone,
isIos,
outcome,
promptInstall,
};
}