From d745e91f8060053ee5e5eca598598b0a7b2fabf1 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Wed, 8 Jan 2025 06:42:32 -0800 Subject: [PATCH] Backspace to delete selected in sidebar --- src-web/components/CommandPaletteDialog.tsx | 42 +++++++++---------- src-web/components/Sidebar.tsx | 12 ++++++ src-web/components/SidebarItemContextMenu.tsx | 8 ++-- ...eteRequest.tsx => useDeleteAnyRequest.tsx} | 8 ++-- src-web/hooks/useHotKey.ts | 9 +++- 5 files changed, 49 insertions(+), 30 deletions(-) rename src-web/hooks/{useDeleteRequest.tsx => useDeleteAnyRequest.tsx} (75%) diff --git a/src-web/components/CommandPaletteDialog.tsx b/src-web/components/CommandPaletteDialog.tsx index ccb69840..e8d9196b 100644 --- a/src-web/components/CommandPaletteDialog.tsx +++ b/src-web/components/CommandPaletteDialog.tsx @@ -10,7 +10,7 @@ import { useCreateGrpcRequest } from '../hooks/useCreateGrpcRequest'; import { useCreateHttpRequest } from '../hooks/useCreateHttpRequest'; import { useCreateWorkspace } from '../hooks/useCreateWorkspace'; import { useDebouncedState } from '../hooks/useDebouncedState'; -import { useDeleteRequest } from '../hooks/useDeleteRequest'; +import { useDeleteAnyRequest } from '../hooks/useDeleteAnyRequest'; import { useEnvironments } from '../hooks/useEnvironments'; import type { HotkeyAction } from '../hooks/useHotKey'; import { useHotKey } from '../hooks/useHotKey'; @@ -66,18 +66,18 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { const recentWorkspaces = useRecentWorkspaces(); const requests = useRequests(); const activeRequest = useActiveRequest(); - const [recentRequests] = useRecentRequests(); - const openWorkspace = useOpenWorkspace(); - const createHttpRequest = useCreateHttpRequest(); const activeCookieJar = useActiveCookieJar(); - const createGrpcRequest = useCreateGrpcRequest(); - const createEnvironment = useCreateEnvironment(); - const sendRequest = useSendAnyHttpRequest(); - const renameRequest = useRenameRequest(activeRequest?.id ?? null); - const deleteRequest = useDeleteRequest(activeRequest?.id ?? null); + const [recentRequests] = useRecentRequests(); const [, setSidebarHidden] = useSidebarHidden(); - const openSettings = useOpenSettings(); const { baseEnvironment } = useEnvironments(); + const { mutate: openSettings } = useOpenSettings(); + const { mutate: openWorkspace } = useOpenWorkspace(); + const { mutate: createHttpRequest } = useCreateHttpRequest(); + const { mutate: createGrpcRequest } = useCreateGrpcRequest(); + const { mutate: createEnvironment } = useCreateEnvironment(); + const { mutate: sendRequest } = useSendAnyHttpRequest(); + const { mutate: renameRequest } = useRenameRequest(activeRequest?.id ?? null); + const { mutate: deleteRequest } = useDeleteAnyRequest(); const workspaceCommands = useMemo(() => { const commands: CommandPaletteItem[] = [ @@ -85,7 +85,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { key: 'settings.open', label: 'Open Settings', action: 'settings.show', - onSelect: openSettings.mutate, + onSelect: openSettings, }, { key: 'app.create', @@ -95,7 +95,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { { key: 'http_request.create', label: 'Create HTTP Request', - onSelect: () => createHttpRequest.mutate({}), + onSelect: () => createHttpRequest({}), }, { key: 'folder.create', @@ -117,7 +117,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { { key: 'grpc_request.create', label: 'Create GRPC Request', - onSelect: () => createGrpcRequest.mutate({}), + onSelect: () => createGrpcRequest({}), }, { key: 'environment.edit', @@ -136,7 +136,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { { key: 'environment.create', label: 'Create Environment', - onSelect: () => createEnvironment.mutate(baseEnvironment), + onSelect: () => createEnvironment(baseEnvironment), }, { key: 'sidebar.toggle', @@ -151,7 +151,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { key: 'http_request.send', action: 'http_request.send', label: 'Send Request', - onSelect: () => sendRequest.mutateAsync(activeRequest.id), + onSelect: () => sendRequest(activeRequest.id), }); for (const a of httpRequestActions) { commands.push({ @@ -166,13 +166,13 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { commands.push({ key: 'http_request.rename', label: 'Rename Request', - onSelect: renameRequest.mutate, + onSelect: renameRequest, }); commands.push({ key: 'http_request.delete', label: 'Delete Request', - onSelect: deleteRequest.mutate, + onSelect: () => deleteRequest(activeRequest.id), }); } @@ -190,10 +190,10 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { createGrpcRequest, createHttpRequest, createWorkspace, - deleteRequest.mutate, + deleteRequest, httpRequestActions, - openSettings.mutate, - renameRequest.mutate, + openSettings, + renameRequest, sendRequest, setSidebarHidden, ]); @@ -315,7 +315,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { workspaceGroup.items.push({ key: `switch-workspace-${w.id}`, label: w.name, - onSelect: () => openWorkspace.mutate({ workspaceId: w.id, inNewWindow: false }), + onSelect: () => openWorkspace({ workspaceId: w.id, inNewWindow: false }), }); } diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx index ff4103cb..1c75a121 100644 --- a/src-web/components/Sidebar.tsx +++ b/src-web/components/Sidebar.tsx @@ -6,6 +6,7 @@ import { useKey, useKeyPressEvent } from 'react-use'; import { getActiveRequest } from '../hooks/useActiveRequest'; import { useActiveWorkspace } from '../hooks/useActiveWorkspace'; import { useCreateDropdownItems } from '../hooks/useCreateDropdownItems'; +import { useDeleteAnyRequest } from '../hooks/useDeleteAnyRequest'; import { useGrpcConnections } from '../hooks/useGrpcConnections'; import { useHotKey } from '../hooks/useHotKey'; import { useHttpResponses } from '../hooks/useHttpResponses'; @@ -123,6 +124,17 @@ export function Sidebar({ className }: Props) { }, [focusActiveRequest, hasFocus]); const handleBlur = useCallback(() => setHasFocus(false), [setHasFocus]); + const deleteRequest = useDeleteAnyRequest(); + + useHotKey( + 'http_request.delete', + async () => { + // Delete only works if a request is focused + if (selectedId == null) return; + deleteRequest.mutate(selectedId); + }, + { enable: hasFocus }, + ); useHotKey('sidebar.focus', async () => { // Hide the sidebar if it's already focused diff --git a/src-web/components/SidebarItemContextMenu.tsx b/src-web/components/SidebarItemContextMenu.tsx index fa091f7d..b4e64a04 100644 --- a/src-web/components/SidebarItemContextMenu.tsx +++ b/src-web/components/SidebarItemContextMenu.tsx @@ -1,7 +1,7 @@ import React, { useMemo } from 'react'; import { useCreateDropdownItems } from '../hooks/useCreateDropdownItems'; import { useDeleteFolder } from '../hooks/useDeleteFolder'; -import { useDeleteRequest } from '../hooks/useDeleteRequest'; +import { useDeleteAnyRequest } from '../hooks/useDeleteAnyRequest'; import { useDuplicateFolder } from '../hooks/useDuplicateFolder'; import { useDuplicateGrpcRequest } from '../hooks/useDuplicateGrpcRequest'; import { useDuplicateHttpRequest } from '../hooks/useDuplicateHttpRequest'; @@ -33,7 +33,7 @@ export function SidebarItemContextMenu({ child, show, close }: Props) { const httpRequestActions = useHttpRequestActions(); const sendRequest = useSendAnyHttpRequest(); const workspaces = useWorkspaces(); - const deleteRequest = useDeleteRequest(child.id); + const deleteRequest = useDeleteAnyRequest(); const renameRequest = useRenameRequest(child.id); const duplicateHttpRequest = useDuplicateHttpRequest({ id: child.id, navigateAfter: true }); const duplicateGrpcRequest = useDuplicateGrpcRequest({ id: child.id, navigateAfter: true }); @@ -134,8 +134,10 @@ export function SidebarItemContextMenu({ child, show, close }: Props) { key: 'delete-request', variant: 'danger', label: 'Delete', + hotKeyAction: 'http_request.delete', + hotKeyLabelOnly: true, leftSlot: , - onSelect: () => deleteRequest.mutate(), + onSelect: () => deleteRequest.mutate(child.id), }, ]; } diff --git a/src-web/hooks/useDeleteRequest.tsx b/src-web/hooks/useDeleteAnyRequest.tsx similarity index 75% rename from src-web/hooks/useDeleteRequest.tsx rename to src-web/hooks/useDeleteAnyRequest.tsx index 503868bc..faa61ad2 100644 --- a/src-web/hooks/useDeleteRequest.tsx +++ b/src-web/hooks/useDeleteAnyRequest.tsx @@ -2,13 +2,13 @@ import { useDeleteAnyGrpcRequest } from './useDeleteAnyGrpcRequest'; import { useDeleteAnyHttpRequest } from './useDeleteAnyHttpRequest'; import { useFastMutation } from './useFastMutation'; -export function useDeleteRequest(id: string | null) { +export function useDeleteAnyRequest() { const deleteAnyHttpRequest = useDeleteAnyHttpRequest(); const deleteAnyGrpcRequest = useDeleteAnyGrpcRequest(); - return useFastMutation({ - mutationKey: ['delete_request', id], - mutationFn: async () => { + return useFastMutation({ + mutationKey: ['delete_request'], + mutationFn: async (id) => { if (id == null) return; // We don't know what type it is based on the ID, so just try deleting both deleteAnyHttpRequest.mutate(id); diff --git a/src-web/hooks/useHotKey.ts b/src-web/hooks/useHotKey.ts index 0fa1a8c9..24a535e2 100644 --- a/src-web/hooks/useHotKey.ts +++ b/src-web/hooks/useHotKey.ts @@ -15,6 +15,7 @@ export type HotkeyAction = | 'grpc_request.send' | 'hotkeys.showHelp' | 'http_request.create' + | 'http_request.delete' | 'http_request.duplicate' | 'http_request.send' | 'request_switcher.next' @@ -34,6 +35,7 @@ const hotkeys: Record = { 'grpc_request.send': ['CmdCtrl+Enter', 'CmdCtrl+r'], 'hotkeys.showHelp': ['CmdCtrl+Shift+/'], 'http_request.create': ['CmdCtrl+n'], + 'http_request.delete': ['Backspace'], 'http_request.duplicate': ['CmdCtrl+d'], 'http_request.send': ['CmdCtrl+Enter', 'CmdCtrl+r'], 'request_switcher.next': ['Control+Shift+Tab'], @@ -54,6 +56,7 @@ const hotkeyLabels: Record = { 'grpc_request.send': 'Send Message', 'hotkeys.showHelp': 'Show Keyboard Shortcuts', 'http_request.create': 'New Request', + 'http_request.delete': 'Delete Request', 'http_request.duplicate': 'Duplicate Request', 'http_request.send': 'Send Request', 'request_switcher.next': 'Go To Previous Request', @@ -95,8 +98,8 @@ export function useHotKey( } // Don't add key if not holding modifier - const isHoldingModifier = e.altKey || e.ctrlKey || e.metaKey || e.shiftKey; - if (!isHoldingModifier) { + const isValidKeymapKey = e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.key === 'Backspace'; + if (!isValidKeymapKey) { return; } @@ -189,6 +192,8 @@ export function useFormattedHotkey(action: HotkeyAction | null): string[] | null labelParts.push('↩'); } else if (p === 'Tab') { labelParts.push('⇥'); + } else if (p === 'Backspace') { + labelParts.push('⌫'); } else { labelParts.push(capitalize(p)); }