Versioning and Updating improvements
CI / build (push) Has been skipped
CI / deploy (push) Successful in 1m3s

This commit is contained in:
2026-03-13 12:21:25 -05:00
parent b89e6ca2c3
commit 3ebc43ef72
5 changed files with 95 additions and 16 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "tasgrid", "name": "tasgrid",
"private": true, "private": true,
"version": "0.0.0", "version": "1.1.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite --host 0.0.0.0 --port 4001", "dev": "vite --host 0.0.0.0 --port 4001",
+38
View File
@@ -1,8 +1,10 @@
import { type Component, createEffect, onCleanup } from 'solid-js'; import { type Component, createEffect, onCleanup } from 'solid-js';
import { useRegisterSW } from 'virtual:pwa-register/solid'; import { useRegisterSW } from 'virtual:pwa-register/solid';
import { toast } from 'solid-sonner';
// We export a function so other parts of the app can manually trigger an update // We export a function so other parts of the app can manually trigger an update
let manualUpdateFn: (() => void) | undefined; let manualUpdateFn: (() => void) | undefined;
let updateAndReloadFn: (() => void) | undefined;
export const forceServiceWorkerUpdate = () => { export const forceServiceWorkerUpdate = () => {
if (manualUpdateFn) { if (manualUpdateFn) {
@@ -12,6 +14,15 @@ export const forceServiceWorkerUpdate = () => {
} }
}; };
export const updateAndReloadApp = () => {
if (updateAndReloadFn) {
updateAndReloadFn();
} else {
console.warn("PWA Updater not initialized yet; reloading directly.");
window.location.reload();
}
};
export const PwaUpdater: Component = () => { export const PwaUpdater: Component = () => {
let registration: ServiceWorkerRegistration | undefined; let registration: ServiceWorkerRegistration | undefined;
@@ -47,6 +58,33 @@ export const PwaUpdater: Component = () => {
registration.update(); registration.update();
} }
}; };
updateAndReloadFn = () => {
if (needRefresh()) {
console.log('Update ready. Reloading now...');
updateServiceWorker(true);
return;
}
if (registration) {
console.log('Checking for updates before reload...');
registration.update();
}
const start = Date.now();
const timeoutMs = 5000;
const poll = window.setInterval(() => {
if (needRefresh()) {
console.log('Update found. Reloading with new SW...');
window.clearInterval(poll);
updateServiceWorker(true);
} else if (Date.now() - start > timeoutMs) {
console.log('No update detected quickly. Reloading now...');
window.clearInterval(poll);
toast.info("No update found");
}
}, 250);
};
}); });
createEffect(() => { createEffect(() => {
+44 -14
View File
@@ -1088,20 +1088,50 @@ export const SettingsView: Component<{ setView?: (v: string) => void }> = (props
</div> </div>
<footer class="pt-40 pb-20 flex flex-col items-center gap-4"> <footer class="pt-40 pb-20 flex flex-col items-center gap-4">
<Button <div class="flex items-center gap-2">
variant="ghost" <Button
size="sm" variant="ghost"
class="h-8 gap-2 text-[0.625rem] font-bold uppercase tracking-widest text-muted-foreground hover:text-foreground opacity-30 hover:opacity-100 transition-opacity" size="sm"
onClick={async () => { class="h-8 gap-2 text-[0.625rem] font-bold uppercase tracking-widest text-muted-foreground hover:text-foreground opacity-70 hover:opacity-100 transition-opacity"
const { forceServiceWorkerUpdate } = await import('@/components/PwaUpdater'); onClick={async () => {
forceServiceWorkerUpdate(); const { updateAndReloadApp } = await import('@/components/PwaUpdater');
toast.success("Checking for updates..."); toast.success("Updating app...");
}} updateAndReloadApp();
> }}
<Upload class="rotate-180" size={14} /> >
Check for Updates <Upload class="rotate-180" size={14} />
</Button> Update App
<p class="text-[0.5rem] font-mono text-muted-foreground/30 uppercase tracking-[0.2em]">Tasgrid v1.0.1</p> {/* Versioning System: v[breaking update].[user-facing update].[patch/backend updated] */} </Button>
<Button
variant="ghost"
size="sm"
class="h-8 gap-2 text-[0.625rem] font-bold uppercase tracking-widest text-muted-foreground hover:text-foreground opacity-70 hover:opacity-100 transition-opacity"
onClick={async () => {
if (!confirm("Reset PWA? This will clear offline data and reload.")) return;
try {
const registrations = await navigator.serviceWorker?.getRegistrations?.();
if (registrations) {
await Promise.all(registrations.map(r => r.unregister()));
}
if ('caches' in window) {
const keys = await caches.keys();
await Promise.all(keys.map(k => caches.delete(k)));
}
toast.success("PWA reset. Reloading...");
window.location.reload();
} catch (err) {
console.error("Failed to reset PWA", err);
toast.error("Failed to reset PWA");
}
}}
>
<Trash2 size={14} />
Reset PWA
</Button>
</div>
<p class="text-[0.5rem] font-mono text-muted-foreground/60 uppercase tracking-[0.2em]">
Tasgrid v{__APP_VERSION__}
</p> {/* Versioning System: v[breaking update].[user-facing update].[patch/backend updated] */}
</footer> </footer>
</div> </div>
); );
+3
View File
@@ -0,0 +1,3 @@
/// <reference types="vite/client" />
declare const __APP_VERSION__: string;
+9 -1
View File
@@ -3,8 +3,16 @@ import solid from 'vite-plugin-solid'
import { VitePWA } from 'vite-plugin-pwa' import { VitePWA } from 'vite-plugin-pwa'
import tailwindcss from '@tailwindcss/vite' import tailwindcss from '@tailwindcss/vite'
import path from 'path' import path from 'path'
import { readFileSync } from 'fs'
const packageJson = JSON.parse(
readFileSync(new URL('./package.json', import.meta.url), 'utf-8')
);
export default defineConfig({ export default defineConfig({
define: {
__APP_VERSION__: JSON.stringify(packageJson.version),
},
plugins: [ plugins: [
solid(), solid(),
tailwindcss(), tailwindcss(),
@@ -76,4 +84,4 @@ export default defineConfig({
"@": path.resolve(__dirname, "./src") "@": path.resolve(__dirname, "./src")
} }
} }
}) })