refactor: migrate app to Alpine.js

This commit is contained in:
abdus committed 2026-10-10 11:07:10 +00:00
1 parent 766212f119
commit bc236db952
9 files changed
+269 -216

No files matched your search

+53 -44
View File
@@ -1,51 +1,60 @@
export function setupPWA({failed}) {
const install = document.getElementById('install');
const guide = document.getElementById('install-guide');
const update = document.getElementById('update');
const standalone = () => window.matchMedia('(display-mode: standalone)').matches || navigator.standalone;
// Browser prompt and worker objects stay outside Alpine's reactive state.
export function setupPWA({installAvailable, updateAvailable, installed, failed}) {
let prompt;
guide.hidden = !!standalone();
window.addEventListener('beforeinstallprompt', event => {
let registration;
const cleanup = [];
const listen = (target, name, handler) => {
target.addEventListener(name, handler);
cleanup.push(() => target.removeEventListener(name, handler));
};
const standalone = () => window.matchMedia('(display-mode: standalone)').matches || !!navigator.standalone;
installed(standalone());
listen(window, 'beforeinstallprompt', event => {
event.preventDefault();
prompt = event;
install.hidden = false;
guide.hidden = true;
installAvailable(true);
});
install.addEventListener('click', async () => {
if (!prompt) return;
const pending = prompt;
listen(window, 'appinstalled', () => {
prompt = undefined;
install.hidden = true;
try {
await pending.prompt();
await pending.userChoice;
} catch {
// Keep manual installation guidance available if the prompt fails.
} finally {
guide.hidden = !!standalone();
}
installAvailable(false);
installed(true);
});
window.addEventListener('appinstalled', () => {
prompt = undefined;
install.hidden = true;
guide.hidden = true;
});
if (!('serviceWorker' in navigator)) return;
let controlled = !!navigator.serviceWorker.controller;
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (controlled) window.location.reload();
controlled = true;
});
navigator.serviceWorker.register('./sw.js', {scope: './', type: 'module', updateViaCache: 'none'})
.then(registration => {
const showUpdate = () => { update.hidden = !registration.waiting; };
showUpdate();
registration.addEventListener('updatefound', () => {
const worker = registration.installing;
if (worker) worker.addEventListener('statechange', showUpdate);
});
update.addEventListener('click', () => {
if (registration.waiting) registration.waiting.postMessage('ACTIVATE_UPDATE');
});
}).catch(failed);
let destroyed = false;
if ('serviceWorker' in navigator) {
let controlled = !!navigator.serviceWorker.controller;
listen(navigator.serviceWorker, 'controllerchange', () => {
if (controlled) window.location.reload();
controlled = true;
});
navigator.serviceWorker.register('./sw.js', {scope: './', type: 'module', updateViaCache: 'none'})
.then(value => {
if (destroyed) return;
registration = value;
const showUpdate = () => updateAvailable(!!registration.waiting);
showUpdate();
listen(registration, 'updatefound', () => {
if (registration.installing) listen(registration.installing, 'statechange', showUpdate);
});
}).catch(() => { if (!destroyed) failed(); });
}
return {
async install() {
if (!prompt) return;
const pending = prompt;
prompt = undefined;
installAvailable(false);
try {
await pending.prompt();
await pending.userChoice;
} catch { /* Manual installation guidance remains available. */ }
},
update() {
registration?.waiting?.postMessage('ACTIVATE_UPDATE');
},
destroy() {
destroyed = true;
cleanup.forEach(remove => remove());
},
};
}