mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:41:09 +02:00
feat(writing): M11 — adopt the unified visibility system
The backing for visibility was already in place from M2 (draft.visibility stamped on create via defaultVisibilityFor, draftsStore.setVisibility mints/clears unlistedToken and emits VisibilityChanged), so M11 is just the UI step that puts it in front of the user. - <VisibilityPicker> from @mana/shared-privacy sits in the meta-row of DetailView, mirroring the library pattern. onChange calls draftsStore.setVisibility — no new store method needed. - Draft type + toDraft converter now surface `unlistedToken` so the UI can render a share row when visibility === 'unlisted'. Token is displayed verbatim + "Kopieren"-button because the public read-URL for drafts ships with M10 (Publish-Hooks); a tooltip makes that explicit so the user doesn't expect a working link yet. With this, Writing is now consistent with the Library / Picture / Calendar / Todo / Goals / Places / Recipes / Wardrobe pilot group. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ed8ec98572
commit
f2275f752d
3 changed files with 70 additions and 0 deletions
|
|
@ -36,6 +36,7 @@ export function toDraft(local: LocalDraft): Draft {
|
|||
publishedTo: local.publishedTo ?? [],
|
||||
isFavorite: local.isFavorite ?? false,
|
||||
visibility: local.visibility ?? 'space',
|
||||
unlistedToken: local.unlistedToken ?? null,
|
||||
createdAt: local.createdAt ?? now,
|
||||
updatedAt: local.updatedAt ?? now,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -210,6 +210,8 @@ export interface Draft {
|
|||
references: DraftReference[];
|
||||
currentVersionId: string | null;
|
||||
visibility: VisibilityLevel;
|
||||
/** 32-char token minted on first flip to 'unlisted'. Null otherwise. */
|
||||
unlistedToken: string | null;
|
||||
publishedTo: DraftPublishTarget[];
|
||||
isFavorite: boolean;
|
||||
createdAt: string;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
-->
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { VisibilityPicker, type VisibilityLevel } from '@mana/shared-privacy';
|
||||
import BriefingForm from '../components/BriefingForm.svelte';
|
||||
import StatusBadge from '../components/StatusBadge.svelte';
|
||||
import VersionEditor from '../components/VersionEditor.svelte';
|
||||
|
|
@ -106,6 +107,31 @@
|
|||
await draftsStore.toggleFavorite(draft.id);
|
||||
}
|
||||
|
||||
async function onVisibilityChange(next: VisibilityLevel) {
|
||||
if (!draft) return;
|
||||
await draftsStore.setVisibility(draft.id, next);
|
||||
}
|
||||
|
||||
let shareCopied = $state(false);
|
||||
let shareCopyTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
async function copyShareToken() {
|
||||
// The public read-URL for a draft lands with M10 (Publish-Hooks).
|
||||
// Until then we copy the bare token so the user has *something* to
|
||||
// keep — when the route goes live, the URL can be reconstructed as
|
||||
// `<origin>/share/writing/<token>`.
|
||||
if (!draft?.unlistedToken) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(draft.unlistedToken);
|
||||
shareCopied = true;
|
||||
if (shareCopyTimer) clearTimeout(shareCopyTimer);
|
||||
shareCopyTimer = setTimeout(() => (shareCopied = false), 2000);
|
||||
} catch {
|
||||
// Clipboard API can fail (e.g. insecure context). Swallow;
|
||||
// the user can still read the token from the DOM.
|
||||
}
|
||||
}
|
||||
|
||||
async function saveCheckpoint() {
|
||||
if (!draft || !currentVersion || saving) return;
|
||||
saving = true;
|
||||
|
|
@ -289,7 +315,23 @@
|
|||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="visibility-slot">
|
||||
<VisibilityPicker level={draft.visibility} onChange={onVisibilityChange} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if draft.visibility === 'unlisted' && draft.unlistedToken}
|
||||
<div
|
||||
class="share-row"
|
||||
title="Öffentlicher Link kommt mit M10 (Publish-Hooks). Bis dahin: Token kopieren."
|
||||
>
|
||||
<span class="share-label">🔗 Unlisted-Token:</span>
|
||||
<code class="share-token">{draft.unlistedToken}</code>
|
||||
<button type="button" class="tiny" onclick={copyShareToken}>
|
||||
{shareCopied ? '✓ Kopiert' : 'Kopieren'}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</header>
|
||||
|
||||
<section class="briefing-section">
|
||||
|
|
@ -483,6 +525,31 @@
|
|||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.visibility-slot {
|
||||
margin-left: auto;
|
||||
}
|
||||
.share-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--color-border, rgba(0, 0, 0, 0.08));
|
||||
background: var(--color-surface, rgba(0, 0, 0, 0.02));
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.share-label {
|
||||
color: var(--color-text-muted, rgba(0, 0, 0, 0.55));
|
||||
}
|
||||
.share-token {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 0.3rem;
|
||||
background: var(--color-surface-muted, rgba(0, 0, 0, 0.05));
|
||||
word-break: break-all;
|
||||
}
|
||||
.status-picker {
|
||||
display: inline-flex;
|
||||
gap: 0.25rem;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue