mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-23 17:01:29 +02:00
remove "Loading..." from profile dropdown and show skeleton
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
@@ -42,8 +43,13 @@ export const ProfileProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [activeProfile, setActiveProfile] = useState("");
|
||||
const [profiles, setProfiles] = useState<Profile[]>([]);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const retryRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
if (retryRef.current) {
|
||||
clearTimeout(retryRef.current);
|
||||
retryRef.current = null;
|
||||
}
|
||||
try {
|
||||
const u = await ProfilesSvc.Username();
|
||||
const [active, list] = await Promise.all([
|
||||
@@ -53,23 +59,28 @@ export const ProfileProvider = ({ children }: { children: ReactNode }) => {
|
||||
setUsername(u);
|
||||
setActiveProfile(active.profileName || "default");
|
||||
setProfiles(list);
|
||||
setLoaded(true);
|
||||
} catch (e) {
|
||||
// Daemon-down is already surfaced by DaemonUnavailableOverlay; swallow it here.
|
||||
const msg = e instanceof Error ? e.message : String(e);
|
||||
if (msg.includes("code = Unavailable")) {
|
||||
retryRef.current = setTimeout(() => {
|
||||
void refresh();
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
setLoaded(true);
|
||||
await errorDialog({
|
||||
Title: i18next.t("profile.error.loadTitle"),
|
||||
Message: formatErrorMessage(e),
|
||||
});
|
||||
} finally {
|
||||
setLoaded(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
refresh().catch((err: unknown) => console.error("[ProfileContext] refresh failed", err));
|
||||
return () => {
|
||||
if (retryRef.current) clearTimeout(retryRef.current);
|
||||
};
|
||||
}, [refresh]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -19,7 +19,7 @@ const MANAGE_VALUE = "__manage_profiles__";
|
||||
|
||||
export const ProfileDropdown = ({ onManageProfiles }: ProfileDropdownProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { activeProfile, profiles, switchProfile } = useProfile();
|
||||
const { activeProfile, profiles, switchProfile, loaded } = useProfile();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
@@ -55,12 +55,15 @@ export const ProfileDropdown = ({ onManageProfiles }: ProfileDropdownProps) => {
|
||||
onManageProfiles?.();
|
||||
};
|
||||
|
||||
const displayName = activeProfile || t("profile.selector.loading");
|
||||
if (!loaded) return <ProfileTriggerSkeleton />;
|
||||
|
||||
const hasProfile = !!activeProfile;
|
||||
const displayName = hasProfile ? activeProfile : t("profile.selector.noProfile");
|
||||
|
||||
return (
|
||||
<Popover.Root open={open} onOpenChange={setOpen}>
|
||||
<Popover.Trigger asChild className={"wails-no-draggable"}>
|
||||
<ProfileTriggerButton name={displayName} />
|
||||
<Popover.Trigger asChild className={"wails-no-draggable"} disabled={!hasProfile}>
|
||||
<ProfileTriggerButton name={displayName} disabled={!hasProfile} />
|
||||
</Popover.Trigger>
|
||||
<Popover.Portal>
|
||||
<Popover.Content
|
||||
@@ -134,6 +137,13 @@ export const ProfileDropdown = ({ onManageProfiles }: ProfileDropdownProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const ProfileTriggerSkeleton = () => (
|
||||
<div className="h-10 flex items-center gap-2 px-3 rounded-lg select-none wails-no-draggable">
|
||||
<div className="size-4 rounded-full bg-nb-gray-900 animate-pulse shrink-0" />
|
||||
<div className="h-4 w-24 rounded bg-nb-gray-900 animate-pulse" />
|
||||
</div>
|
||||
);
|
||||
|
||||
type ProfileTriggerButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||
name: string;
|
||||
};
|
||||
@@ -149,6 +159,7 @@ const ProfileTriggerButton = forwardRef<HTMLButtonElement, ProfileTriggerButtonP
|
||||
"h-10 flex items-center gap-2 px-3 rounded-lg outline-none cursor-default select-none wails-no-draggable",
|
||||
"text-nb-gray-200 hover:bg-nb-gray-900",
|
||||
"data-[state=open]:bg-nb-gray-900",
|
||||
"disabled:opacity-50 disabled:hover:bg-transparent",
|
||||
"transition-colors duration-150 wails-no-draggable",
|
||||
className,
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user