import { defineStore } from 'pinia' import { ref } from 'vue' import { ApiError, useApi, type MeAuthor } from '@/composables/useApi' import type { Access } from '@/lib/permissions' const REFUSAL_REFRESH_GAP_MS = 5000 const STALE_AFTER_MS = 30000 export type SessionStatus = 'idle' | 'checking' | 'ready' | 'error' export interface Session { id?: number verified: boolean username?: string email?: string displayName: string access?: Access author?: MeAuthor } export const useSessionStore = defineStore('session', () => { const status = ref('idle') const session = ref(null) let pending: Promise | null = null let lastAnswerAt: number | null = null let lastAttemptAt: number | null = null function check() { if (pending) { return pending } status.value = 'checking' const { getMe } = useApi() pending = getMe() .then((result) => { session.value = result status.value = 'ready' lastAnswerAt = Date.now() }) .catch((error) => { if (error instanceof ApiError && error.status === 401) { session.value = null status.value = 'ready' return } status.value = 'error' }) .finally(() => { pending = null }) return pending } function retry() { status.value = 'idle' return check() } async function refresh() { const { getMe } = useApi() lastAttemptAt = Date.now() try { session.value = await getMe() lastAnswerAt = Date.now() } catch { return } } async function refreshAfterRefusal() { if (lastAttemptAt !== null && Date.now() - lastAttemptAt < REFUSAL_REFRESH_GAP_MS) { return } await refresh() } async function refreshIfStale() { if (!session.value || lastAnswerAt === null) { return } if (Date.now() - lastAnswerAt > STALE_AFTER_MS) { await refresh() } } function setAuthor(author: MeAuthor | undefined) { if (!session.value) { return } session.value = { ...session.value, author } } function clear() { session.value = null status.value = 'ready' } function setDisplayName(displayName: string | null | undefined) { if (session.value) { session.value = { ...session.value, displayName: displayName ?? '' } } } return { status, session, check, retry, refresh, refreshAfterRefusal, refreshIfStale, clear, setAuthor, setDisplayName, } }) export function refreshSessionWhenVisible() { document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') { void useSessionStore().refreshIfStale() } }) }