2026-04-17 15:28:21 +02:00
|
|
|
import type { Profile } from '$lib/types';
|
2026-04-18 22:22:19 +02:00
|
|
|
import { alertAction } from '$lib/client/confirm.svelte';
|
2026-04-17 15:28:21 +02:00
|
|
|
|
|
|
|
|
const STORAGE_KEY = 'kochwas.activeProfileId';
|
|
|
|
|
|
|
|
|
|
function loadActiveId(): number | null {
|
|
|
|
|
if (typeof localStorage === 'undefined') return null;
|
|
|
|
|
const raw = localStorage.getItem(STORAGE_KEY);
|
|
|
|
|
if (!raw) return null;
|
|
|
|
|
const n = Number(raw);
|
|
|
|
|
return Number.isInteger(n) && n > 0 ? n : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function saveActiveId(id: number | null): void {
|
|
|
|
|
if (typeof localStorage === 'undefined') return;
|
|
|
|
|
if (id === null) localStorage.removeItem(STORAGE_KEY);
|
|
|
|
|
else localStorage.setItem(STORAGE_KEY, String(id));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ProfileStore {
|
|
|
|
|
profiles = $state<Profile[]>([]);
|
|
|
|
|
activeId = $state<number | null>(null);
|
|
|
|
|
loaded = $state(false);
|
|
|
|
|
|
|
|
|
|
active = $derived(this.profiles.find((p) => p.id === this.activeId) ?? null);
|
|
|
|
|
|
|
|
|
|
async load(): Promise<void> {
|
|
|
|
|
const res = await fetch('/api/profiles');
|
|
|
|
|
this.profiles = await res.json();
|
|
|
|
|
const stored = loadActiveId();
|
|
|
|
|
if (stored && this.profiles.some((p) => p.id === stored)) {
|
|
|
|
|
this.activeId = stored;
|
|
|
|
|
}
|
|
|
|
|
this.loaded = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
select(id: number): void {
|
|
|
|
|
this.activeId = id;
|
|
|
|
|
saveActiveId(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clear(): void {
|
|
|
|
|
this.activeId = null;
|
|
|
|
|
saveActiveId(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async create(name: string, avatar_emoji: string | null): Promise<Profile> {
|
|
|
|
|
const res = await fetch('/api/profiles', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'content-type': 'application/json' },
|
|
|
|
|
body: JSON.stringify({ name, avatar_emoji })
|
|
|
|
|
});
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
const err = await res.json().catch(() => ({ message: 'unknown error' }));
|
|
|
|
|
throw new Error(err.message ?? 'failed to create profile');
|
|
|
|
|
}
|
|
|
|
|
const p = (await res.json()) as Profile;
|
|
|
|
|
this.profiles = [...this.profiles, p].sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const profileStore = new ProfileStore();
|
2026-04-18 22:22:19 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the active profile, or null after showing the standard
|
|
|
|
|
* "kein Profil gewählt" dialog. Use as the first line of any per-profile
|
|
|
|
|
* action so we don't repeat the guard at every call-site.
|
2026-04-19 11:45:00 +02:00
|
|
|
*
|
|
|
|
|
* `message` ueberschreibt den Default, wenn eine Aktion einen spezifischen
|
|
|
|
|
* Hinweis braucht (z. B. „um mitzuwünschen" auf der Wunschliste).
|
2026-04-18 22:22:19 +02:00
|
|
|
*/
|
2026-04-19 11:45:00 +02:00
|
|
|
export async function requireProfile(
|
|
|
|
|
message = 'Tippe oben rechts auf „Profil wählen", dann klappt die Aktion.'
|
|
|
|
|
): Promise<Profile | null> {
|
2026-04-18 22:22:19 +02:00
|
|
|
if (profileStore.active) return profileStore.active;
|
2026-04-19 11:45:00 +02:00
|
|
|
await alertAction({ title: 'Kein Profil gewählt', message });
|
2026-04-18 22:22:19 +02:00
|
|
|
return null;
|
|
|
|
|
}
|