Skip to content

Commit

Permalink
feat: added update data loss warning
Browse files Browse the repository at this point in the history
  • Loading branch information
WhyAsh5114 committed Dec 24, 2024
1 parent 098073b commit b124e1d
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 54 deletions.
37 changes: 26 additions & 11 deletions src/routes/(components)/layout/ChangelogDialog.svelte
Original file line number Diff line number Diff line change
@@ -1,38 +1,53 @@
<script lang="ts">
import ResponsiveDialog from '$lib/components/ResponsiveDialog.svelte';
import { onMount } from 'svelte';
import { marked } from 'marked';
import Button from '$lib/components/ui/button/button.svelte';
import ScrollArea from '$lib/components/ui/scroll-area/scroll-area.svelte';
import DOMPurify from 'dompurify';
import { marked } from 'marked';
import { onMount } from 'svelte';
import LoaderCircle from 'virtual:icons/lucide/loader-circle';
import ScrollArea from '$lib/components/ui/scroll-area/scroll-area.svelte';
import ReloadIcon from 'virtual:icons/lucide/refresh-ccw';
import { needRefresh, updateDataLossDialog } from './PWAFunctions.svelte';
let open = $state(false);
let dialogText = $state<string>();
let latestRelease = $state<{ tag_name: string; body: string }>();
onMount(async () => {
const response = await fetch('https://api.github.com/repos/WhyAsh5114/MyFit/releases/latest');
latestRelease = await response.json();
onMount(() => {
const ls = window.localStorage;
if (ls.getItem('changelogShown') !== 'true') {
const changelogShownOf = ls.getItem('changelogShownOf');
if (
changelogShownOf &&
changelogShownOf.localeCompare(latestRelease!.tag_name, undefined, { numeric: true }) === -1
) {
open = true;
loadChangelog();
ls.setItem('changelogShown', 'true');
}
ls.setItem('changelogShownOf', latestRelease!.tag_name);
});
async function loadChangelog() {
await new Promise((resolve) => setTimeout(resolve, 2000));
const response = await fetch('https://api.github.com/repos/WhyAsh5114/MyFit/releases/latest');
const data = await response.json();
dialogText = DOMPurify.sanitize(await marked.parse(data.body));
dialogText = DOMPurify.sanitize(await marked.parse(latestRelease!.body));
}
</script>

<ResponsiveDialog title="What's new?" bind:open>
<ResponsiveDialog title="What's new?" bind:open dismissible={false}>
{#if dialogText}
<ScrollArea class="h-96">
<article class="prose prose-sm dark:prose-invert">
{@html dialogText}
</article>
</ScrollArea>
<Button disabled={!$needRefresh} class="gap-2" onclick={() => (updateDataLossDialog.open = true)}>
{#if !$needRefresh}
Fetching update <LoaderCircle class="animate-spin" />
{:else}
Update & reload <ReloadIcon />
{/if}
</Button>
{:else}
<div class="flex items-center justify-center gap-2 p-2 text-sm text-muted-foreground">
<LoaderCircle class="animate-spin" />
Expand Down
37 changes: 4 additions & 33 deletions src/routes/(components)/layout/PWAButtons.svelte
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
<script lang="ts">
import Button from '$lib/components/ui/button/button.svelte';
import { cn } from '$lib/utils';
import { onMount } from 'svelte';
import { useRegisterSW } from 'virtual:pwa-register/svelte';
import type { Writable } from 'svelte/store';
import DownloadIcon from 'virtual:icons/lucide/download';
import UpdateIcon from 'virtual:icons/lucide/refresh-cw';
import Button from '$lib/components/ui/button/button.svelte';
import { cn } from '$lib/utils';
import { needRefresh, updateDataLossDialog } from './PWAFunctions.svelte';
let { isMobile }: { isMobile: boolean } = $props();
let deferredPrompt: Event | null;
let needRefresh: Writable<boolean> | undefined = $state();
let reloading = $state(false);
let showInstallButton = $state(false);
let updateServiceWorker: (_arg0: boolean) => void;
onMount(() => {
window.addEventListener('beforeinstallprompt', (e) => {
Expand All @@ -26,35 +22,10 @@
showInstallButton = false;
deferredPrompt = null;
});
({ needRefresh, updateServiceWorker } = useRegisterSW({
onRegisteredSW(swUrl, r) {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
r &&
setInterval(async () => {
if (!(!r.installing && navigator)) return;
if ('connection' in navigator && !navigator.onLine) return;
const resp = await fetch(swUrl, {
cache: 'no-store',
headers: {
cache: 'no-store',
'cache-control': 'no-cache'
}
});
if (resp.status === 200) await r.update();
}, 3600000);
console.log(`SW Registered: ${r}`);
},
onRegisterError(error) {
console.log('SW registration error', error);
}
}));
});
function updateApplication() {
reloading = true;
localStorage.clear();
updateServiceWorker(true);
updateDataLossDialog.open = true;
}
</script>

Expand Down
33 changes: 33 additions & 0 deletions src/routes/(components)/layout/PWAFunctions.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { browser } from '$app/environment';
import { useRegisterSW } from 'virtual:pwa-register/svelte';

const sw = browser
? useRegisterSW({
onRegisteredSW(swUrl, r) {
if (r) {
setInterval(async () => {
if (!navigator || r.installing) return;
if ('connection' in navigator && !navigator.onLine) return;

const resp = await fetch(swUrl, {
cache: 'no-store',
headers: {
cache: 'no-store',
'cache-control': 'no-cache'
}
});
if (resp.status === 200) await r.update();
}, 3600000);
}
console.log(`SW Registered: ${r}`);
},
onRegisterError(error) {
console.log('SW registration error', error);
}
})
: null;

export const needRefresh = sw?.needRefresh;
export const updateServiceWorker = sw?.updateServiceWorker;
export const offlineReady = sw?.offlineReady;
export const updateDataLossDialog = $state({ open: false });
27 changes: 27 additions & 0 deletions src/routes/(components)/layout/UpdateDataLossDialog.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script lang="ts">
import ResponsiveDialog from '$lib/components/ResponsiveDialog.svelte';
import Button from '$lib/components/ui/button/button.svelte';
import LoaderCircle from 'virtual:icons/lucide/loader-circle';
import { updateDataLossDialog, updateServiceWorker } from './PWAFunctions.svelte';
let updating = $state(false);
function updateApp() {
updating = true;
localStorage.clear();
updateServiceWorker!(true);
}
</script>

<ResponsiveDialog title="Update app?" bind:open={updateDataLossDialog.open}>
{#snippet description()}
<p>Any unsaved data, like a workout in progress, or an unsaved mesocycle will be lost.</p>
{/snippet}
<Button disabled={updating} onclick={updateApp} class="gap-2">
{#if updating}
<LoaderCircle class="animate-spin" />
{:else}
Update
{/if}
</Button>
</ResponsiveDialog>
1 change: 0 additions & 1 deletion src/routes/(components)/layout/UserDropdown.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
function logOut() {
signOut();
localStorage.clear();
localStorage.setItem('changelogShown', 'true');
}
</script>

Expand Down
15 changes: 9 additions & 6 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<script lang="ts">
import '../app.pcss';
import { pwaInfo } from 'virtual:pwa-info';
import { ModeWatcher } from 'mode-watcher';
import { Toaster } from '$lib/components/ui/sonner';
import MobileLayout from './(components)/layout/MobileLayout.svelte';
import DesktopLayout from './(components)/layout/DesktopLayout.svelte';
import { ModeWatcher } from 'mode-watcher';
import { onMount } from 'svelte';
import { pwaInfo } from 'virtual:pwa-info';
import '../app.pcss';
import ChangelogDialog from './(components)/layout/ChangelogDialog.svelte';
import DesktopLayout from './(components)/layout/DesktopLayout.svelte';
import MobileLayout from './(components)/layout/MobileLayout.svelte';
import UpdateDataLossDialog from './(components)/layout/UpdateDataLossDialog.svelte';
import { overrideItemIdKeyNameBeforeInitialisingDndZones } from 'svelte-dnd-action';
import ChangelogDialog from './(components)/layout/ChangelogDialog.svelte';
overrideItemIdKeyNameBeforeInitialisingDndZones('name');
const { children } = $props();
Expand All @@ -30,7 +31,9 @@

<ModeWatcher />
<Toaster />

<ChangelogDialog />
<UpdateDataLossDialog />

{#if isMobile === true}
<MobileLayout>{@render children()}</MobileLayout>
Expand Down
3 changes: 0 additions & 3 deletions src/routes/changelog/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,3 @@ export const load = async () => {
})) as ReleaseData[];
return { releases };
};

// * fix changelogShownOf to be release version instead of a boolean
// * compare it with the release version of the latest release and show dialog if needed

0 comments on commit b124e1d

Please sign in to comment.