Add rename/delete/send to cmd+k

This commit is contained in:
Gregory Schier
2024-07-30 15:10:24 -07:00
parent d1a2c9622d
commit c2ce446692
6 changed files with 103 additions and 45 deletions

View File

@@ -5,7 +5,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { useActiveCookieJar } from '../hooks/useActiveCookieJar'; import { useActiveCookieJar } from '../hooks/useActiveCookieJar';
import { useActiveEnvironment } from '../hooks/useActiveEnvironment'; import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
import { useActiveEnvironmentId } from '../hooks/useActiveEnvironmentId'; import { useActiveEnvironmentId } from '../hooks/useActiveEnvironmentId';
import { useActiveRequestId } from '../hooks/useActiveRequestId'; import { useActiveRequest } from '../hooks/useActiveRequest';
import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId'; import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId';
import { useAppRoutes } from '../hooks/useAppRoutes'; import { useAppRoutes } from '../hooks/useAppRoutes';
import { useCreateEnvironment } from '../hooks/useCreateEnvironment'; import { useCreateEnvironment } from '../hooks/useCreateEnvironment';
@@ -13,6 +13,7 @@ import { useCreateGrpcRequest } from '../hooks/useCreateGrpcRequest';
import { useCreateHttpRequest } from '../hooks/useCreateHttpRequest'; import { useCreateHttpRequest } from '../hooks/useCreateHttpRequest';
import { useCreateWorkspace } from '../hooks/useCreateWorkspace'; import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
import { useDebouncedState } from '../hooks/useDebouncedState'; import { useDebouncedState } from '../hooks/useDebouncedState';
import { useDeleteRequest } from '../hooks/useDeleteRequest';
import { useEnvironments } from '../hooks/useEnvironments'; import { useEnvironments } from '../hooks/useEnvironments';
import type { HotkeyAction } from '../hooks/useHotKey'; import type { HotkeyAction } from '../hooks/useHotKey';
import { useHotKey } from '../hooks/useHotKey'; import { useHotKey } from '../hooks/useHotKey';
@@ -20,7 +21,9 @@ import { useOpenWorkspace } from '../hooks/useOpenWorkspace';
import { useRecentEnvironments } from '../hooks/useRecentEnvironments'; import { useRecentEnvironments } from '../hooks/useRecentEnvironments';
import { useRecentRequests } from '../hooks/useRecentRequests'; import { useRecentRequests } from '../hooks/useRecentRequests';
import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces'; import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces';
import { useRenameRequest } from '../hooks/useRenameRequest';
import { useRequests } from '../hooks/useRequests'; import { useRequests } from '../hooks/useRequests';
import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest';
import { useSidebarHidden } from '../hooks/useSidebarHidden'; import { useSidebarHidden } from '../hooks/useSidebarHidden';
import { useWorkspaces } from '../hooks/useWorkspaces'; import { useWorkspaces } from '../hooks/useWorkspaces';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { fallbackRequestName } from '../lib/fallbackRequestName';
@@ -55,13 +58,12 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
const [selectedItemKey, setSelectedItemKey] = useState<string | null>(null); const [selectedItemKey, setSelectedItemKey] = useState<string | null>(null);
const routes = useAppRoutes(); const routes = useAppRoutes();
const activeEnvironmentId = useActiveEnvironmentId(); const activeEnvironmentId = useActiveEnvironmentId();
const activeRequestId = useActiveRequestId();
const active = useActiveWorkspaceId();
const workspaces = useWorkspaces(); const workspaces = useWorkspaces();
const environments = useEnvironments(); const environments = useEnvironments();
const recentEnvironments = useRecentEnvironments(); const recentEnvironments = useRecentEnvironments();
const recentWorkspaces = useRecentWorkspaces(); const recentWorkspaces = useRecentWorkspaces();
const requests = useRequests(); const requests = useRequests();
const activeRequest = useActiveRequest();
const recentRequests = useRecentRequests(); const recentRequests = useRecentRequests();
const openWorkspace = useOpenWorkspace(); const openWorkspace = useOpenWorkspace();
const createWorkspace = useCreateWorkspace(); const createWorkspace = useCreateWorkspace();
@@ -72,6 +74,9 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
const dialog = useDialog(); const dialog = useDialog();
const workspaceId = useActiveWorkspaceId(); const workspaceId = useActiveWorkspaceId();
const activeEnvironment = useActiveEnvironment(); const activeEnvironment = useActiveEnvironment();
const sendRequest = useSendAnyHttpRequest();
const renameRequest = useRenameRequest(activeRequest?.id ?? null);
const deleteRequest = useDeleteRequest(activeRequest?.id ?? null);
const [, setSidebarHidden] = useSidebarHidden(); const [, setSidebarHidden] = useSidebarHidden();
const workspaceCommands = useMemo<CommandPaletteItem[]>(() => { const workspaceCommands = useMemo<CommandPaletteItem[]>(() => {
@@ -142,20 +147,48 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
onSelect: () => setSidebarHidden((h) => !h), onSelect: () => setSidebarHidden((h) => !h),
}, },
]; ];
if (activeRequest?.model === 'http_request') {
commands.push({
key: 'http_request.send',
action: 'http_request.send',
label: 'Send Request',
onSelect: () => sendRequest.mutateAsync(activeRequest.id),
});
}
if (activeRequest != null) {
commands.push({
key: 'http_request.rename',
label: 'Rename Request',
onSelect: renameRequest.mutate,
});
commands.push({
key: 'http_request.delete',
label: 'Delete Request',
onSelect: deleteRequest.mutate,
});
}
return commands.sort((a, b) => return commands.sort((a, b) =>
('searchText' in a ? a.searchText : a.label).localeCompare( ('searchText' in a ? a.searchText : a.label).localeCompare(
'searchText' in b ? b.searchText : b.label, 'searchText' in b ? b.searchText : b.label,
), ),
); );
}, [ }, [
activeCookieJar, activeCookieJar?.id,
activeEnvironment, activeEnvironment,
activeRequest,
createEnvironment.mutate, createEnvironment.mutate,
createGrpcRequest, createGrpcRequest,
createHttpRequest, createHttpRequest,
createWorkspace.mutate, createWorkspace.mutate,
deleteRequest.mutate,
dialog, dialog,
renameRequest.mutate,
routes.paths, routes.paths,
sendRequest,
setSidebarHidden, setSidebarHidden,
workspaceId, workspaceId,
]); ]);
@@ -225,10 +258,6 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
}; };
for (const r of sortedRequests) { for (const r of sortedRequests) {
if (r.id === activeRequestId) {
continue;
}
requestGroup.items.push({ requestGroup.items.push({
key: `switch-request-${r.id}`, key: `switch-request-${r.id}`,
searchText: fallbackRequestName(r), searchText: fallbackRequestName(r),
@@ -272,9 +301,6 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
}; };
for (const w of sortedWorkspaces) { for (const w of sortedWorkspaces) {
if (w.id === active) {
continue;
}
workspaceGroup.items.push({ workspaceGroup.items.push({
key: `switch-workspace-${w.id}`, key: `switch-workspace-${w.id}`,
label: w.name, label: w.name,
@@ -286,13 +312,11 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
}, [ }, [
workspaceCommands, workspaceCommands,
sortedRequests, sortedRequests,
activeRequestId,
routes, routes,
activeEnvironmentId, activeEnvironmentId,
sortedEnvironments, sortedEnvironments,
activeEnvironment?.id, activeEnvironment?.id,
sortedWorkspaces, sortedWorkspaces,
active,
openWorkspace, openWorkspace,
]); ]);

View File

@@ -3,7 +3,7 @@ import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useAtiveWorkspaceChangedToast } from '../hooks/useAtiveWorkspaceChangedToast'; import { useAtiveWorkspaceChangedToast } from '../hooks/useAtiveWorkspaceChangedToast';
import { useClipboardText } from '../hooks/useClipboardText'; import { useClipboardText } from '../hooks/useClipboardText';
import { useCommandPalette } from '../hooks/useCommandPalette'; import { useToggleCommandPalette } from '../hooks/useToggleCommandPalette';
import { cookieJarsQueryKey } from '../hooks/useCookieJars'; import { cookieJarsQueryKey } from '../hooks/useCookieJars';
import { environmentsQueryKey } from '../hooks/useEnvironments'; import { environmentsQueryKey } from '../hooks/useEnvironments';
import { foldersQueryKey } from '../hooks/useFolders'; import { foldersQueryKey } from '../hooks/useFolders';
@@ -42,10 +42,12 @@ export function GlobalHooks() {
// Other useful things // Other useful things
useSyncThemeToDocument(); useSyncThemeToDocument();
useCommandPalette();
useNotificationToast(); useNotificationToast();
useAtiveWorkspaceChangedToast(); useAtiveWorkspaceChangedToast();
const toggleCommandPalette = useToggleCommandPalette();
useHotKey('command_palette.toggle', toggleCommandPalette);
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { wasUpdatedExternally } = useRequestUpdateKey(null); const { wasUpdatedExternally } = useRequestUpdateKey(null);

View File

@@ -22,6 +22,7 @@ import { useLatestGrpcConnection } from '../hooks/useLatestGrpcConnection';
import { useLatestHttpResponse } from '../hooks/useLatestHttpResponse'; import { useLatestHttpResponse } from '../hooks/useLatestHttpResponse';
import { useMoveToWorkspace } from '../hooks/useMoveToWorkspace'; import { useMoveToWorkspace } from '../hooks/useMoveToWorkspace';
import { usePrompt } from '../hooks/usePrompt'; import { usePrompt } from '../hooks/usePrompt';
import { useRenameRequest } from '../hooks/useRenameRequest';
import { useRequests } from '../hooks/useRequests'; import { useRequests } from '../hooks/useRequests';
import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest'; import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest';
import { useSendManyRequests } from '../hooks/useSendFolder'; import { useSendManyRequests } from '../hooks/useSendFolder';
@@ -649,6 +650,7 @@ function SidebarItem({
const activeRequest = useActiveRequest(); const activeRequest = useActiveRequest();
const deleteFolder = useDeleteFolder(itemId); const deleteFolder = useDeleteFolder(itemId);
const deleteRequest = useDeleteRequest(itemId); const deleteRequest = useDeleteRequest(itemId);
const renameRequest = useRenameRequest(itemId);
const duplicateHttpRequest = useDuplicateHttpRequest({ id: itemId, navigateAfter: true }); const duplicateHttpRequest = useDuplicateHttpRequest({ id: itemId, navigateAfter: true });
const duplicateGrpcRequest = useDuplicateGrpcRequest({ id: itemId, navigateAfter: true }); const duplicateGrpcRequest = useDuplicateGrpcRequest({ id: itemId, navigateAfter: true });
const copyAsCurl = useCopyAsCurl(itemId); const copyAsCurl = useCopyAsCurl(itemId);
@@ -795,29 +797,7 @@ function SidebarItem({
key: 'renameRequest', key: 'renameRequest',
label: 'Rename', label: 'Rename',
leftSlot: <Icon icon="pencil" />, leftSlot: <Icon icon="pencil" />,
onSelect: async () => { onSelect: renameRequest.mutate,
const name = await prompt({
id: 'rename-request',
title: 'Rename Request',
description:
itemName === '' ? (
'Enter a new name'
) : (
<>
Enter a new name for <InlineCode>{itemName}</InlineCode>
</>
),
name: 'name',
label: 'Name',
placeholder: 'New Name',
defaultValue: itemName,
});
if (itemModel === 'http_request') {
updateHttpRequest.mutate({ id: itemId, update: (r) => ({ ...r, name }) });
} else {
updateGrpcRequest.mutate({ id: itemId, update: (r) => ({ ...r, name }) });
}
},
}, },
{ {
key: 'duplicateRequest', key: 'duplicateRequest',
@@ -849,7 +829,7 @@ function SidebarItem({
} }
}, [ }, [
child.children, child.children,
copyAsCurl, copyAsCurl.mutate,
createDropdownItems, createDropdownItems,
deleteFolder, deleteFolder,
deleteRequest, deleteRequest,
@@ -860,11 +840,10 @@ function SidebarItem({
itemName, itemName,
moveToWorkspace.mutate, moveToWorkspace.mutate,
prompt, prompt,
renameRequest.mutate,
sendManyRequests, sendManyRequests,
sendRequest, sendRequest,
updateAnyFolder, updateAnyFolder,
updateGrpcRequest,
updateHttpRequest,
workspaces.length, workspaces.length,
]); ]);

View File

@@ -1,7 +1,9 @@
import classNames from 'classnames'; import classNames from 'classnames';
import React, { memo } from 'react'; import React, { memo } from 'react';
import { useToggleCommandPalette } from '../hooks/useToggleCommandPalette';
import { CookieDropdown } from './CookieDropdown'; import { CookieDropdown } from './CookieDropdown';
import { Icon } from './core/Icon'; import { Icon } from './core/Icon';
import { IconButton } from './core/IconButton';
import { HStack } from './core/Stacks'; import { HStack } from './core/Stacks';
import { EnvironmentActionsDropdown } from './EnvironmentActionsDropdown'; import { EnvironmentActionsDropdown } from './EnvironmentActionsDropdown';
import { ImportCurlButton } from './ImportCurlButton'; import { ImportCurlButton } from './ImportCurlButton';
@@ -16,6 +18,7 @@ interface Props {
} }
export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) { export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) {
const togglePalette = useToggleCommandPalette();
return ( return (
<HStack space={2} justifyContent="center" className={classNames(className, 'w-full h-full')}> <HStack space={2} justifyContent="center" className={classNames(className, 'w-full h-full')}>
<HStack space={0.5} className="flex-1 pointer-events-none"> <HStack space={0.5} className="flex-1 pointer-events-none">
@@ -32,6 +35,12 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
</div> </div>
<div className="flex-1 flex gap-1 items-center h-full justify-end pointer-events-none pr-0.5"> <div className="flex-1 flex gap-1 items-center h-full justify-end pointer-events-none pr-0.5">
<ImportCurlButton /> <ImportCurlButton />
<IconButton
icon="search"
title="Search or execute a command"
size="sm"
onClick={togglePalette}
/>
<SettingsDropdown /> <SettingsDropdown />
<WindowControls /> <WindowControls />
</div> </div>

View File

@@ -0,0 +1,42 @@
import { useMutation } from '@tanstack/react-query';
import { InlineCode } from '../components/core/InlineCode';
import { usePrompt } from './usePrompt';
import { useRequests } from './useRequests';
import { useUpdateAnyGrpcRequest } from './useUpdateAnyGrpcRequest';
import { useUpdateAnyHttpRequest } from './useUpdateAnyHttpRequest';
export function useRenameRequest(requestId: string | null) {
const prompt = usePrompt();
const updateHttpRequest = useUpdateAnyHttpRequest();
const updateGrpcRequest = useUpdateAnyGrpcRequest();
const requests = useRequests();
return useMutation({
mutationFn: async () => {
const request = requests.find((r) => r.id === requestId);
if (request == null) return;
const name = await prompt({
id: 'rename-request',
title: 'Rename Request',
description:
request.name === '' ? (
'Enter a new name'
) : (
<>
Enter a new name for <InlineCode>{request.name}</InlineCode>
</>
),
name: 'name',
label: 'Name',
placeholder: 'New Name',
defaultValue: request.name,
});
if (request.model === 'http_request') {
updateHttpRequest.mutate({ id: request.id, update: (r) => ({ ...r, name }) });
} else {
updateGrpcRequest.mutate({ id: request.id, update: (r) => ({ ...r, name }) });
}
},
});
}

View File

@@ -1,10 +1,10 @@
import { useCallback } from 'react';
import { CommandPalette } from '../components/CommandPalette'; import { CommandPalette } from '../components/CommandPalette';
import { useDialog } from '../components/DialogContext'; import { useDialog } from '../components/DialogContext';
import { useHotKey } from './useHotKey';
export function useCommandPalette() { export function useToggleCommandPalette() {
const dialog = useDialog(); const dialog = useDialog();
useHotKey('command_palette.toggle', () => { const togglePalette = useCallback(() => {
dialog.toggle({ dialog.toggle({
id: 'command_palette', id: 'command_palette',
size: 'dynamic', size: 'dynamic',
@@ -15,5 +15,7 @@ export function useCommandPalette() {
noScroll: true, noScroll: true,
render: ({ hide }) => <CommandPalette onClose={hide} />, render: ({ hide }) => <CommandPalette onClose={hide} />,
}); });
}); }, [dialog]);
return togglePalette;
} }