Files
arcadia-admin/app/lib/profile.ts
jules ffe3fc0473 profile: drop unused title/signature/defaultAgentId fields
These were aspirational placeholders — stored to localStorage but never
read by anything. Removed from the form, types, and persistence layer.
Local profile is now just the avatar URL mirror, which the appbar reads
before the server profile fetch resolves on mount.

Preferences card renamed to "Avatar" since that's all that's left.
Re-add server-backed if/when something actually consumes them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 09:54:24 +10:00

89 lines
2.2 KiB
TypeScript

// Local mirror of the resolved avatar URL, so the appbar can render the
// avatar before the profile fetch resolves on next mount. The real
// profile (name, email, bio, phone, location, timezone, avatar) is
// server-backed — see ~/lib/arcadia/profiles.ts.
import { useEffect, useSyncExternalStore } from "react"
export type Profile = {
avatarUrl: string
}
export const DEFAULT_PROFILE: Profile = {
avatarUrl: "",
}
const STORAGE_KEY = "crema.profile"
const CHANGE_EVENT = "crema:profile-change"
function readFromStorage(): Profile {
if (typeof window === "undefined") return DEFAULT_PROFILE
try {
const raw = localStorage.getItem(STORAGE_KEY)
if (!raw) return DEFAULT_PROFILE
const parsed = JSON.parse(raw) as Partial<Profile>
return {
avatarUrl:
typeof parsed.avatarUrl === "string"
? parsed.avatarUrl
: DEFAULT_PROFILE.avatarUrl,
}
} catch {
return DEFAULT_PROFILE
}
}
export function loadProfile(): Profile {
return readFromStorage()
}
export function saveProfile(next: Profile) {
if (typeof window === "undefined") return
localStorage.setItem(STORAGE_KEY, JSON.stringify(next))
window.dispatchEvent(new CustomEvent(CHANGE_EVENT))
}
export function resetProfile() {
saveProfile(DEFAULT_PROFILE)
}
export function profileInitials(name: string): string {
const words = name.trim().split(/\s+/).filter(Boolean)
if (words.length === 0) return "?"
if (words.length === 1) return words[0].slice(0, 2).toUpperCase()
return (words[0][0] + words[words.length - 1][0]).toUpperCase()
}
let cached: Profile | null = null
function subscribe(cb: () => void): () => void {
const onChange = () => {
cached = null
cb()
}
window.addEventListener(CHANGE_EVENT, onChange)
window.addEventListener("storage", (e) => {
if (e.key === STORAGE_KEY) onChange()
})
return () => {
window.removeEventListener(CHANGE_EVENT, onChange)
}
}
function getSnapshot(): Profile {
if (!cached) cached = readFromStorage()
return cached
}
function getServerSnapshot(): Profile {
return DEFAULT_PROFILE
}
export function useProfile(): Profile {
const value = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
useEffect(() => {
cached = null
}, [])
return value
}