diff --git a/src-web/commands/moveToWorkspace.tsx b/src-web/commands/moveToWorkspace.tsx
index a3839b93..63672b4f 100644
--- a/src-web/commands/moveToWorkspace.tsx
+++ b/src-web/commands/moveToWorkspace.tsx
@@ -3,23 +3,30 @@ import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-intern
import { MoveToWorkspaceDialog } from '../components/MoveToWorkspaceDialog';
import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace';
import { createFastMutation } from '../hooks/useFastMutation';
+import { pluralizeCount } from '../lib/pluralize';
import { showDialog } from '../lib/dialog';
import { jotaiStore } from '../lib/jotai';
export const moveToWorkspace = createFastMutation({
mutationKey: ['move_workspace'],
- mutationFn: async (request: HttpRequest | GrpcRequest | WebsocketRequest) => {
+ mutationFn: async (requests: (HttpRequest | GrpcRequest | WebsocketRequest)[]) => {
const activeWorkspaceId = jotaiStore.get(activeWorkspaceIdAtom);
if (activeWorkspaceId == null) return;
+ if (requests.length === 0) return;
+
+ const title =
+ requests.length === 1
+ ? 'Move Request'
+ : `Move ${pluralizeCount('Request', requests.length)}`;
showDialog({
id: 'change-workspace',
- title: 'Move Workspace',
+ title,
size: 'sm',
render: ({ hide }) => (
),
diff --git a/src-web/components/MoveToWorkspaceDialog.tsx b/src-web/components/MoveToWorkspaceDialog.tsx
index 33706f32..d6f405ec 100644
--- a/src-web/components/MoveToWorkspaceDialog.tsx
+++ b/src-web/components/MoveToWorkspaceDialog.tsx
@@ -2,6 +2,7 @@ import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-intern
import { patchModel, workspacesAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { useState } from 'react';
+import { pluralizeCount } from '../lib/pluralize';
import { resolvedModelName } from '../lib/resolvedModelName';
import { router } from '../lib/router';
import { showToast } from '../lib/toast';
@@ -12,18 +13,21 @@ import { VStack } from './core/Stacks';
interface Props {
activeWorkspaceId: string;
- request: HttpRequest | GrpcRequest | WebsocketRequest;
+ requests: (HttpRequest | GrpcRequest | WebsocketRequest)[];
onDone: () => void;
}
-export function MoveToWorkspaceDialog({ onDone, request, activeWorkspaceId }: Props) {
+export function MoveToWorkspaceDialog({ onDone, requests, activeWorkspaceId }: Props) {
const workspaces = useAtomValue(workspacesAtom);
const [selectedWorkspaceId, setSelectedWorkspaceId] = useState(activeWorkspaceId);
+ const targetWorkspace = workspaces.find((w) => w.id === selectedWorkspaceId);
+ const isSameWorkspace = selectedWorkspaceId === activeWorkspaceId;
+
return (
);
diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx
index 9f3127a6..c7b8aec3 100644
--- a/src-web/components/Sidebar.tsx
+++ b/src-web/components/Sidebar.tsx
@@ -278,6 +278,7 @@ function Sidebar({ className }: { className?: string }) {
},
},
'sidebar.selected.duplicate': {
+ // Higher priority so this takes precedence over model.duplicate (same Meta+d binding)
priority: 10,
enable,
cb: async (items: SidebarModel[]) => {
@@ -290,6 +291,18 @@ function Sidebar({ className }: { className?: string }) {
}
},
},
+ 'sidebar.selected.move': {
+ enable,
+ cb: async (items: SidebarModel[]) => {
+ const requests = items.filter(
+ (i): i is HttpRequest | GrpcRequest | WebsocketRequest =>
+ i.model === 'http_request' || i.model === 'grpc_request' || i.model === 'websocket_request'
+ );
+ if (requests.length > 0) {
+ moveToWorkspace.mutate(requests);
+ }
+ },
+ },
'request.send': {
enable,
cb: async (items: SidebarModel[]) => {
@@ -320,6 +333,10 @@ function Sidebar({ className }: { className?: string }) {
const workspaces = jotaiStore.get(workspacesAtom);
const onlyHttpRequests = items.every((i) => i.model === 'http_request');
+ const requestItems = items.filter(
+ (i) =>
+ i.model === 'http_request' || i.model === 'grpc_request' || i.model === 'websocket_request',
+ );
const initialItems: ContextMenuProps['items'] = [
{
@@ -416,16 +433,13 @@ function Sidebar({ className }: { className?: string }) {
onSelect: () => actions['sidebar.selected.duplicate'].cb(items),
},
{
- label: 'Move',
+ label: items.length <= 1 ? 'Move' : `Move ${requestItems.length} Requests`,
+ hotKeyAction: 'sidebar.selected.move',
+ hotKeyLabelOnly: true,
leftSlot: ,
- hidden:
- workspaces.length <= 1 ||
- items.length > 1 ||
- child.model === 'folder' ||
- child.model === 'workspace',
+ hidden: workspaces.length <= 1 || requestItems.length === 0 || requestItems.length !== items.length,
onSelect: () => {
- if (child.model === 'folder' || child.model === 'workspace') return;
- moveToWorkspace.mutate(child);
+ actions['sidebar.selected.move'].cb(items);
},
},
{
diff --git a/src-web/hooks/useHotKey.ts b/src-web/hooks/useHotKey.ts
index 40e9985c..1c8a3d2a 100644
--- a/src-web/hooks/useHotKey.ts
+++ b/src-web/hooks/useHotKey.ts
@@ -28,6 +28,7 @@ export type HotkeyAction =
| 'sidebar.filter'
| 'sidebar.selected.delete'
| 'sidebar.selected.duplicate'
+ | 'sidebar.selected.move'
| 'sidebar.selected.rename'
| 'sidebar.expand_all'
| 'sidebar.collapse_all'
@@ -58,6 +59,7 @@ const defaultHotkeysMac: Record = {
'sidebar.collapse_all': ['Meta+Shift+Minus'],
'sidebar.selected.delete': ['Delete', 'Meta+Backspace'],
'sidebar.selected.duplicate': ['Meta+d'],
+ 'sidebar.selected.move': [],
'sidebar.selected.rename': ['Enter'],
'sidebar.focus': ['Meta+b'],
'sidebar.context_menu': ['Control+Enter'],
@@ -87,6 +89,7 @@ const defaultHotkeysOther: Record = {
'sidebar.collapse_all': ['Control+Shift+Minus'],
'sidebar.selected.delete': ['Delete', 'Control+Backspace'],
'sidebar.selected.duplicate': ['Control+d'],
+ 'sidebar.selected.move': [],
'sidebar.selected.rename': ['Enter'],
'sidebar.focus': ['Control+b'],
'sidebar.context_menu': ['Alt+Insert'],
@@ -141,6 +144,7 @@ const hotkeyLabels: Record = {
'sidebar.collapse_all': 'Collapse All Folders',
'sidebar.selected.delete': 'Delete Selected Sidebar Item',
'sidebar.selected.duplicate': 'Duplicate Selected Sidebar Item',
+ 'sidebar.selected.move': 'Move Selected to Workspace',
'sidebar.selected.rename': 'Rename Selected Sidebar Item',
'sidebar.focus': 'Focus or Toggle Sidebar',
'sidebar.context_menu': 'Show Context Menu',