import { createSharedComposable } from '@vueuse/core' type ChangelogEntry = { hash: string shortHash: string subject: string authorName: string committedAt: string } type ChangelogSeenState = { lastOpenedAt: string | null latestSeenHash: string | null } const defaultSeenState = (): ChangelogSeenState => ({ lastOpenedAt: null, latestSeenHash: null }) let changelogRequest: Promise | null = null const _useChangelog = () => { const auth = useAuthStore() const entries = useState('changelog:entries', () => []) const pending = useState('changelog:pending', () => false) const error = useState('changelog:error', () => null) const loadedKey = useState('changelog:loaded-key', () => null) const seenState = useState('changelog:seen-state', defaultSeenState) const scopeKey = computed(() => { const userId = auth.user?.id const tenantId = auth.activeTenant if (!userId || !tenantId) return null return `${userId}:${tenantId}` }) const storageKey = computed(() => { if (!scopeKey.value) return null return `fedeo:changelog:last-opened:${scopeKey.value}` }) const latestEntry = computed(() => entries.value[0] || null) const hasUnread = computed(() => { if (!latestEntry.value?.hash) return false return latestEntry.value.hash !== seenState.value.latestSeenHash }) function loadSeenState() { if (!process.client || !storageKey.value) { seenState.value = defaultSeenState() return } try { const raw = localStorage.getItem(storageKey.value) if (!raw) { seenState.value = defaultSeenState() return } const parsed = JSON.parse(raw) seenState.value = { lastOpenedAt: parsed?.lastOpenedAt || null, latestSeenHash: parsed?.latestSeenHash || null } } catch (err) { console.error('Could not parse changelog seen state', err) seenState.value = defaultSeenState() } } async function refresh(force = false) { if (!process.client || !scopeKey.value) return if (!force && loadedKey.value === scopeKey.value && entries.value.length) return if (changelogRequest) return changelogRequest changelogRequest = (async () => { pending.value = true error.value = null try { const response = await useNuxtApp().$api('/api/functions/changelog', { query: { limit: 20 } }) entries.value = Array.isArray(response?.entries) ? response.entries : [] loadedKey.value = scopeKey.value } catch (err: any) { error.value = err?.data?.error || err?.message || 'Changelog konnte nicht geladen werden.' } finally { pending.value = false } })() try { await changelogRequest } finally { changelogRequest = null } } function markAsSeen() { if (!process.client || !storageKey.value) return const nextState = { lastOpenedAt: new Date().toISOString(), latestSeenHash: latestEntry.value?.hash || null } seenState.value = nextState try { localStorage.setItem(storageKey.value, JSON.stringify(nextState)) } catch (err) { console.error('Could not persist changelog seen state', err) } } watch(storageKey, () => { loadSeenState() }, { immediate: true }) watch(scopeKey, (nextScopeKey, previousScopeKey) => { if (!process.client || !nextScopeKey) return if (nextScopeKey !== previousScopeKey) { entries.value = [] loadedKey.value = null } void refresh(true) }, { immediate: true }) return { entries, pending, error, latestEntry, hasUnread, seenState, refresh, markAsSeen } } export const useChangelog = createSharedComposable(_useChangelog)