import { useEffect, useRef, useState } from 'react' import { Copy, FilePlus2, Loader2, LogOut } from 'lucide-react' import { listRecords, type RepoRecord } from '../lib/atproto' import { MAP_NSID, type MapRecord } from '../lexicon/map' import { publishMap } from '../lib/atprotoWrite' import { saveMapLocal } from '../store/mapPersistence' import { useMapStore } from '../store/mapStore' import { useAuthStore } from '../store/authStore' import HandleAutocomplete from './HandleAutocomplete' import { Avatar, AvatarFallback } from '@/components/ui/avatar' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog' type MapRepoRecord = RepoRecord /** "Save As": publish the active map to the user's atproto repo. Logged out, it * prompts for a handle and runs an OAuth popup login. Logged in, it lists the * user's existing maps so they can overwrite one, or create a new record. The * map analog of {@link "./SaveAsDialog"}. */ export default function MapSaveAsDialog() { const status = useAuthStore((s) => s.status) const did = useAuthStore((s) => s.did) const handle = useAuthStore((s) => s.handle) const agent = useAuthStore((s) => s.agent) const login = useAuthStore((s) => s.login) const logout = useAuthStore((s) => s.logout) const authError = useAuthStore((s) => s.error) const name = useMapStore((s) => s.name) const setSource = useMapStore((s) => s.setSource) const [open, setOpen] = useState(false) const [loginHandle, setLoginHandle] = useState('') const [loggingIn, setLoggingIn] = useState(false) const [records, setRecords] = useState(null) const [listing, setListing] = useState(false) const [listError, setListError] = useState(null) /** rkey being written, '__new__' for a new record, or null when idle. */ const [publishing, setPublishing] = useState(null) const [publishError, setPublishError] = useState(null) const ctrlRef = useRef(null) // Load the user's existing maps when the dialog is open and signed in. useEffect(() => { if (!open || status !== 'in' || !did) return const ctrl = new AbortController() ctrlRef.current = ctrl const run = async () => { setListing(true) setListError(null) setRecords(null) try { const res = await listRecords(did, MAP_NSID, ctrl.signal) if (!ctrl.signal.aborted) setRecords(res.records) } catch (err) { if (!ctrl.signal.aborted) setListError(err instanceof Error ? err.message : 'Could not list your maps.') } finally { if (!ctrl.signal.aborted) setListing(false) } } void run() return () => ctrl.abort() }, [open, status, did]) const onOpenChange = (next: boolean) => { setOpen(next) if (!next) { ctrlRef.current?.abort() setLoginHandle('') setRecords(null) setListError(null) setPublishError(null) setPublishing(null) } } const doLogin = async (h: string) => { if (!h.trim()) return setLoggingIn(true) try { await login(h) } catch { // error surfaced via authError } finally { setLoggingIn(false) } } /** Publish the active map: `rkey` overwrites, undefined creates a new record. */ const saveAs = async (rkey?: string) => { if (!agent || !did) return setPublishing(rkey ?? '__new__') setPublishError(null) try { const res = await publishMap({ agent, did, rkey }) setSource({ did, rkey: res.rkey }) await saveMapLocal() onOpenChange(false) } catch (err) { setPublishError(err instanceof Error ? err.message : 'Could not save the map.') } finally { setPublishing(null) } } const busy = publishing !== null return ( Save to atproto Publish “{name || 'Untitled'}” to your repo as a new record, or overwrite one of your existing maps. {status === 'loading' ? (

Checking your session…

) : status !== 'in' ? ( // ---- logged out: handle + login ----

Log in with your atproto account to save. A window will open to your PDS.

{authError &&

{authError}

}
) : ( // ---- logged in: identity + targets ----
{(handle || did || '?').slice(0, 1)} @{handle || did}
Or overwrite {listing ? (

Loading your maps…

) : listError ? (

{listError}

) : records && records.length === 0 ? (

No maps in your repo yet.

) : ( records && (
    {records.map((rec) => { const recName = (typeof rec.value?.name === 'string' && rec.value.name.trim()) || '(untitled)' return (
  • {recName} {rec.rkey}
  • ) })}
) )}
{publishError &&

{publishError}

}
)}
) }