-
Notifications
You must be signed in to change notification settings - Fork 898
feat (desktop): Scroll to bottom + org at bottom #709
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
daa5188
3a98429
a50f379
c27168e
795911e
3dd2457
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,22 +6,29 @@ import { | |
| DropdownMenuItem, | ||
| DropdownMenuLabel, | ||
| DropdownMenuSeparator, | ||
| DropdownMenuShortcut, | ||
| DropdownMenuSub, | ||
| DropdownMenuSubContent, | ||
| DropdownMenuSubTrigger, | ||
| DropdownMenuTrigger, | ||
| } from "@superset/ui/dropdown-menu"; | ||
| import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; | ||
| import { useLiveQuery } from "@tanstack/react-db"; | ||
| import { FaDiscord, FaXTwitter } from "react-icons/fa6"; | ||
| import { | ||
| HiCheck, | ||
| HiChevronUpDown, | ||
| HiOutlineArrowRightOnRectangle, | ||
| HiOutlineCog6Tooth, | ||
| HiOutlineCommandLine, | ||
| HiOutlineEnvelope, | ||
| HiOutlineUserGroup, | ||
| } from "react-icons/hi2"; | ||
| import { useAuth } from "renderer/contexts/AuthProvider"; | ||
| import { useCollections } from "renderer/contexts/CollectionsProvider"; | ||
| import { trpc } from "renderer/lib/trpc"; | ||
| import { useOpenSettings } from "renderer/stores/app-state"; | ||
| import { useHotkeyText } from "renderer/stores/hotkeys/store"; | ||
|
|
||
| interface OrganizationDropdownProps { | ||
| isCollapsed?: boolean; | ||
|
|
@@ -34,7 +41,9 @@ export function OrganizationDropdown({ | |
| const collections = useCollections(); | ||
| const setActiveOrg = trpc.auth.setActiveOrganization.useMutation(); | ||
| const signOut = trpc.auth.signOut.useMutation(); | ||
| const openUrl = trpc.external.openUrl.useMutation(); | ||
| const openSettings = useOpenSettings(); | ||
| const hotkeysShortcut = useHotkeyText("SHOW_HOTKEYS"); | ||
|
|
||
| const activeOrganizationId = session?.session?.activeOrganizationId; | ||
|
|
||
|
|
@@ -102,13 +111,30 @@ export function OrganizationDropdown({ | |
| {activeOrganization && ( | ||
| <> | ||
| {/* Settings */} | ||
| <DropdownMenuItem onSelect={() => openSettings()}> | ||
| <DropdownMenuItem onSelect={() => openSettings()} className="gap-2"> | ||
| <HiOutlineCog6Tooth className="h-4 w-4" /> | ||
| <span>Settings</span> | ||
| </DropdownMenuItem> | ||
|
|
||
| {/* Team management */} | ||
| <DropdownMenuItem onSelect={() => openSettings("team")}> | ||
| <span>Invite and manage members</span> | ||
| <DropdownMenuItem | ||
| onSelect={() => openSettings("team")} | ||
| className="gap-2" | ||
| > | ||
| <HiOutlineUserGroup className="h-4 w-4" /> | ||
| <span>Team</span> | ||
| </DropdownMenuItem> | ||
|
|
||
| {/* Hotkeys */} | ||
| <DropdownMenuItem | ||
| onSelect={() => openSettings("keyboard")} | ||
| className="gap-2" | ||
| > | ||
| <HiOutlineCommandLine className="h-4 w-4" /> | ||
| <span className="flex-1">Hotkeys</span> | ||
| {hotkeysShortcut !== "Unassigned" && ( | ||
| <DropdownMenuShortcut>{hotkeysShortcut}</DropdownMenuShortcut> | ||
| )} | ||
| </DropdownMenuItem> | ||
|
|
||
| <DropdownMenuSeparator /> | ||
|
|
@@ -153,6 +179,39 @@ export function OrganizationDropdown({ | |
| </> | ||
| )} | ||
|
|
||
| {/* Contact Us */} | ||
| <DropdownMenuSub> | ||
| <DropdownMenuSubTrigger className="gap-2"> | ||
| <HiOutlineEnvelope className="h-4 w-4" /> | ||
| <span>Contact us</span> | ||
| </DropdownMenuSubTrigger> | ||
| <DropdownMenuSubContent> | ||
| <DropdownMenuItem | ||
| onSelect={() => openUrl.mutate("https://discord.gg/superset")} | ||
| className="gap-2" | ||
| > | ||
| <FaDiscord className="h-4 w-4" /> | ||
| <span>Discord</span> | ||
| </DropdownMenuItem> | ||
| <DropdownMenuItem | ||
| onSelect={() => openUrl.mutate("mailto:founders@superset.sh")} | ||
| className="gap-2" | ||
| > | ||
| <HiOutlineEnvelope className="h-4 w-4" /> | ||
| <span>Email founders</span> | ||
| </DropdownMenuItem> | ||
| <DropdownMenuItem | ||
| onSelect={() => openUrl.mutate("https://x.com/supersetsh")} | ||
| className="gap-2" | ||
| > | ||
| <FaXTwitter className="h-4 w-4" /> | ||
| <span>X (Twitter)</span> | ||
| </DropdownMenuItem> | ||
| </DropdownMenuSubContent> | ||
| </DropdownMenuSub> | ||
|
Comment on lines
+182
to
+211
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's examine the target file to understand the context
cat -n "apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/OrganizationDropdown.tsx" | head -250Repository: superset-sh/superset Length of output: 8770 🏁 Script executed: # Find where openUrl is defined as a tRPC handler
fd . apps/desktop --type f --name "*.ts" --o --name "*.tsx" | xargs rg -l "openUrl" | head -20Repository: superset-sh/superset Length of output: 863 🏁 Script executed: # Search for tRPC external handlers/setup
rg "trpc\.external\." apps/desktop/src --type ts --type tsx -A 5 -B 5Repository: superset-sh/superset Length of output: 91 🏁 Script executed: # Check for URL validation or allowlist patterns in the codebase
rg "allowlist|allow.*list|validateUrl|scheme.*check" apps/desktop/src --type ts --type tsx -A 3Repository: superset-sh/superset Length of output: 91 🏁 Script executed: cat -n "apps/desktop/src/lib/trpc/routers/external/index.ts"Repository: superset-sh/superset Length of output: 3270 🏁 Script executed: # Search for shell.openExternal usage and any validation
rg "shell\.openExternal|openExternal" apps/desktop/src --type ts -B 5 -A 10Repository: superset-sh/superset Length of output: 7788 🏁 Script executed: # Check if there are any URL validation utilities or patterns
rg "url|URL|scheme|protocol" apps/desktop/src/lib/trpc/routers/external -B 2 -A 5Repository: superset-sh/superset Length of output: 5370 🏁 Script executed: rg "COMPANY\." apps/desktop/src --type ts -B 2 -A 2 | head -50Repository: superset-sh/superset Length of output: 4103 🏁 Script executed: # Find where COMPANY is defined
fd . apps/desktop/src -name "constants*" -o -name "config*" | xargs cat 2>/dev/null | grep -A 10 -B 2 "COMPANY\|DISCORD\|MAIL"Repository: superset-sh/superset Length of output: 294 🏁 Script executed: # Search for COMPANY definition
rg "^const COMPANY|export.*COMPANY" apps/desktop/src --type ts -A 10Repository: superset-sh/superset Length of output: 46 🏁 Script executed: rg "export const COMPANY\s*=" apps/desktop --type ts -A 20Repository: superset-sh/superset Length of output: 46 🏁 Script executed: # Search more broadly for COMPANY definition
rg "COMPANY\s*=" apps/desktop --type ts | head -5Repository: superset-sh/superset Length of output: 46 🏁 Script executed: # Find config or constants files in main and renderer
find apps/desktop/src -type f \( -name "config.ts" -o -name "constants.ts" -o -name "company.ts" \)Repository: superset-sh/superset Length of output: 476 🏁 Script executed: cat -n apps/desktop/src/shared/constants.tsRepository: superset-sh/superset Length of output: 2126 🏁 Script executed: # Also check if COMPANY is imported from somewhere
rg "import.*COMPANY|from.*constants" apps/desktop/src/main/lib/menu.ts apps/desktop/src/renderer/screens/main/components/TopBar/SupportMenu.tsxRepository: superset-sh/superset Length of output: 278 🏁 Script executed: cat -n $(find apps -name "constants.ts" -path "*/shared/*")Repository: superset-sh/superset Length of output: 2126 🏁 Script executed: # Check the shared constants more directly
find . -path "*/shared/constants*" -type fRepository: superset-sh/superset Length of output: 156 🏁 Script executed: rg "COMPANY" apps/desktop/src/shared/constants.tsRepository: superset-sh/superset Length of output: 46 🏁 Script executed: # Check the entire shared/constants directory
ls -la apps/desktop/src/shared/constants/Repository: superset-sh/superset Length of output: 238 🏁 Script executed: # If it's in a different file, search across shared
rg "export.*COMPANY" apps/desktop/src/shared --type ts -A 15Repository: superset-sh/superset Length of output: 46 🏁 Script executed: # Check tsconfig to understand the @superset/shared alias
rg "@superset/shared" apps/desktop/tsconfig.jsonRepository: superset-sh/superset Length of output: 46 🏁 Script executed: # Find where COMPANY is actually defined by searching all files
rg "COMPANY\s*:" apps/desktop/src --type ts | head -20Repository: superset-sh/superset Length of output: 46 🏁 Script executed: # Try a broader search in apps folder
rg "COMPANY.*DISCORD\|DISCORD.*superset" apps --type ts -B 5 | head -40Repository: superset-sh/superset Length of output: 46 🏁 Script executed: # Look at tsconfig to understand path aliases
cat apps/desktop/tsconfig.json | grep -A 5 '"@superset'Repository: superset-sh/superset Length of output: 211 🏁 Script executed: # Search in packages folder for COMPANY
rg "COMPANY" packages --type ts | head -10Repository: superset-sh/superset Length of output: 524 🏁 Script executed: # Check if COMPANY is defined inline in the importing files
cat -n apps/desktop/src/main/lib/menu.ts | head -100Repository: superset-sh/superset Length of output: 3186 🏁 Script executed: cat -n packages/shared/src/constants.ts | grep -A 30 "export const COMPANY"Repository: superset-sh/superset Length of output: 1319 🏁 Script executed: # Verify the specific URLs being used in OrganizationDropdown vs COMPANY constants
cat -n apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/OrganizationDropdown.tsx | grep -E "discord|founders|x\.com"Repository: superset-sh/superset Length of output: 333 🏁 Script executed: # Check if there are other usages of these inconsistent URLs
rg "discord\.gg/superset" apps/desktop/src --type tsRepository: superset-sh/superset Length of output: 248 🏁 Script executed: # Verify the Electron security practices - check what Electron docs recommend
web_search
Electron shell.openExternal security best practices URL validation allowlistRepository: superset-sh/superset Length of output: 161 🌐 Web query:
💡 Result: Key practices to safely use shell.openExternal in Electron
Minimal example (main process, pseudo-JS)
Citations
Add URL allowlist validation to The |
||
|
|
||
| <DropdownMenuSeparator /> | ||
|
|
||
| {/* Sign out - ALWAYS show so users can never get trapped */} | ||
| <DropdownMenuItem onSelect={handleSignOut} className="gap-2"> | ||
| <HiOutlineArrowRightOnRectangle className="h-4 w-4" /> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t silently ignore
openUrlfailures; add pending/disabled + contextual error logging. (Guideline: “Never swallow errors silently”)Right now
openUrl.mutate(...)has no local handling, so failures can be invisible and users can spam-click items while pending.Proposed patch
export function OrganizationDropdown({ isCollapsed = false, }: OrganizationDropdownProps) { @@ const signOut = trpc.auth.signOut.useMutation(); const openUrl = trpc.external.openUrl.useMutation(); @@ const hotkeysShortcut = useHotkeyText("SHOW_HOTKEYS"); + + type OpenUrlInput = Parameters<typeof openUrl.mutateAsync>[0]; + const handleOpenUrl = async (input: OpenUrlInput) => { + try { + await openUrl.mutateAsync(input); + } catch (err) { + console.error("[external/openUrl] failed to open url", err); + } + }; @@ <DropdownMenuItem - onSelect={() => openUrl.mutate("https://discord.gg/superset")} + onSelect={() => handleOpenUrl("https://discord.gg/superset" as OpenUrlInput)} className="gap-2" + disabled={openUrl.isPending} > @@ <DropdownMenuItem - onSelect={() => openUrl.mutate("mailto:founders@superset.sh")} + onSelect={() => handleOpenUrl("mailto:founders@superset.sh" as OpenUrlInput)} className="gap-2" + disabled={openUrl.isPending} > @@ <DropdownMenuItem - onSelect={() => openUrl.mutate("https://x.com/supersetsh")} + onSelect={() => handleOpenUrl("https://x.com/supersetsh" as OpenUrlInput)} className="gap-2" + disabled={openUrl.isPending} >Also applies to: 189-209