diff --git a/src/lib/components/SafariAnnotationWarning.svelte b/src/lib/components/SafariAnnotationWarning.svelte new file mode 100644 index 000000000..afdc2a7f8 --- /dev/null +++ b/src/lib/components/SafariAnnotationWarning.svelte @@ -0,0 +1,53 @@ + + +{#if visible} +
+ +
+

Annotations may be deleted by Safari

+

+ Safari can delete your bookmarks, highlights, and notes after 1–2 weeks of + inactivity. To keep them permanently, add this app to your Home Screen. +

+
+ +
+{/if} diff --git a/src/lib/scripts/safariUtils.ts b/src/lib/scripts/safariUtils.ts new file mode 100644 index 000000000..7699a8ddc --- /dev/null +++ b/src/lib/scripts/safariUtils.ts @@ -0,0 +1,48 @@ +const DISMISSED_KEY = 'safari_annotation_warning_dismissed'; +const DISMISS_DURATION_MS = 30 * 24 * 60 * 60 * 1000; // 30 days + +function isSafariWithoutStandalone(): boolean { + if (typeof window === 'undefined') { + return false; + } + const ua = navigator.userAgent; + const isSafari = /Safari/.test(ua) && !/Chrome|CriOS|FxiOS|EdgiOS/.test(ua); + const isStandalone = (navigator as Navigator & { standalone?: boolean }).standalone === true; + return isSafari && !isStandalone; +} + +export function shouldShowSafariWarning(): boolean { + if (typeof window === 'undefined') { + return false; + } + // Debug override: because the app uses hash routing, append ?debug_safari=true + // inside the hash, e.g. /#/bookmarks?debug_safari=true + const hashQuery = window.location.hash.split('?')[1] ?? ''; + if (new URLSearchParams(hashQuery).get('debug_safari') === 'true') { + return true; + } + if (!isSafariWithoutStandalone()) { + return false; + } + try { + const dismissed = localStorage.getItem(DISMISSED_KEY); + if (!dismissed) { + return true; + } + const dismissedAt = parseInt(dismissed, 10); + if (isNaN(dismissedAt)) { + return true; + } + return Date.now() - dismissedAt > DISMISS_DURATION_MS; + } catch { + return true; + } +} + +export function dismissSafariWarning(): void { + try { + localStorage.setItem(DISMISSED_KEY, Date.now().toString()); + } catch { + // If storage is unavailable, silently ignore — the warning will reappear next visit + } +} diff --git a/src/routes/bookmarks/+page.svelte b/src/routes/bookmarks/+page.svelte index 845fd46e9..818bc8401 100644 --- a/src/routes/bookmarks/+page.svelte +++ b/src/routes/bookmarks/+page.svelte @@ -3,6 +3,7 @@ import { resolve } from '$app/paths'; import IconCard from '$lib/components/IconCard.svelte'; import Navbar from '$lib/components/Navbar.svelte'; + import SafariAnnotationWarning from '$lib/components/SafariAnnotationWarning.svelte'; import SortMenu from '$lib/components/SortMenu.svelte'; import { shareAnnotation, shareAnnotations } from '$lib/data/annotation-share'; import { SORT_DATE, SORT_REFERENCE, toSorted } from '$lib/data/annotation-sort'; @@ -82,6 +83,7 @@ class="overflow-y-auto p-2.5 max-w-screen-md mx-auto w-full" style:font-size="{$bodyFontSize}px" > + {#if data.bookmarks.length === 0}
{$t['Annotation_Bookmarks_None']}
{$t['Annotation_Bookmarks_None_Info']}
diff --git a/src/routes/highlights/+page.svelte b/src/routes/highlights/+page.svelte index 3fc4bbff3..6e9305496 100644 --- a/src/routes/highlights/+page.svelte +++ b/src/routes/highlights/+page.svelte @@ -3,6 +3,7 @@ import { resolve } from '$app/paths'; import IconCard from '$lib/components/IconCard.svelte'; import Navbar from '$lib/components/Navbar.svelte'; + import SafariAnnotationWarning from '$lib/components/SafariAnnotationWarning.svelte'; import SortMenu from '$lib/components/SortMenu.svelte'; import { shareAnnotation, shareAnnotations } from '$lib/data/annotation-share'; import { SORT_COLOR, SORT_DATE, SORT_REFERENCE, toSorted } from '$lib/data/annotation-sort'; @@ -88,6 +89,7 @@ class="overflow-y-auto p-2.5 max-w-screen-md mx-auto w-full" style:font-size="{$bodyFontSize}px" > + {#if data.highlights.length === 0}
{$t['Annotation_Highlights_None']}
{$t['Annotation_Highlights_None_Info']}
diff --git a/src/routes/notes/+page.svelte b/src/routes/notes/+page.svelte index affd97b84..51f22bcb6 100644 --- a/src/routes/notes/+page.svelte +++ b/src/routes/notes/+page.svelte @@ -3,6 +3,7 @@ import { resolve } from '$app/paths'; import IconCard from '$lib/components/IconCard.svelte'; import Navbar from '$lib/components/Navbar.svelte'; + import SafariAnnotationWarning from '$lib/components/SafariAnnotationWarning.svelte'; import SortMenu from '$lib/components/SortMenu.svelte'; import { shareAnnotation, shareAnnotations } from '$lib/data/annotation-share'; import { SORT_DATE, SORT_REFERENCE, toSorted } from '$lib/data/annotation-sort'; @@ -81,6 +82,7 @@ class="overflow-y-auto p-2.5 max-w-screen-md mx-auto w-full" style:font-size="{$bodyFontSize}px" > + {#if data.notes.length === 0}
{$t['Annotation_Notes_None']}
{$t['Annotation_Notes_None_Info']}