refactor: separate React views, Redux snapshots and Three.js runtime
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
/** Connects install/update lifecycle to the React interface and returns cleanup.
|
||||
* @param {object} ui - Interface bridge.
|
||||
* @returns {function} Releases browser event subscriptions.
|
||||
*/
|
||||
export function setupPwa(ui) {
|
||||
const controller = new AbortController(),
|
||||
{ signal } = controller;
|
||||
let deferred = null,
|
||||
reloading = false,
|
||||
disposed = false;
|
||||
const standalone =
|
||||
matchMedia('(display-mode: standalone), (display-mode: fullscreen)').matches ||
|
||||
navigator.standalone;
|
||||
const ios =
|
||||
/iphone|ipad|ipod/i.test(navigator.userAgent) ||
|
||||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
|
||||
if (!standalone && ios) ui.patch('install', { hidden: false });
|
||||
window.addEventListener(
|
||||
'beforeinstallprompt',
|
||||
(event) => {
|
||||
event.preventDefault();
|
||||
deferred = event;
|
||||
ui.patch('install', { hidden: false });
|
||||
},
|
||||
{ signal },
|
||||
);
|
||||
window.addEventListener('appinstalled', () => ui.patch('install', { hidden: true }), { signal });
|
||||
ui.on('install', async () => {
|
||||
if (deferred) {
|
||||
deferred.prompt();
|
||||
await deferred.userChoice;
|
||||
deferred = null;
|
||||
ui.patch('install', { hidden: true });
|
||||
} else if (ios) ui.open('ios-install');
|
||||
});
|
||||
ui.on('ios-close', () => ui.close('ios-install'));
|
||||
if ('serviceWorker' in navigator && !import.meta.env.DEV) {
|
||||
/** Offers a version change only after a worker has installed successfully. */
|
||||
const offer = (worker) => {
|
||||
ui.patch('update-toast', { hidden: false });
|
||||
ui.on('update-reload', () => worker.postMessage('skip-waiting'));
|
||||
};
|
||||
navigator.serviceWorker
|
||||
.register('./sw.js')
|
||||
.then((registration) => {
|
||||
if (disposed) return;
|
||||
if (registration.waiting && navigator.serviceWorker.controller) offer(registration.waiting);
|
||||
registration.addEventListener(
|
||||
'updatefound',
|
||||
() => {
|
||||
const worker = registration.installing;
|
||||
worker?.addEventListener(
|
||||
'statechange',
|
||||
() => {
|
||||
if (worker.state === 'installed' && navigator.serviceWorker.controller)
|
||||
offer(worker);
|
||||
},
|
||||
{ signal },
|
||||
);
|
||||
},
|
||||
{ signal },
|
||||
);
|
||||
})
|
||||
.catch(() => {});
|
||||
navigator.serviceWorker.addEventListener(
|
||||
'controllerchange',
|
||||
() => {
|
||||
if (reloading) return;
|
||||
reloading = true;
|
||||
location.reload();
|
||||
},
|
||||
{ signal },
|
||||
);
|
||||
}
|
||||
return () => {
|
||||
disposed = true;
|
||||
controller.abort();
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user