mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 18:41:08 +02:00
refactor(visibility): M6.1 — drop legacy isPublic everywhere
Removes the deprecated `isPublic` field from picture, memoro, cards, presi, and uload. The unified `visibility` enum has been the source of truth since M3 (picture) / M6 (others) and the soft-fallback in queries was the last consumer of the legacy field. Killing it cleanly: - types: drops isPublic from LocalX + X interfaces, and from CreateDeckInput/UpdateDeckInput/UpdateDeckDto. - queries.ts: type converters now read `visibility ?? 'space'` (or 'private' for picture) without the isPublic fallback. Cards's `getPublicDecks` helper now filters on `visibility === 'public'`. - stores: createX no longer initializes isPublic; updateX no longer accepts/mirrors it; setVisibility no longer writes the mirror. - UI: cards CreateDeckModal drops the public-toggle (use the Picker in DetailView post-create); DeckCard + presi ListView + /cards/decks/[id] page badges read `visibility === 'public'`. - collections.ts: drops isPublic: false from seed rows. - embeds.ts: picture/memoro/cards/presi resolvers drop the isPublic fallback. Top comment updated to reference canEmbedOnWebsite as the canonical gate. Existing IndexedDB rows still carry the stale isPublic value but nothing reads it. No Dexie schema bump needed (field was never indexed). No data loss — visibility was mirrored on every flip during the soft-migrate window so all "public" intent has already propagated to the unified field. Closes M6.1 — picture/memoro/cards/presi/uload now have no legacy visibility flags. Events' isPublished/publicToken stays (orthogonal RSVP-snapshot system, not legacy). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0ff5030ad2
commit
303058d406
26 changed files with 32 additions and 142 deletions
|
|
@ -24,7 +24,6 @@ export const CARDS_GUEST_SEED = {
|
|||
description: 'Lerne Cards kennen mit diesen Beispiel-Karteikarten.',
|
||||
color: '#6366f1',
|
||||
cardCount: 3,
|
||||
isPublic: false,
|
||||
},
|
||||
],
|
||||
cards: [
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
let title = $state('');
|
||||
let description = $state('');
|
||||
let isPublic = $state(false);
|
||||
let color = $state(DEFAULT_COLOR);
|
||||
let submitting = $state(false);
|
||||
let selectedTagIds = $state<string[]>([]);
|
||||
|
|
@ -27,7 +26,6 @@
|
|||
const deck = await deckStore.createDeck({
|
||||
title: title.trim(),
|
||||
description: description.trim() || undefined,
|
||||
isPublic,
|
||||
});
|
||||
|
||||
submitting = false;
|
||||
|
|
@ -35,7 +33,6 @@
|
|||
if (deck) {
|
||||
title = '';
|
||||
description = '';
|
||||
isPublic = false;
|
||||
open = false;
|
||||
onClose?.();
|
||||
}
|
||||
|
|
@ -96,18 +93,6 @@
|
|||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="deck-public"
|
||||
bind:checked={isPublic}
|
||||
class="h-4 w-4 rounded border-border"
|
||||
/>
|
||||
<label for="deck-public" class="cursor-pointer text-sm text-foreground">
|
||||
Offentlich machen
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span class="mb-1 block text-sm font-medium text-foreground">Tags</span>
|
||||
<TagField
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<span>{deck.cardCount || 0} Karten</span>
|
||||
{#if deck.isPublic}
|
||||
{#if deck.visibility === 'public'}
|
||||
<span class="rounded-full bg-primary/10 px-2 py-0.5 text-xs text-primary">
|
||||
Öffentlich
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ export function toDeck(local: LocalDeck): Deck {
|
|||
title: local.name,
|
||||
description: local.description ?? undefined,
|
||||
color: local.color,
|
||||
isPublic: local.isPublic ?? local.visibility === 'public',
|
||||
visibility: local.visibility ?? (local.isPublic === true ? 'public' : 'space'),
|
||||
visibility: local.visibility ?? 'space',
|
||||
tags: [],
|
||||
cardCount: local.cardCount,
|
||||
createdAt: local.createdAt ?? new Date().toISOString(),
|
||||
|
|
@ -83,7 +82,7 @@ export function getDeckById(decks: Deck[], id: string): Deck | undefined {
|
|||
}
|
||||
|
||||
export function getPublicDecks(decks: Deck[]): Deck[] {
|
||||
return decks.filter((d) => d.isPublic);
|
||||
return decks.filter((d) => d.visibility === 'public');
|
||||
}
|
||||
|
||||
export function getCardCountForDeck(cards: Card[], deckId: string): number {
|
||||
|
|
|
|||
|
|
@ -28,17 +28,13 @@ export const deckStore = {
|
|||
async createDeck(input: CreateDeckInput): Promise<Deck | null> {
|
||||
error = null;
|
||||
try {
|
||||
const initialPublic = input.isPublic ?? false;
|
||||
const newLocal: LocalDeck = {
|
||||
id: crypto.randomUUID(),
|
||||
name: input.title,
|
||||
description: input.description ?? null,
|
||||
color: '#6366f1',
|
||||
cardCount: 0,
|
||||
isPublic: initialPublic,
|
||||
// Initialize the unified field too — if the create flow set
|
||||
// isPublic, mirror it as 'public'; otherwise space-default.
|
||||
visibility: initialPublic ? 'public' : defaultVisibilityFor(getActiveSpace()?.type),
|
||||
visibility: defaultVisibilityFor(getActiveSpace()?.type),
|
||||
};
|
||||
|
||||
const plaintextSnapshot = toDeck(newLocal);
|
||||
|
|
@ -59,12 +55,6 @@ export const deckStore = {
|
|||
const localUpdates: Partial<LocalDeck> = {};
|
||||
if (updates.title !== undefined) localUpdates.name = updates.title;
|
||||
if (updates.description !== undefined) localUpdates.description = updates.description;
|
||||
if (updates.isPublic !== undefined) {
|
||||
// Legacy callers still pass isPublic — mirror to visibility
|
||||
// so the unified field stays in sync until M6.1 hard-drop.
|
||||
localUpdates.isPublic = updates.isPublic;
|
||||
localUpdates.visibility = updates.isPublic ? 'public' : 'space';
|
||||
}
|
||||
|
||||
const diff: Partial<LocalDeck> = {
|
||||
...localUpdates,
|
||||
|
|
@ -79,20 +69,18 @@ export const deckStore = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Flip a deck's visibility. M6 soft-migration: writes both
|
||||
* `visibility` and the legacy `isPublic` mirror so the picker
|
||||
* coexists with the older "public" badge UI until M6.1 hard-drop.
|
||||
* Flip a deck's visibility. Public decks surface in the cards
|
||||
* embed-resolver on the user's website.
|
||||
*/
|
||||
async setVisibility(id: string, next: VisibilityLevel) {
|
||||
const existing = await cardDeckTable.get(id);
|
||||
if (!existing) throw new Error(`Deck ${id} not found`);
|
||||
const before: VisibilityLevel = existing.visibility ?? (existing.isPublic ? 'public' : 'space');
|
||||
const before: VisibilityLevel = existing.visibility ?? 'space';
|
||||
if (before === next) return;
|
||||
|
||||
const stamp = new Date().toISOString();
|
||||
await cardDeckTable.update(id, {
|
||||
visibility: next,
|
||||
isPublic: next === 'public',
|
||||
visibilityChangedAt: stamp,
|
||||
visibilityChangedBy: getEffectiveUserId(),
|
||||
updatedAt: stamp,
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ export interface LocalDeck extends BaseRecord {
|
|||
color: string;
|
||||
cardCount: number;
|
||||
lastStudied?: string | null;
|
||||
/** @deprecated Use `visibility`. Mirror kept until M6.1 hard-drop. */
|
||||
isPublic: boolean;
|
||||
visibility?: VisibilityLevel;
|
||||
visibilityChangedAt?: string;
|
||||
visibilityChangedBy?: string;
|
||||
|
|
@ -36,8 +34,6 @@ export interface Deck {
|
|||
title: string;
|
||||
description?: string;
|
||||
color: string;
|
||||
/** @deprecated Use `visibility`. */
|
||||
isPublic: boolean;
|
||||
visibility: VisibilityLevel;
|
||||
tags: string[];
|
||||
cardCount: number;
|
||||
|
|
@ -61,13 +57,11 @@ export interface Card {
|
|||
export interface CreateDeckInput {
|
||||
title: string;
|
||||
description?: string;
|
||||
isPublic?: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateDeckInput {
|
||||
title?: string;
|
||||
description?: string;
|
||||
isPublic?: boolean;
|
||||
}
|
||||
|
||||
export interface CreateCardInput {
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@
|
|||
<div class="prop-row">
|
||||
<span class="prop-label">Sichtbarkeit</span>
|
||||
<VisibilityPicker
|
||||
level={deck.visibility ?? (deck.isPublic ? 'public' : 'space')}
|
||||
level={deck.visibility ?? 'space'}
|
||||
onChange={(next: VisibilityLevel) => deckStore.setVisibility(deckId, next)}
|
||||
disabledLevels={['unlisted']}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ export const MEMORO_GUEST_SEED = {
|
|||
processingStatus: 'completed' as const,
|
||||
isArchived: false,
|
||||
isPinned: true,
|
||||
isPublic: false,
|
||||
blueprintId: null,
|
||||
language: 'de',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -35,12 +35,7 @@ export function toMemo(local: LocalMemo): Memo {
|
|||
processingStatus: local.processingStatus,
|
||||
isArchived: local.isArchived,
|
||||
isPinned: local.isPinned,
|
||||
isPublic: local.isPublic ?? local.visibility === 'public',
|
||||
// Soft-fallback during M6 soak: legacy rows use isPublic, new
|
||||
// writes set visibility directly. Keep both in sync via the
|
||||
// store's setVisibility/setPublic methods until M6.1 drops the
|
||||
// legacy field.
|
||||
visibility: local.visibility ?? (local.isPublic === true ? 'public' : 'space'),
|
||||
visibility: local.visibility ?? 'space',
|
||||
language: local.language,
|
||||
createdAt: local.createdAt ?? new Date().toISOString(),
|
||||
updatedAt: local.updatedAt ?? new Date().toISOString(),
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ export const memosStore = {
|
|||
processingStatus: data.processingStatus ?? (data.transcript ? 'completed' : 'pending'),
|
||||
isArchived: false,
|
||||
isPinned: false,
|
||||
isPublic: false,
|
||||
visibility: defaultVisibilityFor(getActiveSpace()?.type),
|
||||
blueprintId: data.blueprintId ?? null,
|
||||
language: data.language ?? null,
|
||||
|
|
@ -141,7 +140,7 @@ export const memosStore = {
|
|||
/** Update a memo's fields. */
|
||||
async update(
|
||||
id: string,
|
||||
data: Partial<Pick<LocalMemo, 'title' | 'intro' | 'transcript' | 'language' | 'isPublic'>>
|
||||
data: Partial<Pick<LocalMemo, 'title' | 'intro' | 'transcript' | 'language'>>
|
||||
) {
|
||||
const diff: Partial<LocalMemo> = {
|
||||
...data,
|
||||
|
|
@ -173,22 +172,18 @@ export const memosStore = {
|
|||
unarchive: (id: string) => memoArchive.unarchive(id),
|
||||
|
||||
/**
|
||||
* Flip a memo's visibility. M6 soft-migration: writes both
|
||||
* `visibility` and the legacy `isPublic` mirror so older readers
|
||||
* (search index, server snapshots) keep working until the M6.1
|
||||
* hard-drop. Public memos surface in the user's website embed
|
||||
* once a memoro embed-resolver lands.
|
||||
* Flip a memo's visibility. Public memos surface in the user's
|
||||
* website embed via the memoro embed-resolver.
|
||||
*/
|
||||
async setVisibility(id: string, next: VisibilityLevel) {
|
||||
const existing = await memoTable.get(id);
|
||||
if (!existing) throw new Error(`Memo ${id} not found`);
|
||||
const before: VisibilityLevel = existing.visibility ?? (existing.isPublic ? 'public' : 'space');
|
||||
const before: VisibilityLevel = existing.visibility ?? 'space';
|
||||
if (before === next) return;
|
||||
|
||||
const stamp = new Date().toISOString();
|
||||
await memoTable.update(id, {
|
||||
visibility: next,
|
||||
isPublic: next === 'public',
|
||||
visibilityChangedAt: stamp,
|
||||
visibilityChangedBy: getEffectiveUserId(),
|
||||
updatedAt: stamp,
|
||||
|
|
|
|||
|
|
@ -16,14 +16,6 @@ export interface LocalMemo extends BaseRecord {
|
|||
processingStatus: ProcessingStatus;
|
||||
isArchived: boolean;
|
||||
isPinned: boolean;
|
||||
/**
|
||||
* @deprecated Soft-migrating to unified `visibility`. Kept for the
|
||||
* soak window so the converter can fall back to `isPublic` for
|
||||
* legacy rows that haven't been touched since the M6 rollout.
|
||||
* Hard-drop once `visibility` has propagated to all rows.
|
||||
*/
|
||||
isPublic?: boolean;
|
||||
/** Unified visibility (M6 pilot — replaces isPublic). */
|
||||
visibility?: VisibilityLevel;
|
||||
visibilityChangedAt?: string;
|
||||
visibilityChangedBy?: string;
|
||||
|
|
@ -95,8 +87,6 @@ export interface Memo {
|
|||
processingStatus: ProcessingStatus;
|
||||
isArchived: boolean;
|
||||
isPinned: boolean;
|
||||
/** @deprecated Use `visibility`. Mirror kept until M6.1 hard-drop. */
|
||||
isPublic: boolean;
|
||||
visibility: VisibilityLevel;
|
||||
language: string | null;
|
||||
createdAt: string;
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@
|
|||
<div class="prop-row">
|
||||
<span class="prop-label">Sichtbarkeit</span>
|
||||
<VisibilityPicker
|
||||
level={memo.visibility ?? (memo.isPublic ? 'public' : 'space')}
|
||||
level={memo.visibility ?? 'space'}
|
||||
onChange={(next: VisibilityLevel) => memosStore.setVisibility(memoId, next)}
|
||||
disabledLevels={['unlisted']}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -38,9 +38,7 @@ export function toImage(local: LocalImage): Image {
|
|||
height: local.height ?? undefined,
|
||||
fileSize: local.fileSize ?? undefined,
|
||||
blurhash: local.blurhash ?? undefined,
|
||||
// Soft-migration fallback: rows written before M3 only have the
|
||||
// legacy `isPublic` flag; map it to the nearest visibility level.
|
||||
visibility: local.visibility ?? (local.isPublic === true ? 'public' : 'private'),
|
||||
visibility: local.visibility ?? 'private',
|
||||
isFavorite: local.isFavorite,
|
||||
downloadCount: local.downloadCount,
|
||||
rating: local.rating ?? undefined,
|
||||
|
|
@ -68,7 +66,7 @@ export function toBoard(local: LocalBoard): Board {
|
|||
canvasWidth: local.canvasWidth,
|
||||
canvasHeight: local.canvasHeight,
|
||||
backgroundColor: local.backgroundColor,
|
||||
visibility: local.visibility ?? (local.isPublic === true ? 'public' : 'private'),
|
||||
visibility: local.visibility ?? 'private',
|
||||
createdAt: local.createdAt ?? new Date().toISOString(),
|
||||
updatedAt: local.updatedAt ?? new Date().toISOString(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -190,8 +190,7 @@ export const boardsStore = {
|
|||
try {
|
||||
const existing = await db.table<LocalBoard>('boards').get(id);
|
||||
if (!existing) return { success: false, error: 'Board not found' };
|
||||
const before: VisibilityLevel =
|
||||
existing.visibility ?? (existing.isPublic === true ? 'public' : 'private');
|
||||
const before: VisibilityLevel = existing.visibility ?? 'private';
|
||||
if (before === next) return { success: true };
|
||||
|
||||
const now = new Date().toISOString();
|
||||
|
|
|
|||
|
|
@ -26,12 +26,6 @@ export interface LocalImage extends BaseRecord {
|
|||
height?: number | null;
|
||||
fileSize?: number | null;
|
||||
blurhash?: string | null;
|
||||
/**
|
||||
* @deprecated Use `visibility` instead. Kept for the soft-migration
|
||||
* window — will be dropped in the hard follow-up once no reader
|
||||
* references it. See docs/plans/visibility-system.md §M3.
|
||||
*/
|
||||
isPublic?: boolean;
|
||||
visibility?: VisibilityLevel;
|
||||
visibilityChangedAt?: string;
|
||||
visibilityChangedBy?: string;
|
||||
|
|
@ -95,11 +89,6 @@ export interface LocalBoard extends BaseRecord {
|
|||
canvasWidth: number;
|
||||
canvasHeight: number;
|
||||
backgroundColor: string;
|
||||
/**
|
||||
* @deprecated Use `visibility` instead. Kept during the M3 soft
|
||||
* migration — dropped in the hard follow-up.
|
||||
*/
|
||||
isPublic?: boolean;
|
||||
visibility?: VisibilityLevel;
|
||||
visibilityChangedAt?: string;
|
||||
visibilityChangedBy?: string;
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@
|
|||
<p class="truncate text-sm font-medium text-foreground">{deck.title}</p>
|
||||
<div class="mt-1 flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<span>{slideCount(deck.id)} Folien</span>
|
||||
{#if deck.isPublic}
|
||||
{#if deck.visibility === 'public'}
|
||||
<span class="rounded bg-muted px-1.5 py-0.5 text-[10px]">Öffentlich</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ export const PRESI_GUEST_SEED = {
|
|||
id: ONBOARDING_DECK_ID,
|
||||
title: 'Willkommen bei Presi',
|
||||
description: 'Eine kurze Einfuhrung in die Prasentations-App.',
|
||||
isPublic: false,
|
||||
},
|
||||
],
|
||||
slides: [
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ export function toDeck(local: LocalDeck): Deck {
|
|||
title: local.title,
|
||||
description: local.description ?? undefined,
|
||||
themeId: local.themeId ?? undefined,
|
||||
isPublic: local.isPublic ?? local.visibility === 'public',
|
||||
visibility: local.visibility ?? (local.isPublic === true ? 'public' : 'space'),
|
||||
visibility: local.visibility ?? 'space',
|
||||
createdAt: local.createdAt ?? new Date().toISOString(),
|
||||
updatedAt: local.updatedAt ?? new Date().toISOString(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ function createDecksStore() {
|
|||
title: dto.title,
|
||||
description: dto.description || null,
|
||||
themeId: dto.themeId || null,
|
||||
isPublic: false,
|
||||
visibility: defaultVisibilityFor(getActiveSpace()?.type),
|
||||
};
|
||||
const plaintextSnapshot = toDeck(newLocal);
|
||||
|
|
@ -65,11 +64,6 @@ function createDecksStore() {
|
|||
if (dto.title !== undefined) localUpdates.title = dto.title;
|
||||
if (dto.description !== undefined) localUpdates.description = dto.description;
|
||||
if (dto.themeId !== undefined) localUpdates.themeId = dto.themeId;
|
||||
if (dto.isPublic !== undefined) {
|
||||
// Mirror to unified visibility during M6 soak.
|
||||
localUpdates.isPublic = dto.isPublic;
|
||||
localUpdates.visibility = dto.isPublic ? 'public' : 'space';
|
||||
}
|
||||
|
||||
await encryptRecord('presiDecks', localUpdates);
|
||||
await presiDeckTable.update(id, localUpdates);
|
||||
|
|
@ -85,13 +79,11 @@ function createDecksStore() {
|
|||
try {
|
||||
const existing = await presiDeckTable.get(id);
|
||||
if (!existing) return false;
|
||||
const before: VisibilityLevel =
|
||||
existing.visibility ?? (existing.isPublic ? 'public' : 'space');
|
||||
const before: VisibilityLevel = existing.visibility ?? 'space';
|
||||
if (before === next) return true;
|
||||
const stamp = new Date().toISOString();
|
||||
await presiDeckTable.update(id, {
|
||||
visibility: next,
|
||||
isPublic: next === 'public',
|
||||
visibilityChangedAt: stamp,
|
||||
visibilityChangedBy: getEffectiveUserId(),
|
||||
updatedAt: stamp,
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ export interface LocalDeck extends BaseRecord {
|
|||
title: string;
|
||||
description?: string | null;
|
||||
themeId?: string | null;
|
||||
/** @deprecated Use `visibility`. Mirror kept until M6.1 hard-drop. */
|
||||
isPublic: boolean;
|
||||
visibility?: VisibilityLevel;
|
||||
visibilityChangedAt?: string;
|
||||
visibilityChangedBy?: string;
|
||||
|
|
@ -39,8 +37,6 @@ export interface Deck {
|
|||
title: string;
|
||||
description?: string;
|
||||
themeId?: string;
|
||||
/** @deprecated Use `visibility`. */
|
||||
isPublic: boolean;
|
||||
visibility: VisibilityLevel;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
|
|
@ -66,7 +62,6 @@ export interface UpdateDeckDto {
|
|||
title?: string;
|
||||
description?: string;
|
||||
themeId?: string;
|
||||
isPublic?: boolean;
|
||||
}
|
||||
|
||||
export interface CreateSlideDto {
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@
|
|||
<div class="prop-row">
|
||||
<span class="prop-label">Sichtbarkeit</span>
|
||||
<VisibilityPicker
|
||||
level={deck.visibility ?? (deck.isPublic ? 'public' : 'space')}
|
||||
level={deck.visibility ?? 'space'}
|
||||
onChange={(next: VisibilityLevel) => decksStore.setVisibility(deckId, next)}
|
||||
disabledLevels={['unlisted']}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ export const ULOAD_GUEST_SEED = {
|
|||
slug: 'social-media',
|
||||
color: '#8b5cf6',
|
||||
icon: null,
|
||||
isPublic: false,
|
||||
visibility: 'space',
|
||||
usageCount: 0,
|
||||
},
|
||||
|
|
@ -97,7 +96,6 @@ export const ULOAD_GUEST_SEED = {
|
|||
slug: 'dokumentation',
|
||||
color: '#3b82f6',
|
||||
icon: null,
|
||||
isPublic: false,
|
||||
visibility: 'space',
|
||||
usageCount: 0,
|
||||
},
|
||||
|
|
@ -107,7 +105,6 @@ export const ULOAD_GUEST_SEED = {
|
|||
slug: 'marketing',
|
||||
color: '#10b981',
|
||||
icon: null,
|
||||
isPublic: false,
|
||||
visibility: 'space',
|
||||
usageCount: 0,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -42,8 +42,6 @@ export interface Tag {
|
|||
slug: string;
|
||||
color?: string;
|
||||
icon?: string;
|
||||
/** @deprecated Use `visibility`. */
|
||||
isPublic: boolean;
|
||||
visibility: import('@mana/shared-privacy').VisibilityLevel;
|
||||
usageCount: number;
|
||||
createdAt: string;
|
||||
|
|
@ -106,8 +104,7 @@ export function toTag(local: LocalTag): Tag {
|
|||
slug: local.slug,
|
||||
color: local.color ?? undefined,
|
||||
icon: local.icon ?? undefined,
|
||||
isPublic: local.isPublic ?? local.visibility === 'public',
|
||||
visibility: local.visibility ?? (local.isPublic === true ? 'public' : 'space'),
|
||||
visibility: local.visibility ?? 'space',
|
||||
usageCount: local.usageCount,
|
||||
createdAt: local.createdAt ?? new Date().toISOString(),
|
||||
updatedAt: local.updatedAt ?? new Date().toISOString(),
|
||||
|
|
|
|||
|
|
@ -30,12 +30,6 @@ export interface LocalTag extends BaseRecord {
|
|||
slug: string;
|
||||
color?: string | null;
|
||||
icon?: string | null;
|
||||
/**
|
||||
* @deprecated Use `visibility`. Mirror kept for the M6 soak window.
|
||||
* No active CRUD UI yet — the field is set only by seed data and
|
||||
* the future tag-management view will write `visibility` directly.
|
||||
*/
|
||||
isPublic: boolean;
|
||||
visibility?: VisibilityLevel;
|
||||
usageCount: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import { formatDateTime } from '$lib/i18n/format';
|
|||
* content. Trade-off: publishes are slightly slower, public visits are
|
||||
* much faster.
|
||||
*
|
||||
* Every resolver MUST enforce the source's public-visibility rules —
|
||||
* e.g. `picture.board.isPublic === true`. An owner who embeds a
|
||||
* private board gets an empty result with a clear error message in the
|
||||
* resolved.error field.
|
||||
* Every resolver MUST enforce the source's public-visibility rules
|
||||
* via `canEmbedOnWebsite(visibility)`. An owner who embeds a
|
||||
* non-public record gets an empty result with a clear error message
|
||||
* in the resolved.error field.
|
||||
*/
|
||||
|
||||
import { db } from '$lib/data/database';
|
||||
|
|
@ -117,8 +117,7 @@ export async function resolveEmbed(props: ModuleEmbedProps): Promise<ResolvedEmb
|
|||
/**
|
||||
* Picture-board: returns image items for a board whose owner flipped
|
||||
* its visibility to 'public' via the VisibilityPicker. `canEmbedOnWebsite`
|
||||
* is the hard gate; the soft-migration fallback maps legacy `isPublic`
|
||||
* rows (pre-M3) to the right level.
|
||||
* is the hard gate.
|
||||
*/
|
||||
async function resolvePictureBoard(props: ModuleEmbedProps): Promise<EmbedItem[]> {
|
||||
if (!props.sourceId) {
|
||||
|
|
@ -134,8 +133,7 @@ async function resolvePictureBoard(props: ModuleEmbedProps): Promise<EmbedItem[]
|
|||
if (!rawBoard || rawBoard.deletedAt) {
|
||||
throw new Error('Board nicht gefunden');
|
||||
}
|
||||
const boardVisibility =
|
||||
rawBoard.visibility ?? (rawBoard.isPublic === true ? 'public' : 'private');
|
||||
const boardVisibility = rawBoard.visibility ?? 'private';
|
||||
if (!canEmbedOnWebsite(boardVisibility)) {
|
||||
throw new Error('Board ist nicht öffentlich — setze es im Picture-Modul auf "Öffentlich"');
|
||||
}
|
||||
|
|
@ -804,10 +802,7 @@ async function resolveSocialEvents(props: ModuleEmbedProps): Promise<EmbedItem[]
|
|||
async function resolveMemos(_props: ModuleEmbedProps): Promise<EmbedItem[]> {
|
||||
let memos = await db.table<LocalMemo>('memos').toArray();
|
||||
memos = memos.filter(
|
||||
(m) =>
|
||||
!m.deletedAt &&
|
||||
!m.isArchived &&
|
||||
canEmbedOnWebsite(m.visibility ?? (m.isPublic === true ? 'public' : 'private'))
|
||||
(m) => !m.deletedAt && !m.isArchived && canEmbedOnWebsite(m.visibility ?? 'private')
|
||||
);
|
||||
|
||||
if (memos.length === 0) return [];
|
||||
|
|
@ -853,11 +848,7 @@ async function resolveMemos(_props: ModuleEmbedProps): Promise<EmbedItem[]> {
|
|||
*/
|
||||
async function resolveCardDecks(_props: ModuleEmbedProps): Promise<EmbedItem[]> {
|
||||
let decks = await db.table<LocalCardDeck>('cardDecks').toArray();
|
||||
decks = decks.filter(
|
||||
(d) =>
|
||||
!d.deletedAt &&
|
||||
canEmbedOnWebsite(d.visibility ?? (d.isPublic === true ? 'public' : 'private'))
|
||||
);
|
||||
decks = decks.filter((d) => !d.deletedAt && canEmbedOnWebsite(d.visibility ?? 'private'));
|
||||
|
||||
if (decks.length === 0) return [];
|
||||
|
||||
|
|
@ -886,11 +877,7 @@ async function resolveCardDecks(_props: ModuleEmbedProps): Promise<EmbedItem[]>
|
|||
*/
|
||||
async function resolvePresiDecks(_props: ModuleEmbedProps): Promise<EmbedItem[]> {
|
||||
let decks = await db.table<LocalPresiDeck>('presiDecks').toArray();
|
||||
decks = decks.filter(
|
||||
(d) =>
|
||||
!d.deletedAt &&
|
||||
canEmbedOnWebsite(d.visibility ?? (d.isPublic === true ? 'public' : 'private'))
|
||||
);
|
||||
decks = decks.filter((d) => !d.deletedAt && canEmbedOnWebsite(d.visibility ?? 'private'));
|
||||
|
||||
if (decks.length === 0) return [];
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
{#if deck.isPublic}
|
||||
{#if deck.visibility === 'public'}
|
||||
<span class="rounded-full bg-primary/10 px-3 py-1 text-xs text-primary">
|
||||
Offentlich
|
||||
</span>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue