import { computed, ref } from "vue" import { getAuthor } from "@/modules/atproto/getAuthor" import { restoreSession, sdkSignOut, signInWithHandle } from "@/modules/atproto/service/atprotoOAuth" import { clearSession, loadSession, saveSession } from "@/modules/atproto/service/atprotoSession" const did = ref(null) const handle = ref(null) const avatarUrl = ref(null) let init = true const fetchAvatar = async (actorDid: string) => { try { const res = await fetch( `https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(actorDid)}` ) if (res.ok) { const data = await res.json() avatarUrl.value = data.avatar ?? null } } catch { avatarUrl.value = null } } const initializeAuth = async () => { // Load cached session from IndexedDB first (fast, local) so the UI can render immediately const stored = await loadSession() did.value = stored?.did ?? "" handle.value = stored?.handle ?? "" if (stored?.did) { fetchAvatar(stored.did) } // Then restore OAuth session in the background (may involve network) const session = await restoreSession() if (session) { const author = await getAuthor(session.did) const resolvedHandle = author?.handle ?? "" did.value = session.did handle.value = resolvedHandle await saveSession(session.did, resolvedHandle) fetchAvatar(session.did) window.history.replaceState( null, "", window.location.pathname + window.location.search ) } } export const useATProtoLogin = () => { if (init) { init = false initializeAuth() } const isLoggedIn = computed(() => !!did.value) const isATProtoReady = computed(() => did.value !== null) const signIn = async (inputHandle: string): Promise => { await signInWithHandle(inputHandle) } const signOut = async (): Promise => { if (did.value) { await sdkSignOut(did.value) } await clearSession() did.value = "" handle.value = "" avatarUrl.value = null } return { did, handle, avatarUrl, isLoggedIn, isATProtoReady, signIn, signOut } }