From 2513e8454f240caa315b68dd6738b79838eeeecc Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 13 Aug 2026 11:44:10 +0200 Subject: [PATCH] fix: open user menu instantly (#6568) --- .../components/layout/header/user-client.tsx | 91 +++---------------- .../src/components/user-avatar-menu.tsx | 12 +-- 2 files changed, 13 insertions(+), 90 deletions(-) diff --git a/apps/nextjs/src/components/layout/header/user-client.tsx b/apps/nextjs/src/components/layout/header/user-client.tsx index f6c66cbf7..952354db8 100644 --- a/apps/nextjs/src/components/layout/header/user-client.tsx +++ b/apps/nextjs/src/components/layout/header/user-client.tsx @@ -1,38 +1,17 @@ "use client"; -import type { ComponentProps, ReactNode } from "react"; -import { Suspense, use, useEffect, useState } from "react"; -import { Box, Loader, UnstyledButton, useMantineColorScheme } from "@mantine/core"; +import type { ReactNode } from "react"; +import { useEffect, useState } from "react"; +import { UnstyledButton, useMantineColorScheme } from "@mantine/core"; import { useHotkeys } from "@mantine/hooks"; -import { ErrorBoundary } from "react-error-boundary"; import { clientApi } from "@homarr/api/client"; import { useSession } from "@homarr/auth/client"; import { hotkeys } from "@homarr/definitions"; +import { UserAvatarMenu } from "~/components/user-avatar-menu"; import { UpdateIndicator } from "./update"; -type UserAvatarMenuModule = typeof import("~/components/user-avatar-menu"); -type UserAvatarMenuProps = ComponentProps; - -let userAvatarMenuPromise: Promise | undefined; -const loadUserAvatarMenu = () => { - if (userAvatarMenuPromise) return userAvatarMenuPromise; - - const promise = import("~/components/user-avatar-menu"); - userAvatarMenuPromise = promise; - void promise.catch(() => { - if (userAvatarMenuPromise === promise) userAvatarMenuPromise = undefined; - }); - return promise; -}; -const preloadUserAvatarMenu = () => void loadUserAvatarMenu().catch(() => undefined); - -const LazyUserAvatarMenu = (props: UserAvatarMenuProps) => { - const { UserAvatarMenu } = use(loadUserAvatarMenu()); - return ; -}; - interface UserButtonClientProps { avatar: ReactNode; isAdmin: boolean; @@ -41,8 +20,6 @@ interface UserButtonClientProps { export const UserButtonClient = ({ avatar, isAdmin, isDockerEnabled }: UserButtonClientProps) => { const [canCheckForUpdates, setCanCheckForUpdates] = useState(false); - const [isMenuMounted, setIsMenuMounted] = useState(false); - const [isMenuOpen, setIsMenuOpen] = useState(false); const session = useSession(); const { toggleColorScheme } = useMantineColorScheme(); useHotkeys([[hotkeys.toggleColorScheme, toggleColorScheme]]); @@ -66,60 +43,14 @@ export const UserButtonClient = ({ avatar, isAdmin, isDockerEnabled }: UserButto staleTime: 60 * 60 * 1_000, }); const visibleUpdates = isCurrentSessionAdmin ? availableUpdates : undefined; - const openMenu = () => { - setIsMenuMounted(true); - setIsMenuOpen(true); - }; - const renderButton = (onClick: () => void, isLoading = false) => ( - - - - {avatar} - {isLoading && ( - - )} - - - - ); - const button = renderButton(openMenu); - const loadingButton = renderButton(() => undefined, true); - - if (!isMenuMounted) return button; return ( - - renderButton(() => { - resetErrorBoundary(); - openMenu(); - }) - } - > - - - {button} - - - + + + + {avatar} + + + ); }; diff --git a/apps/nextjs/src/components/user-avatar-menu.tsx b/apps/nextjs/src/components/user-avatar-menu.tsx index 2cfed0b5b..9b2e34fea 100644 --- a/apps/nextjs/src/components/user-avatar-menu.tsx +++ b/apps/nextjs/src/components/user-avatar-menu.tsx @@ -21,17 +21,9 @@ interface UserAvatarMenuProps { children: ReactNode; availableUpdates?: RouterOutputs["updateChecker"]["getAvailableUpdates"]; isDockerEnabled?: boolean; - opened: boolean; - onOpenChange: (opened: boolean) => void; } -export const UserAvatarMenu = ({ - children, - availableUpdates, - isDockerEnabled, - opened, - onOpenChange, -}: UserAvatarMenuProps) => { +export const UserAvatarMenu = ({ children, availableUpdates, isDockerEnabled }: UserAvatarMenuProps) => { const t = useScopedI18n("common.userAvatar.menu"); const session = useSession(); @@ -48,7 +40,7 @@ export const UserAvatarMenu = ({ return ( // We use keepMounted so we can add event listeners to prevent navigating away without saving the board - + }>