mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-11 20:00:29 +01:00
A bunch of changes, including moving prompt/confirm out of context
This commit is contained in:
@@ -6,7 +6,7 @@ import { SyncOp } from './bindings/sync';
|
||||
import { WatchEvent, WatchResult } from './bindings/watch';
|
||||
|
||||
export async function calculateSync(workspace: Workspace) {
|
||||
if (!workspace.settingSyncDir) throw new Error('Workspace sync dir not configured');
|
||||
if (!workspace.settingSyncDir) return;
|
||||
|
||||
return invoke<SyncOp[]>('plugin:yaak-sync|calculate', {
|
||||
workspaceId: workspace.id,
|
||||
@@ -15,6 +15,8 @@ export async function calculateSync(workspace: Workspace) {
|
||||
}
|
||||
|
||||
export async function applySync(workspace: Workspace, syncOps: SyncOp[]) {
|
||||
if (!workspace.settingSyncDir) return;
|
||||
|
||||
return invoke<void>('plugin:yaak-sync|apply', {
|
||||
workspaceId: workspace.id,
|
||||
dir: workspace.settingSyncDir,
|
||||
@@ -23,22 +25,24 @@ export async function applySync(workspace: Workspace, syncOps: SyncOp[]) {
|
||||
}
|
||||
|
||||
export function useWatchWorkspace(workspace: Workspace | null, callback: (e: WatchEvent) => void) {
|
||||
const workspaceId = workspace?.id ?? null;
|
||||
|
||||
useEffect(() => {
|
||||
if (workspaceId == null) return;
|
||||
if (workspace == null) return;
|
||||
if (!workspace.settingSyncDir) return;
|
||||
|
||||
const channel = new Channel<WatchEvent>();
|
||||
channel.onmessage = callback;
|
||||
const promise = invoke<WatchResult>('plugin:yaak-sync|watch', { workspaceId, channel });
|
||||
const promise = invoke<WatchResult>('plugin:yaak-sync|watch', {
|
||||
workspaceId: workspace.id,
|
||||
channel,
|
||||
});
|
||||
|
||||
return () => {
|
||||
promise
|
||||
.then(({ unlistenEvent }) => {
|
||||
console.log('Cancelling workspace watch', workspaceId, unlistenEvent);
|
||||
console.log('Cancelling workspace watch', workspace.id, unlistenEvent);
|
||||
return emit(unlistenEvent);
|
||||
})
|
||||
.catch(console.error);
|
||||
};
|
||||
}, [workspaceId]);
|
||||
}, [workspace]);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useActiveCookieJar } from '../hooks/useActiveCookieJar';
|
||||
import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
|
||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||
import { useCommands } from '../hooks/useCommands';
|
||||
import { useCreateEnvironment } from '../hooks/useCreateEnvironment';
|
||||
import { useCreateGrpcRequest } from '../hooks/useCreateGrpcRequest';
|
||||
import { useCreateHttpRequest } from '../hooks/useCreateHttpRequest';
|
||||
@@ -27,6 +26,7 @@ import { useScrollIntoView } from '../hooks/useScrollIntoView';
|
||||
import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest';
|
||||
import { useSidebarHidden } from '../hooks/useSidebarHidden';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { createFolder } from '../lib/commands';
|
||||
import { showDialog, toggleDialog } from '../lib/dialog';
|
||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||
import { router } from '../lib/router';
|
||||
@@ -69,7 +69,6 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
||||
const [recentRequests] = useRecentRequests();
|
||||
const openWorkspace = useOpenWorkspace();
|
||||
const createHttpRequest = useCreateHttpRequest();
|
||||
const { createFolder } = useCommands();
|
||||
const activeCookieJar = useActiveCookieJar();
|
||||
const createGrpcRequest = useCreateGrpcRequest();
|
||||
const createEnvironment = useCreateEnvironment();
|
||||
@@ -188,7 +187,6 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
||||
activeRequest,
|
||||
baseEnvironment,
|
||||
createEnvironment,
|
||||
createFolder,
|
||||
createGrpcRequest,
|
||||
createHttpRequest,
|
||||
createWorkspace,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { setActiveCookieJar, useActiveCookieJar } from '../hooks/useActiveCookieJar';
|
||||
import { useActiveCookieJar } from '../hooks/useActiveCookieJar';
|
||||
import { cookieJarsAtom } from '../hooks/useCookieJars';
|
||||
import { useCreateCookieJar } from '../hooks/useCreateCookieJar';
|
||||
import { useDeleteCookieJar } from '../hooks/useDeleteCookieJar';
|
||||
import { usePrompt } from '../hooks/usePrompt';
|
||||
import { useUpdateCookieJar } from '../hooks/useUpdateCookieJar';
|
||||
import { showDialog } from '../lib/dialog';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { showPrompt } from '../lib/prompt';
|
||||
import {setWorkspaceSearchParams} from "../lib/setWorkspaceSearchParams";
|
||||
import { CookieDialog } from './CookieDialog';
|
||||
import { Dropdown, type DropdownItem } from './core/Dropdown';
|
||||
import { Icon } from './core/Icon';
|
||||
@@ -18,18 +19,19 @@ export const CookieDropdown = memo(function CookieDropdown() {
|
||||
const updateCookieJar = useUpdateCookieJar(activeCookieJar?.id ?? null);
|
||||
const deleteCookieJar = useDeleteCookieJar(activeCookieJar ?? null);
|
||||
const createCookieJar = useCreateCookieJar();
|
||||
const prompt = usePrompt();
|
||||
const cookieJars = useAtomValue(cookieJarsAtom);
|
||||
|
||||
const items = useMemo((): DropdownItem[] => {
|
||||
const cookieJars = jotaiStore.get(cookieJarsAtom) ?? [];
|
||||
return [
|
||||
...cookieJars.map((j) => ({
|
||||
...(cookieJars ?? []).map((j) => ({
|
||||
key: j.id,
|
||||
label: j.name,
|
||||
leftSlot: <Icon icon={j.id === activeCookieJar?.id ? 'check' : 'empty'} />,
|
||||
onSelect: () => setActiveCookieJar(j),
|
||||
onSelect: () => {
|
||||
setWorkspaceSearchParams({ cookie_jar_id: j.id });
|
||||
},
|
||||
})),
|
||||
...((cookieJars.length > 0 && activeCookieJar != null
|
||||
...(((cookieJars ?? []).length > 0 && activeCookieJar != null
|
||||
? [
|
||||
{ type: 'separator', label: activeCookieJar.name },
|
||||
{
|
||||
@@ -51,7 +53,7 @@ export const CookieDropdown = memo(function CookieDropdown() {
|
||||
label: 'Rename',
|
||||
leftSlot: <Icon icon="pencil" />,
|
||||
onSelect: async () => {
|
||||
const name = await prompt({
|
||||
const name = await showPrompt({
|
||||
id: 'rename-cookie-jar',
|
||||
title: 'Rename Cookie Jar',
|
||||
description: (
|
||||
@@ -68,7 +70,7 @@ export const CookieDropdown = memo(function CookieDropdown() {
|
||||
updateCookieJar.mutate({ name });
|
||||
},
|
||||
},
|
||||
...((cookieJars.length > 1 // Never delete the last one
|
||||
...(((cookieJars ?? []).length > 1 // Never delete the last one
|
||||
? [
|
||||
{
|
||||
key: 'delete',
|
||||
@@ -89,7 +91,7 @@ export const CookieDropdown = memo(function CookieDropdown() {
|
||||
onSelect: () => createCookieJar.mutate(),
|
||||
},
|
||||
];
|
||||
}, [activeCookieJar, createCookieJar, deleteCookieJar, prompt, updateCookieJar]);
|
||||
}, [activeCookieJar, cookieJars, createCookieJar, deleteCookieJar, updateCookieJar]);
|
||||
|
||||
return (
|
||||
<Dropdown items={items}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { useCommands } from '../hooks/useCommands';
|
||||
import {createWorkspace} from "../lib/commands";
|
||||
import { Button } from './core/Button';
|
||||
import { PlainInput } from './core/PlainInput';
|
||||
import { VStack } from './core/Stacks';
|
||||
@@ -14,7 +14,6 @@ export function CreateWorkspaceDialog({ hide }: Props) {
|
||||
const [name, setName] = useState<string>('');
|
||||
const [description, setDescription] = useState<string>('');
|
||||
const [settingSyncDir, setSettingSyncDir] = useState<string | null>(null);
|
||||
const { createWorkspace } = useCommands();
|
||||
|
||||
return (
|
||||
<VStack
|
||||
|
||||
@@ -12,19 +12,26 @@ export function Dialogs() {
|
||||
const dialogs = useAtomValue(dialogsAtom);
|
||||
return (
|
||||
<>
|
||||
{dialogs.map(({ render, onClose, id, ...props }: DialogInstance) => (
|
||||
<Dialog
|
||||
open
|
||||
key={id}
|
||||
onClose={() => {
|
||||
onClose?.();
|
||||
hideDialog(id);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{render({ hide: () => hideDialog(id) })}
|
||||
</Dialog>
|
||||
{dialogs.map(({ id, ...props }) => (
|
||||
<DialogInstance key={id} id={id} {...props} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function DialogInstance({ render, onClose, id, ...props }: DialogInstance) {
|
||||
const children = render({ hide: () => hideDialog(id) });
|
||||
return (
|
||||
<Dialog
|
||||
open
|
||||
key={id}
|
||||
onClose={() => {
|
||||
onClose?.();
|
||||
hideDialog(id);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import { useCreateEnvironment } from '../hooks/useCreateEnvironment';
|
||||
import { useDeleteEnvironment } from '../hooks/useDeleteEnvironment';
|
||||
import { useEnvironments } from '../hooks/useEnvironments';
|
||||
import { useKeyValue } from '../hooks/useKeyValue';
|
||||
import { usePrompt } from '../hooks/usePrompt';
|
||||
import { useUpdateEnvironment } from '../hooks/useUpdateEnvironment';
|
||||
import { showPrompt } from '../lib/prompt';
|
||||
import { Banner } from './core/Banner';
|
||||
import { Button } from './core/Button';
|
||||
import { ContextMenu } from './core/Dropdown';
|
||||
@@ -212,7 +212,6 @@ function SidebarButton({
|
||||
rightSlot?: ReactNode;
|
||||
environment: Environment | null;
|
||||
}) {
|
||||
const prompt = usePrompt();
|
||||
const updateEnvironment = useUpdateEnvironment(environment?.id ?? null);
|
||||
const deleteEnvironment = useDeleteEnvironment(environment);
|
||||
const [showContextMenu, setShowContextMenu] = useState<{
|
||||
@@ -260,7 +259,7 @@ function SidebarButton({
|
||||
label: 'Rename',
|
||||
leftSlot: <Icon icon="pencil" size="sm" />,
|
||||
onSelect: async () => {
|
||||
const name = await prompt({
|
||||
const name = await showPrompt({
|
||||
id: 'rename-environment',
|
||||
title: 'Rename Environment',
|
||||
description: (
|
||||
|
||||
@@ -2,34 +2,18 @@ import { emit } from '@tauri-apps/api/event';
|
||||
import type { PromptTextRequest, PromptTextResponse } from '@yaakapp-internal/plugins';
|
||||
import { useWatchWorkspace } from '@yaakapp-internal/sync';
|
||||
import type { ShowToastRequest } from '@yaakapp/api';
|
||||
import {
|
||||
useEnsureActiveCookieJar,
|
||||
useSubscribeActiveCookieJarId,
|
||||
} from '../hooks/useActiveCookieJar';
|
||||
import { useSubscribeActiveEnvironmentId } from '../hooks/useActiveEnvironment';
|
||||
import { getActiveRequest, useActiveRequest } from '../hooks/useActiveRequest';
|
||||
import { useSubscribeActiveRequestId } from '../hooks/useActiveRequestId';
|
||||
import { useActiveWorkspace, useSubscribeActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
||||
import { useActiveWorkspaceChangedToast } from '../hooks/useActiveWorkspaceChangedToast';
|
||||
import { useDuplicateGrpcRequest } from '../hooks/useDuplicateGrpcRequest';
|
||||
import { useDuplicateHttpRequest } from '../hooks/useDuplicateHttpRequest';
|
||||
import { useGenerateThemeCss } from '../hooks/useGenerateThemeCss';
|
||||
import { useHotKey } from '../hooks/useHotKey';
|
||||
import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent';
|
||||
import { useNotificationToast } from '../hooks/useNotificationToast';
|
||||
import { usePrompt } from '../hooks/usePrompt';
|
||||
import { useSubscribeRecentCookieJars } from '../hooks/useRecentCookieJars';
|
||||
import { useSubscribeRecentEnvironments } from '../hooks/useRecentEnvironments';
|
||||
import { useSubscribeRecentRequests } from '../hooks/useRecentRequests';
|
||||
import { useSubscribeRecentWorkspaces } from '../hooks/useRecentWorkspaces';
|
||||
import { useSyncFontSizeSetting } from '../hooks/useSyncFontSizeSetting';
|
||||
import { useSyncModelStores } from '../hooks/useSyncModelStores';
|
||||
import { useSyncWorkspace } from '../hooks/useSyncWorkspace';
|
||||
import { useSyncWorkspaceChildModels } from '../hooks/useSyncWorkspaceChildModels';
|
||||
import { useSyncWorkspaceRequestTitle } from '../hooks/useSyncWorkspaceRequestTitle';
|
||||
import { useSyncZoomSetting } from '../hooks/useSyncZoomSetting';
|
||||
import { useSubscribeTemplateFunctions } from '../hooks/useTemplateFunctions';
|
||||
import { useToggleCommandPalette } from '../hooks/useToggleCommandPalette';
|
||||
import { showPrompt } from '../lib/prompt';
|
||||
import { showToast } from '../lib/toast';
|
||||
|
||||
export function GlobalHooks() {
|
||||
@@ -37,17 +21,8 @@ export function GlobalHooks() {
|
||||
useSyncZoomSetting();
|
||||
useSyncFontSizeSetting();
|
||||
useGenerateThemeCss();
|
||||
useSyncWorkspaceRequestTitle();
|
||||
|
||||
useSubscribeActiveWorkspaceId();
|
||||
useSubscribeActiveRequestId();
|
||||
useSubscribeActiveEnvironmentId();
|
||||
useSubscribeActiveCookieJarId();
|
||||
|
||||
useSubscribeRecentRequests();
|
||||
useSubscribeRecentWorkspaces();
|
||||
useSubscribeRecentEnvironments();
|
||||
useSubscribeRecentCookieJars();
|
||||
|
||||
useSyncWorkspaceChildModels();
|
||||
useSubscribeTemplateFunctions();
|
||||
@@ -55,7 +30,6 @@ export function GlobalHooks() {
|
||||
// Other useful things
|
||||
useNotificationToast();
|
||||
useActiveWorkspaceChangedToast();
|
||||
useEnsureActiveCookieJar();
|
||||
|
||||
// Listen for toasts
|
||||
useListenToTauriEvent<ShowToastRequest>('show_toast', (event) => {
|
||||
@@ -68,32 +42,10 @@ export function GlobalHooks() {
|
||||
useListenToTauriEvent('upserted_model', debouncedSync);
|
||||
useWatchWorkspace(activeWorkspace, debouncedSync);
|
||||
|
||||
const activeRequest = useActiveRequest();
|
||||
const duplicateHttpRequest = useDuplicateHttpRequest({
|
||||
id: activeRequest?.id ?? null,
|
||||
navigateAfter: true,
|
||||
});
|
||||
const duplicateGrpcRequest = useDuplicateGrpcRequest({
|
||||
id: activeRequest?.id ?? null,
|
||||
navigateAfter: true,
|
||||
});
|
||||
useHotKey('http_request.duplicate', async () => {
|
||||
const activeRequest = getActiveRequest();
|
||||
if (activeRequest?.model === 'http_request') {
|
||||
await duplicateHttpRequest.mutateAsync();
|
||||
} else {
|
||||
await duplicateGrpcRequest.mutateAsync();
|
||||
}
|
||||
});
|
||||
|
||||
const toggleCommandPalette = useToggleCommandPalette();
|
||||
useHotKey('command_palette.toggle', toggleCommandPalette);
|
||||
|
||||
const prompt = usePrompt();
|
||||
useListenToTauriEvent<{ replyId: string; args: PromptTextRequest }>(
|
||||
'show_prompt',
|
||||
async (event) => {
|
||||
const value = await prompt(event.payload.args);
|
||||
const value = await showPrompt(event.payload.args);
|
||||
const result: PromptTextResponse = { value };
|
||||
await emit(event.payload.replyId, result);
|
||||
},
|
||||
|
||||
@@ -42,7 +42,7 @@ export function MarkdownEditor({ className, defaultValue, onChange, name, ...edi
|
||||
<Editor
|
||||
hideGutter
|
||||
wrapLines
|
||||
className="max-w-2xl max-h-full"
|
||||
className="max-w-2xl max-h-full"
|
||||
language="markdown"
|
||||
defaultValue={defaultValue}
|
||||
onChange={onChange}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import classNames from 'classnames';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { usePrompt } from '../hooks/usePrompt';
|
||||
import { showPrompt } from '../lib/prompt';
|
||||
import { Button } from './core/Button';
|
||||
import type { DropdownItem } from './core/Dropdown';
|
||||
import { Icon } from './core/Icon';
|
||||
@@ -32,7 +32,6 @@ export const RequestMethodDropdown = memo(function RequestMethodDropdown({
|
||||
onChange,
|
||||
className,
|
||||
}: Props) {
|
||||
const prompt = usePrompt();
|
||||
const extraItems = useMemo<DropdownItem[]>(
|
||||
() => [
|
||||
{
|
||||
@@ -40,7 +39,7 @@ export const RequestMethodDropdown = memo(function RequestMethodDropdown({
|
||||
label: 'CUSTOM',
|
||||
leftSlot: <Icon icon="sparkles" />,
|
||||
onSelect: async () => {
|
||||
const newMethod = await prompt({
|
||||
const newMethod = await showPrompt({
|
||||
id: 'custom-method',
|
||||
label: 'Http Method',
|
||||
defaultValue: '',
|
||||
@@ -54,7 +53,7 @@ export const RequestMethodDropdown = memo(function RequestMethodDropdown({
|
||||
},
|
||||
},
|
||||
],
|
||||
[onChange, prompt],
|
||||
[onChange],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -298,7 +298,6 @@ export const RequestPane = memo(function RequestPane({
|
||||
);
|
||||
|
||||
const activeTab = activeTabs?.[activeRequestId];
|
||||
console.log('ACTIVE TAB', activeTab);
|
||||
const setActiveTab = useCallback(
|
||||
(tab: string) => {
|
||||
setActiveTabs((r) => ({ ...r, [activeRequest.id]: tab }));
|
||||
|
||||
@@ -15,6 +15,7 @@ import { useUpdateAnyFolder } from '../hooks/useUpdateAnyFolder';
|
||||
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
||||
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
||||
import { router } from '../lib/router';
|
||||
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
|
||||
import { ContextMenu } from './core/Dropdown';
|
||||
import { sidebarSelectedIdAtom, sidebarTreeAtom } from './SidebarAtoms';
|
||||
import type { SidebarItemProps } from './SidebarItem';
|
||||
@@ -151,11 +152,7 @@ export function Sidebar({ className }: Props) {
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
await router.navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: activeWorkspace?.id ?? null },
|
||||
search: (prev) => ({ ...prev, request_id: selected.id }),
|
||||
});
|
||||
setWorkspaceSearchParams({ request_id: selected.id });
|
||||
});
|
||||
|
||||
useKey(
|
||||
|
||||
@@ -2,13 +2,25 @@ import classNames from 'classnames';
|
||||
import { motion } from 'framer-motion';
|
||||
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||
import {useEnsureActiveCookieJar, useSubscribeActiveCookieJarId} from "../hooks/useActiveCookieJar";
|
||||
import {useSubscribeActiveEnvironmentId} from "../hooks/useActiveEnvironment";
|
||||
import {getActiveRequest, useActiveRequest} from '../hooks/useActiveRequest';
|
||||
import {useSubscribeActiveRequestId} from "../hooks/useActiveRequestId";
|
||||
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
||||
import {useDuplicateGrpcRequest} from "../hooks/useDuplicateGrpcRequest";
|
||||
import {useDuplicateHttpRequest} from "../hooks/useDuplicateHttpRequest";
|
||||
import { useFloatingSidebarHidden } from '../hooks/useFloatingSidebarHidden';
|
||||
import {useHotKey} from "../hooks/useHotKey";
|
||||
import { useImportData } from '../hooks/useImportData';
|
||||
import {useSubscribeRecentCookieJars} from "../hooks/useRecentCookieJars";
|
||||
import {useSubscribeRecentEnvironments} from "../hooks/useRecentEnvironments";
|
||||
import {useSubscribeRecentRequests} from "../hooks/useRecentRequests";
|
||||
import {useSubscribeRecentWorkspaces} from "../hooks/useRecentWorkspaces";
|
||||
import { useShouldFloatSidebar } from '../hooks/useShouldFloatSidebar';
|
||||
import { useSidebarHidden } from '../hooks/useSidebarHidden';
|
||||
import { useSidebarWidth } from '../hooks/useSidebarWidth';
|
||||
import {useSyncWorkspaceRequestTitle} from "../hooks/useSyncWorkspaceRequestTitle";
|
||||
import {useToggleCommandPalette} from "../hooks/useToggleCommandPalette";
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { Banner } from './core/Banner';
|
||||
import { Button } from './core/Button';
|
||||
@@ -31,6 +43,9 @@ const body = { gridArea: 'body' };
|
||||
const drag = { gridArea: 'drag' };
|
||||
|
||||
export function Workspace() {
|
||||
// First, subscribe to some things applicable to workspaces
|
||||
useGlobalWorkspaceHooks();
|
||||
|
||||
const workspaces = useWorkspaces();
|
||||
const { setWidth, width, resetWidth } = useSidebarWidth();
|
||||
const [sidebarHidden, setSidebarHidden] = useSidebarHidden();
|
||||
@@ -202,3 +217,40 @@ function WorkspaceBody() {
|
||||
|
||||
return <HttpRequestLayout activeRequest={activeRequest} style={body} />;
|
||||
}
|
||||
|
||||
function useGlobalWorkspaceHooks() {
|
||||
useEnsureActiveCookieJar();
|
||||
|
||||
useSubscribeActiveRequestId();
|
||||
useSubscribeActiveEnvironmentId();
|
||||
useSubscribeActiveCookieJarId();
|
||||
|
||||
useSubscribeRecentRequests();
|
||||
useSubscribeRecentWorkspaces();
|
||||
useSubscribeRecentEnvironments();
|
||||
useSubscribeRecentCookieJars();
|
||||
|
||||
useSyncWorkspaceRequestTitle();
|
||||
|
||||
const toggleCommandPalette = useToggleCommandPalette();
|
||||
useHotKey('command_palette.toggle', toggleCommandPalette);
|
||||
|
||||
const activeRequest = useActiveRequest();
|
||||
const duplicateHttpRequest = useDuplicateHttpRequest({
|
||||
id: activeRequest?.id ?? null,
|
||||
navigateAfter: true,
|
||||
});
|
||||
const duplicateGrpcRequest = useDuplicateGrpcRequest({
|
||||
id: activeRequest?.id ?? null,
|
||||
navigateAfter: true,
|
||||
});
|
||||
|
||||
useHotKey('http_request.duplicate', async () => {
|
||||
const activeRequest = getActiveRequest();
|
||||
if (activeRequest?.model === 'http_request') {
|
||||
await duplicateHttpRequest.mutateAsync();
|
||||
} else {
|
||||
await duplicateGrpcRequest.mutateAsync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
|
||||
import { useDeleteSendHistory } from '../hooks/useDeleteSendHistory';
|
||||
import { useOpenWorkspace } from '../hooks/useOpenWorkspace';
|
||||
import { useSettings } from '../hooks/useSettings';
|
||||
import { useSyncWorkspace } from '../hooks/useSyncWorkspace';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { showDialog } from '../lib/dialog';
|
||||
import { getWorkspace } from '../lib/store';
|
||||
@@ -31,7 +30,6 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
||||
const settings = useSettings();
|
||||
const openWorkspace = useOpenWorkspace();
|
||||
const openWorkspaceNewWindow = settings?.openWorkspaceNewWindow ?? null;
|
||||
const { sync } = useSyncWorkspace(activeWorkspace);
|
||||
|
||||
const orderedWorkspaces = useMemo(
|
||||
() => [...workspaces].sort((a, b) => (a.name.localeCompare(b.name) > 0 ? 1 : -1)),
|
||||
@@ -66,13 +64,6 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'sync',
|
||||
label: 'Sync Workspace',
|
||||
leftSlot: <Icon icon="folder_sync" />,
|
||||
hidden: !activeWorkspace?.settingSyncDir,
|
||||
onSelect: sync,
|
||||
},
|
||||
{
|
||||
key: 'delete-responses',
|
||||
label: 'Clear Send History',
|
||||
@@ -89,14 +80,7 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
||||
];
|
||||
|
||||
return { workspaceItems, extraItems };
|
||||
}, [
|
||||
orderedWorkspaces,
|
||||
activeWorkspace?.settingSyncDir,
|
||||
activeWorkspace?.id,
|
||||
sync,
|
||||
deleteSendHistory,
|
||||
createWorkspace,
|
||||
]);
|
||||
}, [orderedWorkspaces, activeWorkspace?.id, deleteSendHistory, createWorkspace]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
async (workspaceId: string | null) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { Button } from '../components/core/Button';
|
||||
import { HStack, VStack } from '../components/core/Stacks';
|
||||
import { Button } from './Button';
|
||||
import { HStack, VStack } from './Stacks';
|
||||
|
||||
export interface AlertProps {
|
||||
onHide: () => void;
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ButtonProps } from '../components/core/Button';
|
||||
import { Button } from '../components/core/Button';
|
||||
import { HStack } from '../components/core/Stacks';
|
||||
import type { ButtonProps } from './Button';
|
||||
import { Button } from './Button';
|
||||
import { HStack } from './Stacks';
|
||||
|
||||
export interface ConfirmProps {
|
||||
onHide: () => void;
|
||||
@@ -12,9 +12,9 @@ import {
|
||||
} from 'react';
|
||||
import type { XYCoord } from 'react-dnd';
|
||||
import { useDrag, useDrop } from 'react-dnd';
|
||||
import { usePrompt } from '../../hooks/usePrompt';
|
||||
import { useToggle } from '../../hooks/useToggle';
|
||||
import { generateId } from '../../lib/generateId';
|
||||
import { showPrompt } from '../../lib/prompt';
|
||||
import { DropMarker } from '../DropMarker';
|
||||
import { SelectFile } from '../SelectFile';
|
||||
import { Button } from './Button';
|
||||
@@ -556,7 +556,6 @@ function FileActionsDropdown({
|
||||
onChangeContentType: (contentType: string) => void;
|
||||
onDelete: () => void;
|
||||
}) {
|
||||
const prompt = usePrompt();
|
||||
const onChange = useCallback(
|
||||
(v: string) => {
|
||||
if (v === 'file') onChangeFile({ filePath: '' });
|
||||
@@ -573,7 +572,7 @@ function FileActionsDropdown({
|
||||
leftSlot: <Icon icon="pencil" />,
|
||||
hidden: !pair.isFile,
|
||||
onSelect: async () => {
|
||||
const contentType = await prompt({
|
||||
const contentType = await showPrompt({
|
||||
id: 'content-type',
|
||||
require: false,
|
||||
title: 'Override Content-Type',
|
||||
@@ -604,7 +603,7 @@ function FileActionsDropdown({
|
||||
leftSlot: <Icon icon="trash" />,
|
||||
},
|
||||
],
|
||||
[onChangeContentType, onChangeFile, onDelete, pair.contentType, pair.isFile, prompt],
|
||||
[onChangeContentType, onChangeFile, onDelete, pair.contentType, pair.isFile],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { PromptTextRequest } from '@yaakapp-internal/plugins';
|
||||
import type { FormEvent, ReactNode } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { Button } from '../components/core/Button';
|
||||
import { PlainInput } from '../components/core/PlainInput';
|
||||
import { HStack } from '../components/core/Stacks';
|
||||
import {PlainInput} from "./PlainInput";
|
||||
import { HStack } from './Stacks';
|
||||
import { Button } from './Button';
|
||||
|
||||
export type PromptProps = Omit<PromptTextRequest, 'id' | 'title' | 'description'> & {
|
||||
description?: ReactNode;
|
||||
@@ -3,34 +3,26 @@ import type { CookieJar } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai/index';
|
||||
import { useEffect } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { router } from '../lib/router';
|
||||
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
|
||||
import { cookieJarsAtom, useCookieJars } from './useCookieJars';
|
||||
|
||||
export const QUERY_COOKIE_JAR_ID = 'cookie_jar_id';
|
||||
|
||||
export const activeCookieJarIdAtom = atom<string>();
|
||||
|
||||
export const activeCookieJarAtom = atom<CookieJar | null>((get) => {
|
||||
const activeId = get(activeCookieJarIdAtom);
|
||||
return get(cookieJarsAtom)?.find((e) => e.id === activeId) ?? null;
|
||||
});
|
||||
|
||||
export function setActiveCookieJar(cookieJar: CookieJar) {
|
||||
router.navigate({
|
||||
from: '/workspaces/$workspaceId',
|
||||
search: (prev) => ({ ...prev, cookie_jar_id: cookieJar.id }),
|
||||
});
|
||||
}
|
||||
export const activeCookieJarAtom = atom<CookieJar | null>(null);
|
||||
|
||||
export function useActiveCookieJar() {
|
||||
return useAtomValue(activeCookieJarAtom);
|
||||
}
|
||||
|
||||
export function useSubscribeActiveCookieJarId() {
|
||||
const { cookie_jar_id } = useSearch({ strict: false });
|
||||
const search = useSearch({ strict: false });
|
||||
const cookieJarId = search.cookie_jar_id;
|
||||
const cookieJars = useAtomValue(cookieJarsAtom);
|
||||
useEffect(() => {
|
||||
jotaiStore.set(activeCookieJarIdAtom, cookie_jar_id ?? undefined);
|
||||
}, [cookie_jar_id]);
|
||||
if (search == null) return; // Happens during Vite hot reload
|
||||
const activeCookieJar = cookieJars?.find((j) => j.id == cookieJarId) ?? null;
|
||||
jotaiStore.set(activeCookieJarAtom, activeCookieJar);
|
||||
}, [cookieJarId, cookieJars, search]);
|
||||
}
|
||||
|
||||
export function getActiveCookieJar() {
|
||||
@@ -39,12 +31,12 @@ export function getActiveCookieJar() {
|
||||
|
||||
export function useEnsureActiveCookieJar() {
|
||||
const cookieJars = useCookieJars();
|
||||
const activeCookieJar = useActiveCookieJar();
|
||||
const { cookie_jar_id: activeCookieJarId } = useSearch({ from: '/workspaces/$workspaceId/' });
|
||||
|
||||
// Set the active cookie jar to the first one, if none set
|
||||
useEffect(() => {
|
||||
if (cookieJars == null) return; // Hasn't loaded yet
|
||||
if (cookieJars.find((j) => j.id === activeCookieJar?.id)) {
|
||||
if (cookieJars.find((j) => j.id === activeCookieJarId)) {
|
||||
return; // There's an active jar
|
||||
}
|
||||
|
||||
@@ -55,7 +47,7 @@ export function useEnsureActiveCookieJar() {
|
||||
}
|
||||
|
||||
// There's no active jar, so set it to the first one
|
||||
console.log('Setting active cookie jar to', firstJar.id);
|
||||
setActiveCookieJar(firstJar);
|
||||
}, [activeCookieJar?.id, cookieJars]);
|
||||
console.log('Setting active cookie jar to', cookieJars, activeCookieJarId, firstJar.id);
|
||||
setWorkspaceSearchParams({ cookie_jar_id: firstJar.id });
|
||||
}, [activeCookieJarId, cookieJars]);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useAtomValue } from 'jotai';
|
||||
import { atom } from 'jotai/index';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { router } from '../lib/router';
|
||||
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
|
||||
import { environmentsAtom } from './useEnvironments';
|
||||
|
||||
export const QUERY_ENVIRONMENT_ID = 'environment_id';
|
||||
@@ -18,11 +18,7 @@ export const activeEnvironmentAtom = atom<Environment | null>((get) => {
|
||||
|
||||
export function useActiveEnvironment() {
|
||||
const setId = useCallback(
|
||||
(id: string | null) =>
|
||||
router.navigate({
|
||||
from: '/workspaces/$workspaceId',
|
||||
search: (prev) => ({ ...prev, environment_id: id }),
|
||||
}),
|
||||
(id: string | null) => setWorkspaceSearchParams({ environment_id: id }),
|
||||
[],
|
||||
);
|
||||
const environment = useAtomValue(activeEnvironmentAtom);
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import type { Folder, Workspace } from '@yaakapp-internal/models';
|
||||
import { useMemo } from 'react';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { router } from '../lib/router';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { createFastMutation } from './useFastMutation';
|
||||
import { usePrompt } from './usePrompt';
|
||||
|
||||
function makeCommands({ prompt }: { prompt: ReturnType<typeof usePrompt> }) {
|
||||
return {
|
||||
createWorkspace: createFastMutation<Workspace, void, Partial<Workspace>>({
|
||||
mutationKey: ['create_workspace'],
|
||||
mutationFn: (patch) => invokeCmd<Workspace>('cmd_update_workspace', { workspace: patch }),
|
||||
onSuccess: async (workspace) => {
|
||||
await router.navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: workspace.id },
|
||||
});
|
||||
},
|
||||
onSettled: () => trackEvent('workspace', 'create'),
|
||||
}),
|
||||
|
||||
createFolder: createFastMutation<
|
||||
Folder | null,
|
||||
void,
|
||||
Partial<Pick<Folder, 'name' | 'sortPriority' | 'folderId'>>
|
||||
>({
|
||||
mutationKey: ['create_folder'],
|
||||
mutationFn: async (patch) => {
|
||||
const workspaceId = getActiveWorkspaceId();
|
||||
if (workspaceId == null) {
|
||||
throw new Error("Cannot create folder when there's no active workspace");
|
||||
}
|
||||
|
||||
if (!patch.name) {
|
||||
const name = await prompt({
|
||||
id: 'new-folder',
|
||||
label: 'Name',
|
||||
defaultValue: 'Folder',
|
||||
title: 'New Folder',
|
||||
confirmText: 'Create',
|
||||
placeholder: 'Name',
|
||||
});
|
||||
if (name == null) return null;
|
||||
|
||||
patch.name = name;
|
||||
}
|
||||
|
||||
patch.sortPriority = patch.sortPriority || -Date.now();
|
||||
return invokeCmd<Folder>('cmd_update_folder', { folder: { workspaceId, ...patch } });
|
||||
},
|
||||
onSettled: () => trackEvent('folder', 'create'),
|
||||
}),
|
||||
} as const;
|
||||
}
|
||||
|
||||
export function useCommands() {
|
||||
const prompt = usePrompt();
|
||||
return useMemo(() => makeCommands({ prompt }), [prompt]);
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { useCallback } from 'react';
|
||||
import type { DialogProps } from '../components/core/Dialog';
|
||||
import { showDialog } from '../lib/dialog';
|
||||
import type { ConfirmProps } from './Confirm';
|
||||
import { Confirm } from './Confirm';
|
||||
|
||||
export function useConfirm() {
|
||||
return useCallback(
|
||||
({
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
variant,
|
||||
confirmText,
|
||||
}: {
|
||||
id: string;
|
||||
title: DialogProps['title'];
|
||||
description?: DialogProps['description'];
|
||||
variant?: ConfirmProps['variant'];
|
||||
confirmText?: ConfirmProps['confirmText'];
|
||||
}) =>
|
||||
new Promise((onResult: ConfirmProps['onResult']) => {
|
||||
showDialog({
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
hideX: true,
|
||||
size: 'sm',
|
||||
render: ({ hide }) => Confirm({ onHide: hide, variant, onResult, confirmText }),
|
||||
});
|
||||
}),
|
||||
[],
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
import type { CookieJar } from '@yaakapp-internal/models';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { showPrompt } from '../lib/prompt';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { usePrompt } from './usePrompt';
|
||||
|
||||
export function useCreateCookieJar() {
|
||||
const prompt = usePrompt();
|
||||
|
||||
return useFastMutation<CookieJar | null>({
|
||||
mutationKey: ['create_cookie_jar'],
|
||||
mutationFn: async () => {
|
||||
@@ -15,7 +13,7 @@ export function useCreateCookieJar() {
|
||||
if (workspaceId == null) {
|
||||
throw new Error("Cannot create cookie jar when there's no active workspace");
|
||||
}
|
||||
const name = await prompt({
|
||||
const name = await showPrompt({
|
||||
id: 'new-cookie-jar',
|
||||
title: 'New CookieJar',
|
||||
placeholder: 'My Jar',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useMemo } from 'react';
|
||||
import type { DropdownItem } from '../components/core/Dropdown';
|
||||
import { Icon } from '../components/core/Icon';
|
||||
import { createFolder } from '../lib/commands';
|
||||
import { generateId } from '../lib/generateId';
|
||||
import { BODY_TYPE_GRAPHQL } from '../lib/model_util';
|
||||
import { getActiveRequest } from './useActiveRequest';
|
||||
import { useCommands } from './useCommands';
|
||||
import { useCreateGrpcRequest } from './useCreateGrpcRequest';
|
||||
import { useCreateHttpRequest } from './useCreateHttpRequest';
|
||||
|
||||
@@ -19,7 +19,6 @@ export function useCreateDropdownItems({
|
||||
} = {}): DropdownItem[] {
|
||||
const { mutate: createHttpRequest } = useCreateHttpRequest();
|
||||
const { mutate: createGrpcRequest } = useCreateGrpcRequest();
|
||||
const { createFolder } = useCommands();
|
||||
|
||||
return useMemo((): DropdownItem[] => {
|
||||
const folderId =
|
||||
@@ -66,5 +65,5 @@ export function useCreateDropdownItems({
|
||||
},
|
||||
]) as DropdownItem[]),
|
||||
];
|
||||
}, [createFolder, createGrpcRequest, createHttpRequest, folderIdOption, hideFolder, hideIcons]);
|
||||
}, [createGrpcRequest, createHttpRequest, folderIdOption, hideFolder, hideIcons]);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import type { Environment } from '@yaakapp-internal/models';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { showPrompt } from '../lib/prompt';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useActiveEnvironment } from './useActiveEnvironment';
|
||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { usePrompt } from './usePrompt';
|
||||
|
||||
export function useCreateEnvironment() {
|
||||
const [, setActiveEnvironmentId] = useActiveEnvironment();
|
||||
const prompt = usePrompt();
|
||||
|
||||
return useFastMutation<Environment | null, unknown, Environment | null>({
|
||||
mutationKey: ['create_environment'],
|
||||
@@ -18,7 +17,7 @@ export function useCreateEnvironment() {
|
||||
}
|
||||
|
||||
const workspaceId = getActiveWorkspaceId();
|
||||
const name = await prompt({
|
||||
const name = await showPrompt({
|
||||
id: 'new-environment',
|
||||
title: 'New Environment',
|
||||
description: 'Create multiple environments with different sets of variables',
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import type { Workspace } from '@yaakapp-internal/models';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { showConfirm } from '../lib/confirm';
|
||||
import { router } from '../lib/router';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useConfirm } from './useConfirm';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDeleteActiveWorkspace() {
|
||||
const confirm = useConfirm();
|
||||
|
||||
return useFastMutation<Workspace | null, string>({
|
||||
mutationKey: ['delete_workspace'],
|
||||
mutationFn: async () => {
|
||||
const workspace = getActiveWorkspace();
|
||||
const confirmed = await confirm({
|
||||
const confirmed = await showConfirm({
|
||||
id: 'delete-workspace',
|
||||
title: 'Delete Workspace',
|
||||
variant: 'delete',
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { showConfirm } from '../lib/confirm';
|
||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||
import { getGrpcRequest } from '../lib/store';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useConfirm } from './useConfirm';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDeleteAnyGrpcRequest() {
|
||||
const confirm = useConfirm();
|
||||
|
||||
return useFastMutation<GrpcRequest | null, string, string>({
|
||||
mutationKey: ['delete_any_grpc_request'],
|
||||
mutationFn: async (id) => {
|
||||
const request = await getGrpcRequest(id);
|
||||
if (request == null) return null;
|
||||
|
||||
const confirmed = await confirm({
|
||||
const confirmed = await showConfirm({
|
||||
id: 'delete-grpc-request',
|
||||
title: 'Delete Request',
|
||||
variant: 'delete',
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { showConfirm } from '../lib/confirm';
|
||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||
import { getHttpRequest } from '../lib/store';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useConfirm } from './useConfirm';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDeleteAnyHttpRequest() {
|
||||
const confirm = useConfirm();
|
||||
|
||||
return useFastMutation<HttpRequest | null, string, string>({
|
||||
mutationKey: ['delete_any_http_request'],
|
||||
mutationFn: async (id) => {
|
||||
const request = await getHttpRequest(id);
|
||||
if (request == null) return null;
|
||||
|
||||
const confirmed = await confirm({
|
||||
const confirmed = await showConfirm({
|
||||
id: 'delete-request',
|
||||
title: 'Delete Request',
|
||||
variant: 'delete',
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import type { CookieJar } from '@yaakapp-internal/models';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { showConfirm } from '../lib/confirm';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useConfirm } from './useConfirm';
|
||||
import { cookieJarsAtom } from './useCookieJars';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { removeModelById } from './useSyncModelStores';
|
||||
|
||||
export function useDeleteCookieJar(cookieJar: CookieJar | null) {
|
||||
const confirm = useConfirm();
|
||||
const setCookieJars = useSetAtom(cookieJarsAtom);
|
||||
|
||||
return useFastMutation<CookieJar | null, string>({
|
||||
mutationKey: ['delete_cookie_jar', cookieJar?.id],
|
||||
mutationFn: async () => {
|
||||
const confirmed = await confirm({
|
||||
const confirmed = await showConfirm({
|
||||
id: 'delete-cookie-jar',
|
||||
title: 'Delete CookieJar',
|
||||
variant: 'delete',
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import type { Environment } from '@yaakapp-internal/models';
|
||||
import {useSetAtom} from "jotai";
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { showConfirm } from '../lib/confirm';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useConfirm } from './useConfirm';
|
||||
import {environmentsAtom} from "./useEnvironments";
|
||||
import {removeModelById} from "./useSyncModelStores";
|
||||
import { environmentsAtom } from './useEnvironments';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { removeModelById } from './useSyncModelStores';
|
||||
|
||||
export function useDeleteEnvironment(environment: Environment | null) {
|
||||
const confirm = useConfirm();
|
||||
const setEnvironments = useSetAtom(environmentsAtom);
|
||||
|
||||
return useFastMutation<Environment | null, string>({
|
||||
mutationKey: ['delete_environment', environment?.id],
|
||||
mutationFn: async () => {
|
||||
const confirmed = await confirm({
|
||||
const confirmed = await showConfirm({
|
||||
id: 'delete-environment',
|
||||
title: 'Delete Environment',
|
||||
variant: 'delete',
|
||||
@@ -33,6 +32,6 @@ export function useDeleteEnvironment(environment: Environment | null) {
|
||||
if (environment == null) return;
|
||||
|
||||
setEnvironments(removeModelById(environment));
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,22 +2,21 @@ import type { Folder } from '@yaakapp-internal/models';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { showConfirm } from '../lib/confirm';
|
||||
import { getFolder } from '../lib/store';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useConfirm } from './useConfirm';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { foldersAtom } from './useFolders';
|
||||
import { removeModelById } from './useSyncModelStores';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDeleteFolder(id: string | null) {
|
||||
const confirm = useConfirm();
|
||||
const setFolders = useSetAtom(foldersAtom);
|
||||
|
||||
return useFastMutation<Folder | null, string>({
|
||||
mutationKey: ['delete_folder', id],
|
||||
mutationFn: async () => {
|
||||
const folder = await getFolder(id);
|
||||
const confirmed = await confirm({
|
||||
const confirmed = await showConfirm({
|
||||
id: 'delete-folder',
|
||||
title: 'Delete Folder',
|
||||
variant: 'delete',
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { useSetAtom } from 'jotai/index';
|
||||
import { showAlert } from '../lib/alert';
|
||||
import { showConfirm } from '../lib/confirm';
|
||||
import { pluralizeCount } from '../lib/pluralize';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { useConfirm } from './useConfirm';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useGrpcConnections } from './useGrpcConnections';
|
||||
import { httpResponsesAtom, useHttpResponses } from './useHttpResponses';
|
||||
|
||||
export function useDeleteSendHistory() {
|
||||
const confirm = useConfirm();
|
||||
const setHttpResponses = useSetAtom(httpResponsesAtom);
|
||||
const httpResponses = useHttpResponses();
|
||||
const grpcConnections = useGrpcConnections();
|
||||
@@ -30,7 +29,7 @@ export function useDeleteSendHistory() {
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmed = await confirm({
|
||||
const confirmed = await showConfirm({
|
||||
id: 'delete-send-history',
|
||||
title: 'Clear Send History',
|
||||
variant: 'delete',
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import type { DialogProps } from '../components/core/Dialog';
|
||||
import { showDialog } from '../lib/dialog';
|
||||
import type { PromptProps } from './Prompt';
|
||||
import { Prompt } from './Prompt';
|
||||
|
||||
type Props = Pick<DialogProps, 'title' | 'description'> &
|
||||
Omit<PromptProps, 'onClose' | 'onCancel' | 'onResult'> & { id: string };
|
||||
|
||||
export function usePrompt() {
|
||||
return ({ id, title, description, ...props }: Props) =>
|
||||
new Promise((resolve: PromptProps['onResult']) => {
|
||||
showDialog({
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
hideX: true,
|
||||
size: 'sm',
|
||||
onClose: () => {
|
||||
// Click backdrop, close, or escape
|
||||
resolve(null);
|
||||
},
|
||||
render: ({ hide }) =>
|
||||
Prompt({
|
||||
onCancel: () => {
|
||||
// Click cancel button within dialog
|
||||
resolve(null);
|
||||
hide();
|
||||
},
|
||||
onResult: (v) => {
|
||||
resolve(v);
|
||||
hide();
|
||||
},
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
|
||||
import { activeCookieJarIdAtom } from './useActiveCookieJar';
|
||||
import {activeCookieJarAtom} from "./useActiveCookieJar";
|
||||
import { activeWorkspaceIdAtom, useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useCookieJars } from './useCookieJars';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
@@ -29,9 +29,9 @@ export function useRecentCookieJars() {
|
||||
|
||||
export function useSubscribeRecentCookieJars() {
|
||||
useEffect(() => {
|
||||
return jotaiStore.sub(activeCookieJarIdAtom, async () => {
|
||||
return jotaiStore.sub(activeCookieJarAtom, async () => {
|
||||
const activeWorkspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||
const activeCookieJarId = jotaiStore.get(activeCookieJarIdAtom);
|
||||
const activeCookieJarId = jotaiStore.get(activeCookieJarAtom)?.id ?? null;
|
||||
if (activeWorkspaceId == null) return;
|
||||
if (activeCookieJarId == null) return;
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { usePrompt } from './usePrompt';
|
||||
import {showPrompt} from "../lib/prompt";
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
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();
|
||||
@@ -18,7 +17,7 @@ export function useRenameRequest(requestId: string | null) {
|
||||
const request = requests.find((r) => r.id === requestId);
|
||||
if (request == null) return;
|
||||
|
||||
const name = await prompt({
|
||||
const name = await showPrompt({
|
||||
id: 'rename-request',
|
||||
title: 'Rename Request',
|
||||
description:
|
||||
|
||||
@@ -4,9 +4,9 @@ import { applySync, calculateSync } from '@yaakapp-internal/sync';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { VStack } from '../components/core/Stacks';
|
||||
import {showConfirm} from "../lib/confirm";
|
||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||
import { pluralizeCount } from '../lib/pluralize';
|
||||
import { useConfirm } from './useConfirm';
|
||||
|
||||
export function useSyncWorkspace(
|
||||
workspace: Workspace | null,
|
||||
@@ -16,12 +16,10 @@ export function useSyncWorkspace(
|
||||
debounceMillis?: number;
|
||||
} = {},
|
||||
) {
|
||||
const confirm = useConfirm();
|
||||
|
||||
const sync = useCallback(async () => {
|
||||
if (workspace == null) return;
|
||||
if (workspace == null || workspace.settingSyncDir) return;
|
||||
|
||||
const ops = await calculateSync(workspace);
|
||||
const ops = await calculateSync(workspace) ?? [];
|
||||
if (ops.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -33,7 +31,7 @@ export function useSyncWorkspace(
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmed = await confirm({
|
||||
const confirmed = await showConfirm({
|
||||
id: 'commit-sync',
|
||||
title: 'Filesystem Changes Detected',
|
||||
confirmText: 'Apply Changes',
|
||||
@@ -92,7 +90,7 @@ export function useSyncWorkspace(
|
||||
if (confirmed) {
|
||||
await applySync(workspace, ops);
|
||||
}
|
||||
}, [confirm, workspace]);
|
||||
}, [workspace]);
|
||||
|
||||
const debouncedSync = useMemo(() => {
|
||||
return debounce(sync, debounceMillis);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AlertProps } from '../components/core/Alert';
|
||||
import { Alert } from '../components/core/Alert';
|
||||
import type { DialogProps } from '../components/core/Dialog';
|
||||
import type { AlertProps } from '../hooks/Alert';
|
||||
import { Alert } from '../hooks/Alert';
|
||||
import { showDialog } from './dialog';
|
||||
|
||||
interface AlertArgs {
|
||||
|
||||
51
src-web/lib/commands.ts
Normal file
51
src-web/lib/commands.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { Folder, Workspace } from '@yaakapp-internal/models';
|
||||
import { getActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
||||
import { createFastMutation } from '../hooks/useFastMutation';
|
||||
import { trackEvent } from './analytics';
|
||||
import { showPrompt } from './prompt';
|
||||
import { router } from './router';
|
||||
import { invokeCmd } from './tauri';
|
||||
|
||||
export const createWorkspace = createFastMutation<Workspace, void, Partial<Workspace>>({
|
||||
mutationKey: ['create_workspace'],
|
||||
mutationFn: (patch) => invokeCmd<Workspace>('cmd_update_workspace', { workspace: patch }),
|
||||
onSuccess: async (workspace) => {
|
||||
await router.navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: workspace.id },
|
||||
});
|
||||
},
|
||||
onSettled: () => trackEvent('workspace', 'create'),
|
||||
});
|
||||
|
||||
export const createFolder = createFastMutation<
|
||||
Folder | null,
|
||||
void,
|
||||
Partial<Pick<Folder, 'name' | 'sortPriority' | 'folderId'>>
|
||||
>({
|
||||
mutationKey: ['create_folder'],
|
||||
mutationFn: async (patch) => {
|
||||
const workspaceId = getActiveWorkspaceId();
|
||||
if (workspaceId == null) {
|
||||
throw new Error("Cannot create folder when there's no active workspace");
|
||||
}
|
||||
|
||||
if (!patch.name) {
|
||||
const name = await showPrompt({
|
||||
id: 'new-folder',
|
||||
label: 'Name',
|
||||
defaultValue: 'Folder',
|
||||
title: 'New Folder',
|
||||
confirmText: 'Create',
|
||||
placeholder: 'Name',
|
||||
});
|
||||
if (name == null) return null;
|
||||
|
||||
patch.name = name;
|
||||
}
|
||||
|
||||
patch.sortPriority = patch.sortPriority || -Date.now();
|
||||
return invokeCmd<Folder>('cmd_update_folder', { folder: { workspaceId, ...patch } });
|
||||
},
|
||||
onSettled: () => trackEvent('folder', 'create'),
|
||||
});
|
||||
25
src-web/lib/confirm.ts
Normal file
25
src-web/lib/confirm.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { ConfirmProps } from '../components/core/Confirm';
|
||||
import { Confirm } from '../components/core/Confirm';
|
||||
import type { DialogProps } from '../components/core/Dialog';
|
||||
import { showDialog } from './dialog';
|
||||
|
||||
interface ConfirmArgs {
|
||||
id: string;
|
||||
title: DialogProps['title'];
|
||||
description?: DialogProps['description'];
|
||||
variant?: ConfirmProps['variant'];
|
||||
confirmText?: ConfirmProps['confirmText'];
|
||||
}
|
||||
|
||||
export async function showConfirm({ id, title, description, variant, confirmText }: ConfirmArgs) {
|
||||
return new Promise((onResult: ConfirmProps['onResult']) => {
|
||||
showDialog({
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
hideX: true,
|
||||
size: 'sm',
|
||||
render: ({ hide }) => Confirm({ onHide: hide, variant, onResult, confirmText }),
|
||||
});
|
||||
});
|
||||
}
|
||||
36
src-web/lib/prompt.ts
Normal file
36
src-web/lib/prompt.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { DialogProps } from '../components/core/Dialog';
|
||||
import type { PromptProps } from '../components/core/Prompt';
|
||||
import { Prompt } from '../components/core/Prompt';
|
||||
import { showDialog } from './dialog';
|
||||
|
||||
type PromptArgs = Pick<DialogProps, 'title' | 'description'> &
|
||||
Omit<PromptProps, 'onClose' | 'onCancel' | 'onResult'> & { id: string };
|
||||
|
||||
export async function showPrompt({ id, title, description, ...props }: PromptArgs) {
|
||||
return new Promise((resolve: PromptProps['onResult']) => {
|
||||
showDialog({
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
hideX: true,
|
||||
size: 'sm',
|
||||
onClose: () => {
|
||||
// Click backdrop, close, or escape
|
||||
resolve(null);
|
||||
},
|
||||
render: ({ hide }) =>
|
||||
Prompt({
|
||||
onCancel: () => {
|
||||
// Click cancel button within dialog
|
||||
resolve(null);
|
||||
hide();
|
||||
},
|
||||
onResult: (v) => {
|
||||
resolve(v);
|
||||
hide();
|
||||
},
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
});
|
||||
}
|
||||
20
src-web/lib/setWorkspaceSearchParams.ts
Normal file
20
src-web/lib/setWorkspaceSearchParams.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { router } from './router.js';
|
||||
|
||||
/**
|
||||
* Setting search params using "from" on the global router instance in tanstack router does not
|
||||
* currently behave very well, so this is a wrapper function that gives a typesafe interface
|
||||
* for the same thing.
|
||||
*/
|
||||
export function setWorkspaceSearchParams(
|
||||
search: Partial<{
|
||||
cookie_jar_id: string | null;
|
||||
environment_id: string | null;
|
||||
request_id: string | null;
|
||||
}>,
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(router as any).navigate({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
search: (prev: any) => ({ ...prev, ...search }),
|
||||
});
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { Workspace } from '../../../components/Workspace'
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { Workspace } from '../../../components/Workspace';
|
||||
|
||||
interface WorkspaceSearchSchema {
|
||||
request_id?: string | null
|
||||
environment_id?: string | null
|
||||
cookie_jar_id?: string | null
|
||||
request_id?: string | null;
|
||||
environment_id?: string | null;
|
||||
cookie_jar_id?: string | null;
|
||||
}
|
||||
|
||||
export const Route = createFileRoute('/workspaces/$workspaceId/')({
|
||||
@@ -14,8 +14,8 @@ export const Route = createFileRoute('/workspaces/$workspaceId/')({
|
||||
environment_id: search.environment_id as string,
|
||||
cookie_jar_id: search.cookie_jar_id as string,
|
||||
}),
|
||||
})
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <Workspace />
|
||||
return <Workspace />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user