diff --git a/apps/mana/apps/web/src/lib/modules/places/queries.ts b/apps/mana/apps/web/src/lib/modules/places/queries.ts index 5b6a5f237..67ef91462 100644 --- a/apps/mana/apps/web/src/lib/modules/places/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/places/queries.ts @@ -24,6 +24,7 @@ export function toPlace(local: LocalPlace): Place { visitCount: local.visitCount ?? 0, lastVisitedAt: local.lastVisitedAt || null, tagIds: local.tagIds ?? [], + visibility: local.visibility ?? 'space', createdAt: local.createdAt ?? new Date().toISOString(), updatedAt: local.updatedAt ?? new Date().toISOString(), }; diff --git a/apps/mana/apps/web/src/lib/modules/places/stores/places.svelte.ts b/apps/mana/apps/web/src/lib/modules/places/stores/places.svelte.ts index 7af8af449..b2cc397b8 100644 --- a/apps/mana/apps/web/src/lib/modules/places/stores/places.svelte.ts +++ b/apps/mana/apps/web/src/lib/modules/places/stores/places.svelte.ts @@ -7,6 +7,13 @@ import { encryptRecord, decryptRecord } from '$lib/data/crypto'; import { emitDomainEvent } from '$lib/data/events'; +import { getActiveSpace } from '$lib/data/scope'; +import { getEffectiveUserId } from '$lib/data/current-user'; +import { + defaultVisibilityFor, + generateUnlistedToken, + type VisibilityLevel, +} from '@mana/shared-privacy'; import { createBlock } from '$lib/data/time-blocks/service'; import { placeTable } from '../collections'; import { toPlace } from '../queries'; @@ -33,6 +40,7 @@ export const placesStore = { isFavorite: false, isArchived: false, visitCount: 0, + visibility: defaultVisibilityFor(getActiveSpace()?.type), createdAt: now, updatedAt: now, }; @@ -134,4 +142,37 @@ export const placesStore = { visitCount: (local.visitCount ?? 0) + 1, }); }, + + /** + * Flip a place's visibility. Typical use: mark favourite cafes / + * running routes 'public' so a website-embed can list them. Emits + * cross-module VisibilityChanged. + */ + async setVisibility(id: string, next: VisibilityLevel) { + const existing = await placeTable.get(id); + if (!existing) throw new Error(`Place ${id} not found`); + const before: VisibilityLevel = existing.visibility ?? 'space'; + if (before === next) return; + + const now = new Date().toISOString(); + const patch: Partial = { + visibility: next, + visibilityChangedAt: now, + visibilityChangedBy: getEffectiveUserId(), + updatedAt: now, + }; + if (next === 'unlisted' && !existing.unlistedToken) { + patch.unlistedToken = generateUnlistedToken(); + } else if (next !== 'unlisted' && existing.unlistedToken) { + patch.unlistedToken = undefined; + } + await placeTable.update(id, patch); + + emitDomainEvent('VisibilityChanged', 'places', 'places', id, { + recordId: id, + collection: 'places', + before, + after: next, + }); + }, }; diff --git a/apps/mana/apps/web/src/lib/modules/places/types.ts b/apps/mana/apps/web/src/lib/modules/places/types.ts index b03d9814d..7f51b7619 100644 --- a/apps/mana/apps/web/src/lib/modules/places/types.ts +++ b/apps/mana/apps/web/src/lib/modules/places/types.ts @@ -3,6 +3,7 @@ */ import type { BaseRecord } from '@mana/local-store'; +import type { VisibilityLevel } from '@mana/shared-privacy'; export type PlaceCategory = 'home' | 'work' | 'food' | 'shopping' | 'transit' | 'leisure' | 'other'; @@ -18,6 +19,10 @@ export interface LocalPlace extends BaseRecord { visitCount?: number; lastVisitedAt?: string; tagIds?: string[]; + visibility?: VisibilityLevel; + visibilityChangedAt?: string; + visibilityChangedBy?: string; + unlistedToken?: string; } export interface LocalLocationLog extends BaseRecord { @@ -46,6 +51,7 @@ export interface Place { visitCount: number; lastVisitedAt: string | null; tagIds: string[]; + visibility: VisibilityLevel; createdAt: string; updatedAt: string; } diff --git a/apps/mana/apps/web/src/lib/modules/places/views/DetailView.svelte b/apps/mana/apps/web/src/lib/modules/places/views/DetailView.svelte index 70e2b7b04..ce992bea0 100644 --- a/apps/mana/apps/web/src/lib/modules/places/views/DetailView.svelte +++ b/apps/mana/apps/web/src/lib/modules/places/views/DetailView.svelte @@ -15,6 +15,7 @@ type GeocodingResult, } from '$lib/geocoding'; import { Star, MapPin, X, MagnifyingGlass, ArrowsClockwise } from '@mana/shared-icons'; + import { VisibilityPicker, type VisibilityLevel } from '@mana/shared-privacy'; import type { ViewProps } from '$lib/app-registry'; import type { LocalPlace, PlaceCategory, LocalLocationLog } from '../types'; import { useAllTags, getTagsByIds } from '@mana/shared-stores'; @@ -153,6 +154,10 @@ await saveField(); } + async function handleVisibilityChange(next: VisibilityLevel) { + await placesStore.setVisibility(placeId, next); + } + async function toggleFavorite() { await placesStore.toggleFavorite(placeId); } @@ -225,6 +230,11 @@ {/if}
+
+ Sichtbarkeit + +
+
Kategorie