diff --git a/surfsense_web/components/layout/ui/icon-rail/SearchSpaceAvatar.tsx b/surfsense_web/components/layout/ui/icon-rail/SearchSpaceAvatar.tsx index 3459ebb3d..d807d5898 100644 --- a/surfsense_web/components/layout/ui/icon-rail/SearchSpaceAvatar.tsx +++ b/surfsense_web/components/layout/ui/icon-rail/SearchSpaceAvatar.tsx @@ -1,6 +1,7 @@ "use client"; import { Settings, Trash2, Users } from "lucide-react"; +import { useCallback, useRef, useState } from "react"; import { useTranslations } from "next-intl"; import { ContextMenu, @@ -9,6 +10,13 @@ import { ContextMenuSeparator, ContextMenuTrigger, } from "@/components/ui/context-menu"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; @@ -73,6 +81,35 @@ export function SearchSpaceAvatar({ const initials = getInitials(name); const sizeClasses = size === "sm" ? "h-8 w-8 text-xs" : "h-10 w-10 text-sm"; + // Long-press state for mobile + const [longPressMenuOpen, setLongPressMenuOpen] = useState(false); + const longPressTimer = useRef | null>(null); + const touchMoved = useRef(false); + + const handleTouchStart = useCallback(() => { + touchMoved.current = false; + longPressTimer.current = setTimeout(() => { + if (!touchMoved.current) { + setLongPressMenuOpen(true); + } + }, 500); + }, []); + + const handleTouchMove = useCallback(() => { + touchMoved.current = true; + if (longPressTimer.current) { + clearTimeout(longPressTimer.current); + longPressTimer.current = null; + } + }, []); + + const handleTouchEnd = useCallback(() => { + if (longPressTimer.current) { + clearTimeout(longPressTimer.current); + longPressTimer.current = null; + } + }, []); + const tooltipContent = (
{name} @@ -112,26 +149,67 @@ export function SearchSpaceAvatar({ ); + const menuItems = ( + <> + {onSettings && ( + + + {tCommon("settings")} + + )} + {onSettings && onDelete && } + {onDelete && isOwner && ( + + + {tCommon("delete")} + + )} + {onDelete && !isOwner && ( + + + {t("leave")} + + )} + + ); + // If delete or settings handlers are provided, wrap with context menu if (onDelete || onSettings) { + // Mobile: use long-press triggered DropdownMenu + if (disableTooltip) { + return ( + + +
+ {avatarButton} +
+
+ + {menuItems} + +
+ ); + } + + // Desktop: use right-click ContextMenu + Tooltip return ( - {disableTooltip ? ( - -
{avatarButton}
-
- ) : ( - - - -
{avatarButton}
-
-
- - {tooltipContent} - -
- )} + + + +
{avatarButton}
+
+
+ + {tooltipContent} + +
{onSettings && ( diff --git a/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx index 353788c59..84784d71b 100644 --- a/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx @@ -594,20 +594,15 @@ export function InboxSidebar({ {/* Mobile: Button that opens bottom drawer */} {isMobile ? ( <> - - - - - {t("filter") || "Filter"} - + )} + {isMobile ? ( + + ) : ( + ) : ( + ) : ( + ) : (