import { updateOwnProfile } from '@chat-app/shared/auth'; import type { PresenceState } from '@chat-app/shared/supabase'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useAuth } from '../context/AuthContext'; import { supabase } from '../lib/supabase'; import { Avatar } from './Avatar'; import { ChevronDownIcon } from './icons'; const STATUS_MAX = 128; const PRESENCE_OPTIONS: PresenceState[] = ['online', 'idle', 'dnd', 'invisible', 'offline']; const PRESENCE_DOT: Record = { online: 'bg-emerald-500', idle: 'bg-amber-400', dnd: 'bg-rose-500', invisible: 'bg-neutral-500', offline: 'bg-neutral-400 dark:bg-neutral-600', }; export function UserBar() { const { t } = useTranslation(['app']); const { profile, refreshProfile } = useAuth(); const [open, setOpen] = useState(false); const [busy, setBusy] = useState(false); const presence = profile?.presenceState ?? 'offline'; const persistedStatus = profile?.statusMessage ?? ''; const [statusDraft, setStatusDraft] = useState(persistedStatus); // Re-sync local draft with the server when the profile refreshes (e.g. after // a successful save) — without this the input would forget any incoming // updates from another device. useEffect(() => { setStatusDraft(persistedStatus); }, [persistedStatus]); // Subtitle priority: custom status when online and set, else label, else "Offline". const subtitle = presence === 'offline' ? t('app:presence.offline') : persistedStatus.trim().length > 0 ? persistedStatus.trim() : t('app:presence.' + presence); async function changePresence(next: PresenceState) { if (busy || next === presence) { setOpen(false); return; } setBusy(true); try { await updateOwnProfile(supabase, { presenceState: next }); await refreshProfile(); } catch (err: unknown) { console.error('updatePresence failed', err); } finally { setBusy(false); setOpen(false); } } async function saveStatus() { const next = statusDraft.trim().slice(0, STATUS_MAX); if (next === persistedStatus) return; setBusy(true); try { await updateOwnProfile(supabase, { statusMessage: next.length === 0 ? null : next }); await refreshProfile(); } catch (err: unknown) { console.error('updateStatusMessage failed', err); } finally { setBusy(false); } } return (
{open && (
setStatusDraft(e.target.value.slice(0, STATUS_MAX))} onBlur={() => void saveStatus()} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); void saveStatus(); setOpen(false); } if (e.key === 'Escape') { setStatusDraft(persistedStatus); setOpen(false); } }} placeholder={t('app:presence.status_placeholder', { defaultValue: 'Status setzen…', })} maxLength={STATUS_MAX} className="w-full rounded-md border border-line bg-surface-2 px-2 py-1.5 text-sm text-fg placeholder-fg-muted outline-none focus:border-accent dark:bg-[#383a40]" />
{PRESENCE_OPTIONS.map((opt) => ( ))}
)}
); }