diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 5dc5584..94ff430 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -14,6 +14,7 @@ import PublicList from './components/PublicList.svelte'; import Admin from './components/Admin.svelte'; import ModerateTags from './components/ModerateTags.svelte'; + import UnlockBanner from './components/UnlockBanner.svelte'; import ActivityPermalink from './components/ActivityPermalink.svelte'; import Personvern from './components/Personvern.svelte'; import TagPage from './components/TagPage.svelte'; @@ -263,6 +264,8 @@ {/if} + + {#if view === 'loading'}

Laster …

{:else if view === 'public-list'} diff --git a/frontend/src/components/ActivityForm.svelte b/frontend/src/components/ActivityForm.svelte index a64d8d3..69b5942 100644 --- a/frontend/src/components/ActivityForm.svelte +++ b/frontend/src/components/ActivityForm.svelte @@ -107,7 +107,12 @@ */ function buildBody(): CreateActivityRequest { if (visibility === 'private') { - if (!session.dek) throw new Error('not_logged_in'); + if (!session.dek) { + // Caller (submit) catches this and surfaces a useful message + // pointing at the unlock banner. The error code is a sentinel, + // not user-facing text. + throw new Error('dek_missing'); + } const payload: PrivatePayload = { title: title.trim(), tags, @@ -166,6 +171,14 @@ error = 'Tittel er påkrevd.'; return; } + // Pre-flight: if the user picked Privat but the DEK is missing + // (post-reload state), point them at the unlock banner instead of + // letting them fill out a form they can't submit. Same check in + // buildBody catches direct mis-calls. + if (visibility === 'private' && !session.dek) { + error = 'Privat innhold er låst etter sideoppdatering. Lås opp øverst på siden, så kan du lagre.'; + return; + } busy = true; try { const body = buildBody(); @@ -175,7 +188,11 @@ await syncPrivateTagIndex(); onCreated(result); } catch (err) { - error = err instanceof Error ? err.message : String(err); + if (err instanceof Error && err.message === 'dek_missing') { + error = 'Privat innhold er låst etter sideoppdatering. Lås opp øverst på siden, så kan du lagre.'; + } else { + error = err instanceof Error ? err.message : String(err); + } } finally { busy = false; } diff --git a/frontend/src/components/ActivityRow.svelte b/frontend/src/components/ActivityRow.svelte index d481812..c619342 100644 --- a/frontend/src/components/ActivityRow.svelte +++ b/frontend/src/components/ActivityRow.svelte @@ -316,7 +316,7 @@ Privat

- Innholdet er kryptert. Logg inn på nytt med passordet ditt for å vise det. + Innholdet er kryptert. Lås opp øverst på siden for å vise det.

{:else}

Dekrypterer …

diff --git a/frontend/src/components/Profile.svelte b/frontend/src/components/Profile.svelte index 447a2a3..7d78462 100644 --- a/frontend/src/components/Profile.svelte +++ b/frontend/src/components/Profile.svelte @@ -204,6 +204,13 @@ exporting = true; exportError = null; exportSize = null; + // Without the DEK we'd silently drop every private row from the + // export. Surface that instead of producing a confusingly small file. + if (!session.dek) { + exportError = 'Privat innhold er låst etter sideoppdatering. Lås opp øverst på siden, så får du med alt.'; + exporting = false; + return; + } try { exportSize = await downloadExport(); } catch { diff --git a/frontend/src/components/UnlockBanner.svelte b/frontend/src/components/UnlockBanner.svelte new file mode 100644 index 0000000..a252fa8 --- /dev/null +++ b/frontend/src/components/UnlockBanner.svelte @@ -0,0 +1,75 @@ + + +{#if visible} + +{/if}