Versioning and Updating improvements
This commit is contained in:
+1
-1
@@ -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",
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare const __APP_VERSION__: string;
|
||||||
+9
-1
@@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user