import { forwardRef, useLayoutEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { Dialogs } from "@wailsio/runtime"; import * as Popover from "@radix-ui/react-popover"; import * as ScrollArea from "@radix-ui/react-scroll-area"; import { Command } from "cmdk"; import { Check, ChevronDown, PlusCircle, Settings2, UserCircle } from "lucide-react"; import { pickProfileIcon } from "@/components/ProfileAvatar"; import type { Profile } from "@bindings/services/models.js"; import { NewProfileModal } from "@/components/NewProfileModal"; import { Tooltip } from "@/components/Tooltip"; import { useProfile } from "@/modules/profile/ProfileContext"; import { cn } from "@/lib/cn"; type ProfileDropdownProps = { onManageProfiles?: () => void; }; const ADD_VALUE = "__add_profile__"; const MANAGE_VALUE = "__manage_profiles__"; export const ProfileDropdown = ({ onManageProfiles }: ProfileDropdownProps) => { const { t } = useTranslation(); const { activeProfile, profiles, addProfile, switchProfile } = useProfile(); const [open, setOpen] = useState(false); const [newProfileOpen, setNewProfileOpen] = useState(false); const [busy, setBusy] = useState(false); const sortedProfiles = [...profiles].sort((a, b) => { if (a.name === activeProfile) return -1; if (b.name === activeProfile) return 1; return a.name.localeCompare(b.name); }); const guarded = async (title: string, fn: () => Promise) => { if (busy) return; setBusy(true); try { await fn(); } catch (e) { await Dialogs.Error({ Title: title, Message: e instanceof Error ? e.message : String(e), }); } finally { setBusy(false); } }; const handleSelect = (name: string) => { setOpen(false); if (name === activeProfile) return; void guarded(t("profile.error.switchTitle"), () => switchProfile(name)); }; const handleAdd = () => { setOpen(false); setNewProfileOpen(true); }; const handleManage = () => { setOpen(false); onManageProfiles?.(); }; const handleCreateProfile = async (name: string) => { try { await addProfile(name); } catch (e) { await Dialogs.Error({ Title: t("profile.error.createTitle"), Message: e instanceof Error ? e.message : String(e), }); } }; const displayName = activeProfile || t("profile.selector.loading"); return ( <> e.preventDefault()} className={cn( "z-50 min-w-64 overflow-hidden rounded-xl border border-nb-gray-900 bg-nb-gray-935 text-nb-gray-200 shadow-lg", "data-[state=open]:animate-in data-[state=closed]:animate-out", "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95", "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2", "data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", )} > e.stopPropagation()} > {sortedProfiles.length > 0 && ( <> {sortedProfiles.map((profile) => ( ))}
)}
{t("profile.dropdown.addProfile")} {t("profile.dropdown.manageProfiles")}
); }; type ProfileTriggerButtonProps = React.ButtonHTMLAttributes & { name: string; }; const ProfileTriggerButton = forwardRef( function ProfileTriggerButton({ name, className, ...props }, ref) { const Icon = pickProfileIcon(name) ?? UserCircle; return ( ); }, ); type ProfileRowProps = { profile: Profile; isActive: boolean; onSelect: (name: string) => void; }; const ProfileRow = ({ profile, isActive, onSelect }: ProfileRowProps) => { const showEmail = !!profile.email; const Icon = pickProfileIcon(profile.name) ?? UserCircle; return ( onSelect(profile.name)} className={cn( "flex gap-2 px-2 py-1.5 mx-1.5 my-0.5 w-auto", "rounded-md outline-none cursor-default text-sm", "data-[selected=true]:bg-nb-gray-900", showEmail ? "items-start" : "items-center", )} >
{profile.name} {showEmail && }
{isActive && ( )}
); }; const TruncatedEmail = ({ email }: { email: string }) => { const ref = useRef(null); const [overflowing, setOverflowing] = useState(false); useLayoutEffect(() => { const el = ref.current; if (!el) return; setOverflowing(el.scrollWidth > el.clientWidth); }, [email]); const span = ( {email} ); if (!overflowing) return span; return {span}; };