mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-24 02:11:25 +01:00
Remove useNavigate everywhere, and make request a query param. And convert dialog to Jotai
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import classNames from 'classnames';
|
||||
import { fuzzyFilter } from 'fuzzbunny';
|
||||
import type { KeyboardEvent, ReactNode } from 'react';
|
||||
@@ -13,7 +12,6 @@ import { useCreateHttpRequest } from '../hooks/useCreateHttpRequest';
|
||||
import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
|
||||
import { useDebouncedState } from '../hooks/useDebouncedState';
|
||||
import { useDeleteRequest } from '../hooks/useDeleteRequest';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
import { useEnvironments } from '../hooks/useEnvironments';
|
||||
import type { HotkeyAction } from '../hooks/useHotKey';
|
||||
import { useHotKey } from '../hooks/useHotKey';
|
||||
@@ -29,7 +27,9 @@ import { useScrollIntoView } from '../hooks/useScrollIntoView';
|
||||
import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest';
|
||||
import { useSidebarHidden } from '../hooks/useSidebarHidden';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { showDialog, toggleDialog } from '../lib/dialog';
|
||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||
import { router } from '../lib/router';
|
||||
import { CookieDialog } from './CookieDialog';
|
||||
import { Button } from './core/Button';
|
||||
import { Heading } from './core/Heading';
|
||||
@@ -70,16 +70,14 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
||||
const openWorkspace = useOpenWorkspace();
|
||||
const createHttpRequest = useCreateHttpRequest();
|
||||
const { createFolder } = useCommands();
|
||||
const [activeCookieJar] = useActiveCookieJar();
|
||||
const activeCookieJar = useActiveCookieJar();
|
||||
const createGrpcRequest = useCreateGrpcRequest();
|
||||
const createEnvironment = useCreateEnvironment();
|
||||
const dialog = useDialog();
|
||||
const sendRequest = useSendAnyHttpRequest();
|
||||
const renameRequest = useRenameRequest(activeRequest?.id ?? null);
|
||||
const deleteRequest = useDeleteRequest(activeRequest?.id ?? null);
|
||||
const [, setSidebarHidden] = useSidebarHidden();
|
||||
const openSettings = useOpenSettings();
|
||||
const navigate = useNavigate();
|
||||
const { baseEnvironment } = useEnvironments();
|
||||
|
||||
const workspaceCommands = useMemo<CommandPaletteItem[]>(() => {
|
||||
@@ -109,7 +107,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
||||
key: 'cookies.show',
|
||||
label: 'Show Cookies',
|
||||
onSelect: async () => {
|
||||
dialog.show({
|
||||
showDialog({
|
||||
id: 'cookies',
|
||||
title: 'Manage Cookies',
|
||||
size: 'full',
|
||||
@@ -127,7 +125,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
||||
label: 'Edit Environment',
|
||||
action: 'environmentEditor.toggle',
|
||||
onSelect: () => {
|
||||
dialog.toggle({
|
||||
toggleDialog({
|
||||
id: 'environment-editor',
|
||||
noPadding: true,
|
||||
size: 'lg',
|
||||
@@ -195,7 +193,6 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
||||
createHttpRequest,
|
||||
createWorkspace,
|
||||
deleteRequest.mutate,
|
||||
dialog,
|
||||
httpRequestActions,
|
||||
openSettings.mutate,
|
||||
renameRequest.mutate,
|
||||
@@ -284,13 +281,10 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
||||
</HStack>
|
||||
),
|
||||
onSelect: async () => {
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: {
|
||||
workspaceId: r.workspaceId,
|
||||
requestId: r.id,
|
||||
},
|
||||
search: (prev) => ({ ...prev }),
|
||||
await router.navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: r.workspaceId },
|
||||
search: (prev) => ({ ...prev, request_id: r.id }),
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -331,7 +325,6 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
||||
}, [
|
||||
workspaceCommands,
|
||||
sortedRequests,
|
||||
navigate,
|
||||
sortedEnvironments,
|
||||
activeEnvironment?.id,
|
||||
setActiveEnvironmentId,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useActiveCookieJar } from '../hooks/useActiveCookieJar';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { setActiveCookieJar, useActiveCookieJar } from '../hooks/useActiveCookieJar';
|
||||
import { cookieJarsAtom } from '../hooks/useCookieJars';
|
||||
import { useCreateCookieJar } from '../hooks/useCreateCookieJar';
|
||||
import { useDeleteCookieJar } from '../hooks/useDeleteCookieJar';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
import { usePrompt } from '../hooks/usePrompt';
|
||||
import { useUpdateCookieJar } from '../hooks/useUpdateCookieJar';
|
||||
import { showDialog } from '../lib/dialog';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { CookieDialog } from './CookieDialog';
|
||||
import { Dropdown, type DropdownItem } from './core/Dropdown';
|
||||
@@ -14,21 +14,20 @@ import { IconButton } from './core/IconButton';
|
||||
import { InlineCode } from './core/InlineCode';
|
||||
|
||||
export const CookieDropdown = memo(function CookieDropdown() {
|
||||
const [activeCookieJar, setActiveCookieJarId] = useActiveCookieJar();
|
||||
const activeCookieJar = useActiveCookieJar();
|
||||
const updateCookieJar = useUpdateCookieJar(activeCookieJar?.id ?? null);
|
||||
const deleteCookieJar = useDeleteCookieJar(activeCookieJar ?? null);
|
||||
const createCookieJar = useCreateCookieJar();
|
||||
const dialog = useDialog();
|
||||
const prompt = usePrompt();
|
||||
|
||||
const getItems = useCallback((): DropdownItem[] => {
|
||||
const items = useMemo((): DropdownItem[] => {
|
||||
const cookieJars = jotaiStore.get(cookieJarsAtom) ?? [];
|
||||
return [
|
||||
...cookieJars.map((j) => ({
|
||||
key: j.id,
|
||||
label: j.name,
|
||||
leftSlot: <Icon icon={j.id === activeCookieJar?.id ? 'check' : 'empty'} />,
|
||||
onSelect: () => setActiveCookieJarId(j.id),
|
||||
onSelect: () => setActiveCookieJar(j),
|
||||
})),
|
||||
...((cookieJars.length > 0 && activeCookieJar != null
|
||||
? [
|
||||
@@ -39,7 +38,7 @@ export const CookieDropdown = memo(function CookieDropdown() {
|
||||
leftSlot: <Icon icon="cookie" />,
|
||||
onSelect: () => {
|
||||
if (activeCookieJar == null) return;
|
||||
dialog.show({
|
||||
showDialog({
|
||||
id: 'cookies',
|
||||
title: 'Manage Cookies',
|
||||
size: 'full',
|
||||
@@ -90,18 +89,10 @@ export const CookieDropdown = memo(function CookieDropdown() {
|
||||
onSelect: () => createCookieJar.mutate(),
|
||||
},
|
||||
];
|
||||
}, [
|
||||
activeCookieJar,
|
||||
createCookieJar,
|
||||
deleteCookieJar,
|
||||
dialog,
|
||||
prompt,
|
||||
setActiveCookieJarId,
|
||||
updateCookieJar,
|
||||
]);
|
||||
}, [activeCookieJar, createCookieJar, deleteCookieJar, prompt, updateCookieJar]);
|
||||
|
||||
return (
|
||||
<Dropdown items={getItems}>
|
||||
<Dropdown items={items}>
|
||||
<IconButton size="sm" icon="cookie" title="Cookie Jar" />
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
import type { DialogState } from './Dialogs';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const DialogContext = createContext<DialogState>({} as DialogState);
|
||||
@@ -1,74 +1,29 @@
|
||||
import React, { useContext, useMemo, useState } from 'react';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import React from 'react';
|
||||
import { dialogsAtom, hideDialog } from '../lib/dialog';
|
||||
import { Dialog, type DialogProps } from './core/Dialog';
|
||||
import { DialogContext } from './DialogContext';
|
||||
|
||||
type DialogEntry = {
|
||||
export type DialogInstance = {
|
||||
id: string;
|
||||
render: ({ hide }: { hide: () => void }) => React.ReactNode;
|
||||
} & Omit<DialogProps, 'open' | 'children'>;
|
||||
|
||||
export interface DialogState {
|
||||
dialogs: DialogEntry[];
|
||||
actions: Actions;
|
||||
}
|
||||
|
||||
interface Actions {
|
||||
show: (d: DialogEntry) => void;
|
||||
toggle: (d: DialogEntry) => void;
|
||||
hide: (id: string) => void;
|
||||
}
|
||||
|
||||
export const DialogProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [dialogs, setDialogs] = useState<DialogState['dialogs']>([]);
|
||||
const actions = useMemo<Actions>(
|
||||
() => ({
|
||||
show({ id, ...props }: DialogEntry) {
|
||||
trackEvent('dialog', 'show', { id });
|
||||
setDialogs((a) => [...a.filter((d) => d.id !== id), { id, ...props }]);
|
||||
},
|
||||
toggle({ id, ...props }: DialogEntry) {
|
||||
if (dialogs.some((d) => d.id === id)) this.hide(id);
|
||||
else this.show({ id, ...props });
|
||||
},
|
||||
hide: (id: string) => {
|
||||
setDialogs((a) => a.filter((d) => d.id !== id));
|
||||
},
|
||||
}),
|
||||
[dialogs],
|
||||
);
|
||||
|
||||
const state: DialogState = {
|
||||
dialogs,
|
||||
actions,
|
||||
};
|
||||
|
||||
return <DialogContext.Provider value={state}>{children}</DialogContext.Provider>;
|
||||
};
|
||||
|
||||
function DialogInstance({ id, render, onClose, ...props }: DialogEntry) {
|
||||
const { actions } = useContext(DialogContext);
|
||||
const children = render({ hide: () => actions.hide(id) });
|
||||
return (
|
||||
<Dialog
|
||||
open
|
||||
onClose={() => {
|
||||
onClose?.();
|
||||
actions.hide(id);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export function Dialogs() {
|
||||
const { dialogs } = useContext(DialogContext);
|
||||
const dialogs = useAtomValue(dialogsAtom);
|
||||
return (
|
||||
<>
|
||||
{dialogs.map((props: DialogEntry) => (
|
||||
<DialogInstance key={props.id} {...props} />
|
||||
{dialogs.map(({ render, onClose, id, ...props }: DialogInstance) => (
|
||||
<Dialog
|
||||
open
|
||||
key={id}
|
||||
onClose={() => {
|
||||
onClose?.();
|
||||
hideDialog(id);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{render({ hide: () => hideDialog(id) })}
|
||||
</Dialog>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -2,12 +2,12 @@ import classNames from 'classnames';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
|
||||
import { useEnvironments } from '../hooks/useEnvironments';
|
||||
import { toggleDialog } from '../lib/dialog';
|
||||
import type { ButtonProps } from './core/Button';
|
||||
import { Button } from './core/Button';
|
||||
import type { DropdownItem } from './core/Dropdown';
|
||||
import { Dropdown } from './core/Dropdown';
|
||||
import { Icon } from './core/Icon';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
import { EnvironmentEditDialog } from './EnvironmentEditDialog';
|
||||
|
||||
type Props = {
|
||||
@@ -20,17 +20,16 @@ export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdo
|
||||
}: Props) {
|
||||
const { subEnvironments, baseEnvironment } = useEnvironments();
|
||||
const [activeEnvironment, setActiveEnvironmentId] = useActiveEnvironment();
|
||||
const dialog = useDialog();
|
||||
|
||||
const showEnvironmentDialog = useCallback(() => {
|
||||
dialog.toggle({
|
||||
toggleDialog({
|
||||
id: 'environment-editor',
|
||||
noPadding: true,
|
||||
size: 'lg',
|
||||
className: 'h-[80vh]',
|
||||
render: () => <EnvironmentEditDialog initialEnvironment={activeEnvironment} />,
|
||||
});
|
||||
}, [dialog, activeEnvironment]);
|
||||
}, [activeEnvironment]);
|
||||
|
||||
const items: DropdownItem[] = useMemo(
|
||||
() => [
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
useSubscribeActiveCookieJarId,
|
||||
} from '../hooks/useActiveCookieJar';
|
||||
import { useSubscribeActiveEnvironmentId } from '../hooks/useActiveEnvironment';
|
||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||
import { getActiveRequest, useActiveRequest } from '../hooks/useActiveRequest';
|
||||
import { useSubscribeActiveRequestId } from '../hooks/useActiveRequestId';
|
||||
import { useActiveWorkspace, useSubscribeActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
||||
import { useActiveWorkspaceChangedToast } from '../hooks/useActiveWorkspaceChangedToast';
|
||||
@@ -78,6 +78,7 @@ export function GlobalHooks() {
|
||||
navigateAfter: true,
|
||||
});
|
||||
useHotKey('http_request.duplicate', async () => {
|
||||
const activeRequest = getActiveRequest();
|
||||
if (activeRequest?.model === 'http_request') {
|
||||
await duplicateHttpRequest.mutateAsync();
|
||||
} else {
|
||||
|
||||
@@ -5,8 +5,8 @@ import type { EditorView } from 'codemirror';
|
||||
import { formatSdl } from 'format-graphql';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useLocalStorage } from 'react-use';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
import { useIntrospectGraphQL } from '../hooks/useIntrospectGraphQL';
|
||||
import { showDialog } from '../lib/dialog';
|
||||
import { tryFormatJson } from '../lib/formatters';
|
||||
import { Button } from './core/Button';
|
||||
import { Dropdown } from './core/Dropdown';
|
||||
@@ -62,8 +62,6 @@ export function GraphQLEditor({ request, onChange, baseRequest, ...extraEditorPr
|
||||
updateSchema(editorViewRef.current, schema ?? undefined);
|
||||
}, [schema]);
|
||||
|
||||
const dialog = useDialog();
|
||||
|
||||
const actions = useMemo<EditorProps['actions']>(
|
||||
() => [
|
||||
<div key="introspection" className="!opacity-100">
|
||||
@@ -122,7 +120,7 @@ export function GraphQLEditor({ request, onChange, baseRequest, ...extraEditorPr
|
||||
color="danger"
|
||||
isLoading={isLoading}
|
||||
onClick={() => {
|
||||
dialog.show({
|
||||
showDialog({
|
||||
title: 'Introspection Failed',
|
||||
size: 'dynamic',
|
||||
id: 'introspection-failed',
|
||||
@@ -161,7 +159,6 @@ export function GraphQLEditor({ request, onChange, baseRequest, ...extraEditorPr
|
||||
clear,
|
||||
schema,
|
||||
setAutoIntrospectDisabled,
|
||||
dialog,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { jsonLanguage } from '@codemirror/lang-json';
|
||||
import { linter } from '@codemirror/lint';
|
||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||
import classNames from 'classnames';
|
||||
import type { EditorView } from 'codemirror';
|
||||
import {
|
||||
@@ -10,19 +11,18 @@ import {
|
||||
updateSchema,
|
||||
} from 'codemirror-json-schema';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { useAlert } from '../hooks/useAlert';
|
||||
import type { ReflectResponseService } from '../hooks/useGrpc';
|
||||
import { showAlert } from '../lib/alert';
|
||||
import { showDialog } from '../lib/dialog';
|
||||
import { tryFormatJson } from '../lib/formatters';
|
||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||
import { pluralizeCount } from '../lib/pluralize';
|
||||
import { Button } from './core/Button';
|
||||
import type { EditorProps } from './core/Editor/Editor';
|
||||
import { Editor } from './core/Editor/Editor';
|
||||
import { FormattedError } from './core/FormattedError';
|
||||
import { InlineCode } from './core/InlineCode';
|
||||
import { VStack } from './core/Stacks';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
import { GrpcProtoSelection } from './GrpcProtoSelection';
|
||||
import type { EditorProps} from './core/Editor/Editor';
|
||||
import {Editor} from './core/Editor/Editor';
|
||||
|
||||
type Props = Pick<EditorProps, 'heightMode' | 'onChange' | 'className'> & {
|
||||
services: ReflectResponseService[] | null;
|
||||
@@ -42,9 +42,6 @@ export function GrpcEditor({
|
||||
}: Props) {
|
||||
const editorViewRef = useRef<EditorView>(null);
|
||||
|
||||
const alert = useAlert();
|
||||
const dialog = useDialog();
|
||||
|
||||
// Find the schema for the selected service and method and update the editor
|
||||
useEffect(() => {
|
||||
if (
|
||||
@@ -59,7 +56,7 @@ export function GrpcEditor({
|
||||
const s = services.find((s) => s.name === request.service);
|
||||
if (s == null) {
|
||||
console.log('Failed to find service', { service: request.service, services });
|
||||
alert({
|
||||
showAlert({
|
||||
id: 'grpc-find-service-error',
|
||||
title: "Couldn't Find Service",
|
||||
body: (
|
||||
@@ -74,7 +71,7 @@ export function GrpcEditor({
|
||||
const schema = s.methods.find((m) => m.name === request.method)?.schema;
|
||||
if (request.method != null && schema == null) {
|
||||
console.log('Failed to find method', { method: request.method, methods: s?.methods });
|
||||
alert({
|
||||
showAlert({
|
||||
id: 'grpc-find-schema-error',
|
||||
title: "Couldn't Find Method",
|
||||
body: (
|
||||
@@ -94,7 +91,7 @@ export function GrpcEditor({
|
||||
try {
|
||||
updateSchema(editorViewRef.current, JSON.parse(schema));
|
||||
} catch (err) {
|
||||
alert({
|
||||
showAlert({
|
||||
id: 'grpc-parse-schema-error',
|
||||
title: 'Failed to Parse Schema',
|
||||
body: (
|
||||
@@ -108,7 +105,7 @@ export function GrpcEditor({
|
||||
),
|
||||
});
|
||||
}
|
||||
}, [alert, services, request.method, request.service]);
|
||||
}, [services, request.method, request.service]);
|
||||
|
||||
const extraExtensions = useMemo(
|
||||
() => [
|
||||
@@ -143,7 +140,7 @@ export function GrpcEditor({
|
||||
}
|
||||
isLoading={reflectionLoading}
|
||||
onClick={() => {
|
||||
dialog.show({
|
||||
showDialog({
|
||||
title: 'Configure Schema',
|
||||
size: 'md',
|
||||
id: 'reflection-failed',
|
||||
@@ -172,7 +169,6 @@ export function GrpcEditor({
|
||||
</div>,
|
||||
],
|
||||
[
|
||||
dialog,
|
||||
protoFiles.length,
|
||||
reflectionError,
|
||||
reflectionLoading,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
|
||||
import React, { useState } from 'react';
|
||||
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
||||
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||
import {showToast} from "../lib/toast";
|
||||
import { router } from '../lib/router';
|
||||
import { showToast } from '../lib/toast';
|
||||
import { Button } from './core/Button';
|
||||
import { InlineCode } from './core/InlineCode';
|
||||
import { Select } from './core/Select';
|
||||
@@ -21,7 +21,6 @@ export function MoveToWorkspaceDialog({ onDone, request, activeWorkspaceId }: Pr
|
||||
const workspaces = useWorkspaces();
|
||||
const updateHttpRequest = useUpdateAnyHttpRequest();
|
||||
const updateGrpcRequest = useUpdateAnyGrpcRequest();
|
||||
const navigate = useNavigate();
|
||||
const [selectedWorkspaceId, setSelectedWorkspaceId] = useState<string>(activeWorkspaceId);
|
||||
|
||||
return (
|
||||
@@ -69,7 +68,7 @@ export function MoveToWorkspaceDialog({ onDone, request, activeWorkspaceId }: Pr
|
||||
color="secondary"
|
||||
className="mr-auto min-w-[5rem]"
|
||||
onClick={async () => {
|
||||
await navigate({
|
||||
await router.navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: selectedWorkspaceId },
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useKeyPressEvent } from 'react-use';
|
||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||
import { getActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
||||
@@ -10,6 +9,7 @@ import { httpRequestsAtom } from '../hooks/useHttpRequests';
|
||||
import { useRecentRequests } from '../hooks/useRecentRequests';
|
||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { router } from '../lib/router';
|
||||
import { Button } from './core/Button';
|
||||
import type { DropdownItem, DropdownRef } from './core/Dropdown';
|
||||
import { Dropdown } from './core/Dropdown';
|
||||
@@ -24,7 +24,6 @@ export function RecentRequestsDropdown({ className }: Props) {
|
||||
const dropdownRef = useRef<DropdownRef>(null);
|
||||
const [allRecentRequestIds] = useRecentRequests();
|
||||
const recentRequestIds = useMemo(() => allRecentRequestIds.slice(1), [allRecentRequestIds]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Handle key-up
|
||||
useKeyPressEvent('Control', undefined, () => {
|
||||
@@ -42,7 +41,7 @@ export function RecentRequestsDropdown({ className }: Props) {
|
||||
dropdownRef.current?.prev?.();
|
||||
});
|
||||
|
||||
const getItems = useCallback(() => {
|
||||
const items = useMemo(() => {
|
||||
const activeWorkspaceId = getActiveWorkspaceId();
|
||||
if (activeWorkspaceId === null) return [];
|
||||
|
||||
@@ -58,13 +57,10 @@ export function RecentRequestsDropdown({ className }: Props) {
|
||||
// leftSlot: <CountBadge className="!ml-0 px-0 w-5" count={recentRequestItems.length} />,
|
||||
leftSlot: <HttpMethodTag className="text-right" shortNames request={request} />,
|
||||
onSelect: async () => {
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: {
|
||||
requestId: request.id,
|
||||
workspaceId: activeWorkspaceId,
|
||||
},
|
||||
search: (prev) => ({ ...prev }),
|
||||
await router.navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: activeWorkspaceId },
|
||||
search: (prev) => ({ ...prev, request_id: request.id }),
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -82,10 +78,10 @@ export function RecentRequestsDropdown({ className }: Props) {
|
||||
}
|
||||
|
||||
return recentRequestItems.slice(0, 20);
|
||||
}, [navigate, recentRequestIds]);
|
||||
}, [recentRequestIds]);
|
||||
|
||||
return (
|
||||
<Dropdown ref={dropdownRef} items={getItems}>
|
||||
<Dropdown ref={dropdownRef} items={items}>
|
||||
<Button
|
||||
data-tauri-drag-region
|
||||
size="sm"
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import { useEffect } from 'react';
|
||||
import { getRecentCookieJars } from '../hooks/useRecentCookieJars';
|
||||
import { getRecentEnvironments } from '../hooks/useRecentEnvironments';
|
||||
import { getRecentRequests } from '../hooks/useRecentRequests';
|
||||
import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { router } from '../lib/router';
|
||||
|
||||
export function RedirectToLatestWorkspace() {
|
||||
const workspaces = useWorkspaces();
|
||||
const recentWorkspaces = useRecentWorkspaces();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (workspaces.length === 0 || recentWorkspaces == null) {
|
||||
@@ -22,23 +21,16 @@ export function RedirectToLatestWorkspace() {
|
||||
const environmentId = (await getRecentEnvironments(workspaceId))[0] ?? null;
|
||||
const cookieJarId = (await getRecentCookieJars(workspaceId))[0] ?? null;
|
||||
const requestId = (await getRecentRequests(workspaceId))[0] ?? null;
|
||||
const search = { cookie_jar_id: cookieJarId, environment_id: environmentId };
|
||||
const params = { workspaceId };
|
||||
const search = {
|
||||
cookie_jar_id: cookieJarId,
|
||||
environment_id: environmentId,
|
||||
requestId: requestId,
|
||||
};
|
||||
|
||||
if (workspaceId != null && requestId != null) {
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: { workspaceId, requestId },
|
||||
search,
|
||||
});
|
||||
} else {
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId },
|
||||
search,
|
||||
});
|
||||
}
|
||||
await router.navigate({ to: '/workspaces/$workspaceId', params, search });
|
||||
})();
|
||||
}, [navigate, recentWorkspaces, workspaces, workspaces.length]);
|
||||
}, [recentWorkspaces, workspaces, workspaces.length]);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ import { openUrl } from '@tauri-apps/plugin-opener';
|
||||
import { useRef } from 'react';
|
||||
import { useAppInfo } from '../hooks/useAppInfo';
|
||||
import { useCheckForUpdates } from '../hooks/useCheckForUpdates';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
import { useExportData } from '../hooks/useExportData';
|
||||
import { useImportData } from '../hooks/useImportData';
|
||||
import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent';
|
||||
import { useOpenSettings } from '../hooks/useOpenSettings';
|
||||
import { showDialog } from '../lib/dialog';
|
||||
import type { DropdownRef } from './core/Dropdown';
|
||||
import { Dropdown } from './core/Dropdown';
|
||||
import { Icon } from './core/Icon';
|
||||
@@ -18,7 +18,6 @@ export function SettingsDropdown() {
|
||||
const exportData = useExportData();
|
||||
const appInfo = useAppInfo();
|
||||
const dropdownRef = useRef<DropdownRef>(null);
|
||||
const dialog = useDialog();
|
||||
const checkForUpdates = useCheckForUpdates();
|
||||
const openSettings = useOpenSettings();
|
||||
|
||||
@@ -41,7 +40,7 @@ export function SettingsDropdown() {
|
||||
hotKeyAction: 'hotkeys.showHelp',
|
||||
leftSlot: <Icon icon="keyboard" />,
|
||||
onSelect: () => {
|
||||
dialog.show({
|
||||
showDialog({
|
||||
id: 'hotkey',
|
||||
title: 'Keyboard Shortcuts',
|
||||
size: 'dynamic',
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import type { AnyModel, Folder, GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
|
||||
import type { Folder, GrpcRequest, HttpRequest, Workspace } from '@yaakapp-internal/models';
|
||||
import classNames from 'classnames';
|
||||
import { useAtom, useAtomValue } from 'jotai';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
@@ -15,6 +14,7 @@ import { getSidebarCollapsedMap } from '../hooks/useSidebarItemCollapsed';
|
||||
import { useUpdateAnyFolder } from '../hooks/useUpdateAnyFolder';
|
||||
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
||||
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
||||
import { router } from '../lib/router';
|
||||
import { ContextMenu } from './core/Dropdown';
|
||||
import { sidebarSelectedIdAtom, sidebarTreeAtom } from './SidebarAtoms';
|
||||
import type { SidebarItemProps } from './SidebarItem';
|
||||
@@ -24,10 +24,12 @@ interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export type SidebarModel = Folder | GrpcRequest | HttpRequest | Workspace;
|
||||
|
||||
export interface SidebarTreeNode {
|
||||
id: string;
|
||||
name: string;
|
||||
model: AnyModel['model'];
|
||||
model: SidebarModel['model'];
|
||||
sortPriority?: number;
|
||||
workspaceId?: string;
|
||||
folderId?: string | null;
|
||||
@@ -35,8 +37,6 @@ export interface SidebarTreeNode {
|
||||
depth: number;
|
||||
}
|
||||
|
||||
export type SidebarModel = Folder | GrpcRequest | HttpRequest;
|
||||
|
||||
export function Sidebar({ className }: Props) {
|
||||
const [hidden, setHidden] = useSidebarHidden();
|
||||
const sidebarRef = useRef<HTMLElement>(null);
|
||||
@@ -52,7 +52,6 @@ export function Sidebar({ className }: Props) {
|
||||
const [draggingId, setDraggingId] = useState<string | null>(null);
|
||||
const [hoveredTree, setHoveredTree] = useState<SidebarTreeNode | null>(null);
|
||||
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { tree, treeParentMap, selectableRequests } = useAtomValue(sidebarTreeAtom);
|
||||
|
||||
@@ -95,14 +94,13 @@ export function Sidebar({ className }: Props) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.model === 'http_request' || node.model === 'grpc_request') {
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: {
|
||||
requestId: id,
|
||||
workspaceId: node.workspaceId ?? 'n/a',
|
||||
},
|
||||
search: (prev) => ({ ...prev }),
|
||||
// NOTE: I'm not sure why, but TS thinks workspaceId is (string | undefined) here
|
||||
if ((node.model === 'http_request' || node.model === 'grpc_request') && node.workspaceId) {
|
||||
const workspaceId = node.workspaceId;
|
||||
await router.navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId },
|
||||
search: (prev) => ({ ...prev, request_id: node.id }),
|
||||
});
|
||||
|
||||
setHasFocus(true);
|
||||
@@ -110,7 +108,7 @@ export function Sidebar({ className }: Props) {
|
||||
setSelectedTree(tree);
|
||||
}
|
||||
},
|
||||
[treeParentMap, navigate, setSelectedId],
|
||||
[treeParentMap, setSelectedId],
|
||||
);
|
||||
|
||||
const handleClearSelected = useCallback(() => {
|
||||
@@ -153,13 +151,10 @@ export function Sidebar({ className }: Props) {
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: {
|
||||
requestId: selected.id,
|
||||
workspaceId: activeWorkspace?.id ?? null,
|
||||
},
|
||||
search: (prev) => ({ ...prev }),
|
||||
await router.navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: activeWorkspace?.id ?? null },
|
||||
search: (prev) => ({ ...prev, request_id: selected.id }),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useCreateDropdownItems } from '../hooks/useCreateDropdownItems';
|
||||
import { useDeleteFolder } from '../hooks/useDeleteFolder';
|
||||
import { useDeleteRequest } from '../hooks/useDeleteRequest';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
import { useDuplicateFolder } from '../hooks/useDuplicateFolder';
|
||||
import { useDuplicateGrpcRequest } from '../hooks/useDuplicateGrpcRequest';
|
||||
import { useDuplicateHttpRequest } from '../hooks/useDuplicateHttpRequest';
|
||||
@@ -12,6 +11,8 @@ import { useRenameRequest } from '../hooks/useRenameRequest';
|
||||
import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest';
|
||||
import { useSendManyRequests } from '../hooks/useSendManyRequests';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
|
||||
import { showDialog } from '../lib/dialog';
|
||||
import { getHttpRequest } from '../lib/store';
|
||||
import type { DropdownItem } from './core/Dropdown';
|
||||
import { ContextMenu } from './core/Dropdown';
|
||||
@@ -32,7 +33,6 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
||||
const httpRequestActions = useHttpRequestActions();
|
||||
const sendRequest = useSendAnyHttpRequest();
|
||||
const workspaces = useWorkspaces();
|
||||
const dialog = useDialog();
|
||||
const deleteRequest = useDeleteRequest(child.id);
|
||||
const renameRequest = useRenameRequest(child.id);
|
||||
const duplicateHttpRequest = useDuplicateHttpRequest({ id: child.id, navigateAfter: true });
|
||||
@@ -42,7 +42,7 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
||||
folderId: child.model === 'folder' ? child.id : null,
|
||||
});
|
||||
|
||||
const items = useCallback((): DropdownItem[] => {
|
||||
const items = useMemo((): DropdownItem[] => {
|
||||
if (child.model === 'folder') {
|
||||
return [
|
||||
{
|
||||
@@ -56,7 +56,7 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
||||
label: 'Settings',
|
||||
leftSlot: <Icon icon="settings" />,
|
||||
onSelect: () =>
|
||||
dialog.show({
|
||||
showDialog({
|
||||
id: 'folder-settings',
|
||||
title: 'Folder Settings',
|
||||
size: 'md',
|
||||
@@ -77,7 +77,7 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
||||
onSelect: () => deleteFolder.mutate(),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
...createDropdownItems(),
|
||||
...createDropdownItems,
|
||||
];
|
||||
} else {
|
||||
const requestItems: DropdownItem[] =
|
||||
@@ -146,7 +146,6 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
||||
createDropdownItems,
|
||||
deleteFolder,
|
||||
deleteRequest,
|
||||
dialog,
|
||||
duplicateFolder,
|
||||
duplicateGrpcRequest,
|
||||
duplicateHttpRequest,
|
||||
|
||||
@@ -5,41 +5,30 @@ import { hideToast, toastsAtom } from '../lib/toast';
|
||||
import { Toast, type ToastProps } from './core/Toast';
|
||||
import { Portal } from './Portal';
|
||||
|
||||
export type ToastEntry = {
|
||||
id?: string;
|
||||
export type ToastInstance = {
|
||||
id: string;
|
||||
message: ReactNode;
|
||||
timeout?: 3000 | 5000 | 8000 | null;
|
||||
timeout: 3000 | 5000 | 8000 | null;
|
||||
onClose?: ToastProps['onClose'];
|
||||
} & Omit<ToastProps, 'onClose' | 'open' | 'children' | 'timeout'>;
|
||||
|
||||
export type PrivateToastEntry = ToastEntry & {
|
||||
id: string;
|
||||
timeout: number | null;
|
||||
};
|
||||
|
||||
function ToastInstance({ id, message, timeout, ...props }: PrivateToastEntry) {
|
||||
return (
|
||||
<Toast
|
||||
open
|
||||
timeout={timeout}
|
||||
{...props}
|
||||
// We call onClose inside actions.hide instead of passing to toast so that
|
||||
// it gets called from external close calls as well
|
||||
onClose={() => hideToast(id)}
|
||||
>
|
||||
{message}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
export const Toasts = () => {
|
||||
const toasts = useAtomValue(toastsAtom);
|
||||
return (
|
||||
<Portal name="toasts">
|
||||
<div className="absolute right-0 bottom-0 z-20">
|
||||
<AnimatePresence>
|
||||
{toasts.map((props: PrivateToastEntry) => (
|
||||
<ToastInstance key={props.id} {...props} />
|
||||
{toasts.map(({ message, ...props }: ToastInstance) => (
|
||||
<Toast
|
||||
key={props.id}
|
||||
open
|
||||
{...props}
|
||||
// We call onClose inside actions.hide instead of passing to toast so that
|
||||
// it gets called from external close calls as well
|
||||
onClose={() => hideToast(props.id)}
|
||||
>
|
||||
{message}
|
||||
</Toast>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
@@ -3,11 +3,11 @@ import { memo, useCallback, useMemo } from 'react';
|
||||
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
||||
import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
|
||||
import { useDeleteSendHistory } from '../hooks/useDeleteSendHistory';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
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';
|
||||
import type { ButtonProps } from './core/Button';
|
||||
import { Button } from './core/Button';
|
||||
@@ -28,7 +28,6 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const createWorkspace = useCreateWorkspace();
|
||||
const { mutate: deleteSendHistory } = useDeleteSendHistory();
|
||||
const dialog = useDialog();
|
||||
const settings = useSettings();
|
||||
const openWorkspace = useOpenWorkspace();
|
||||
const openWorkspaceNewWindow = settings?.openWorkspaceNewWindow ?? null;
|
||||
@@ -57,7 +56,7 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
||||
leftSlot: <Icon icon="settings" />,
|
||||
hotKeyAction: 'workspace_settings.show',
|
||||
onSelect: async () => {
|
||||
dialog.show({
|
||||
showDialog({
|
||||
id: 'workspace-settings',
|
||||
title: 'Workspace Settings',
|
||||
size: 'md',
|
||||
@@ -97,7 +96,6 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
||||
sync,
|
||||
deleteSendHistory,
|
||||
createWorkspace,
|
||||
dialog,
|
||||
]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
@@ -112,14 +110,14 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
||||
const workspace = await getWorkspace(workspaceId);
|
||||
if (workspace == null) return;
|
||||
|
||||
dialog.show({
|
||||
showDialog({
|
||||
id: 'open-workspace',
|
||||
size: 'sm',
|
||||
title: 'Open Workspace',
|
||||
render: ({ hide }) => <OpenWorkspaceDialog workspace={workspace} hide={hide} />,
|
||||
});
|
||||
},
|
||||
[dialog, openWorkspace, openWorkspaceNewWindow],
|
||||
[openWorkspace, openWorkspaceNewWindow],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -57,7 +57,7 @@ export type DropdownItem = DropdownItemDefault | DropdownItemSeparator;
|
||||
|
||||
export interface DropdownProps {
|
||||
children: ReactElement<HTMLAttributes<HTMLButtonElement>>;
|
||||
items: DropdownItem[] | (() => DropdownItem[]);
|
||||
items: DropdownItem[];
|
||||
onOpen?: () => void;
|
||||
onClose?: () => void;
|
||||
fullWidth?: boolean;
|
||||
@@ -75,7 +75,7 @@ export interface DropdownRef {
|
||||
}
|
||||
|
||||
export const Dropdown = forwardRef<DropdownRef, DropdownProps>(function Dropdown(
|
||||
{ children, items: itemsGetter, onOpen, onClose, hotKeyAction, fullWidth }: DropdownProps,
|
||||
{ children, items, onOpen, onClose, hotKeyAction, fullWidth }: DropdownProps,
|
||||
ref,
|
||||
) {
|
||||
const [isOpen, _setIsOpen] = useState<boolean>(false);
|
||||
@@ -83,8 +83,6 @@ export const Dropdown = forwardRef<DropdownRef, DropdownProps>(function Dropdown
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const menuRef = useRef<Omit<DropdownRef, 'open'>>(null);
|
||||
|
||||
const [items, setItems] = useState<DropdownItem[]>([]);
|
||||
|
||||
const setIsOpen = useCallback(
|
||||
(o: SetStateAction<boolean>) => {
|
||||
_setIsOpen(o);
|
||||
@@ -103,8 +101,7 @@ export const Dropdown = forwardRef<DropdownRef, DropdownProps>(function Dropdown
|
||||
|
||||
const openDropdown = useCallback(() => {
|
||||
setIsOpen((o) => !o);
|
||||
setItems(typeof itemsGetter === 'function' ? itemsGetter() : itemsGetter);
|
||||
}, [itemsGetter, setIsOpen]);
|
||||
}, [setIsOpen]);
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
@@ -205,7 +202,7 @@ export const ContextMenu = forwardRef<DropdownRef, ContextMenuProps>(function Co
|
||||
isOpen={true} // Always open because we return null if not
|
||||
className={className}
|
||||
ref={ref}
|
||||
items={typeof items === 'function' ? items() : items}
|
||||
items={items}
|
||||
onClose={onClose}
|
||||
triggerShape={triggerShape}
|
||||
/>
|
||||
@@ -417,11 +414,9 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle' | 'items'
|
||||
[filteredItems, setSelectedIndex],
|
||||
);
|
||||
|
||||
if (items.length === 0) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{filteredItems.map(
|
||||
{items.map(
|
||||
(item) =>
|
||||
item.type !== 'separator' &&
|
||||
!item.hotKeyLabelOnly && (
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { useActiveEnvironmentVariables } from '../../../hooks/useActiveEnvironmentVariables';
|
||||
import { useDialog } from '../../../hooks/useDialog';
|
||||
import { parseTemplate } from '../../../hooks/useParseTemplate';
|
||||
import { useRequestEditor } from '../../../hooks/useRequestEditor';
|
||||
import { useSettings } from '../../../hooks/useSettings';
|
||||
@@ -28,6 +27,7 @@ import {
|
||||
useTemplateFunctions,
|
||||
useTwigCompletionOptions,
|
||||
} from '../../../hooks/useTemplateFunctions';
|
||||
import { showDialog } from '../../../lib/dialog';
|
||||
import { TemplateFunctionDialog } from '../../TemplateFunctionDialog';
|
||||
import { TemplateVariableDialog } from '../../TemplateVariableDialog';
|
||||
import { IconButton } from '../IconButton';
|
||||
@@ -195,11 +195,10 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
[wrapLines],
|
||||
);
|
||||
|
||||
const dialog = useDialog();
|
||||
const onClickFunction = useCallback(
|
||||
async (fn: TemplateFunction, tagValue: string, startPos: number) => {
|
||||
const initialTokens = await parseTemplate(tagValue);
|
||||
dialog.show({
|
||||
showDialog({
|
||||
id: 'template-function',
|
||||
size: 'sm',
|
||||
title: 'Configure Function',
|
||||
@@ -218,13 +217,13 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
),
|
||||
});
|
||||
},
|
||||
[dialog],
|
||||
[],
|
||||
);
|
||||
|
||||
const onClickVariable = useCallback(
|
||||
async (_v: EnvironmentVariable, tagValue: string, startPos: number) => {
|
||||
const initialTokens = await parseTemplate(tagValue);
|
||||
dialog.show({
|
||||
showDialog({
|
||||
size: 'dynamic',
|
||||
id: 'template-variable',
|
||||
title: 'Change Variable',
|
||||
@@ -241,13 +240,13 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
),
|
||||
});
|
||||
},
|
||||
[dialog],
|
||||
[],
|
||||
);
|
||||
|
||||
const onClickMissingVariable = useCallback(
|
||||
async (_name: string, tagValue: string, startPos: number) => {
|
||||
const initialTokens = await parseTemplate(tagValue);
|
||||
dialog.show({
|
||||
showDialog({
|
||||
size: 'dynamic',
|
||||
id: 'template-variable',
|
||||
title: 'Configure Variable',
|
||||
@@ -264,7 +263,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
),
|
||||
});
|
||||
},
|
||||
[dialog],
|
||||
[],
|
||||
);
|
||||
|
||||
const [, { focusParamValue }] = useRequestEditor();
|
||||
|
||||
@@ -338,7 +338,7 @@ function PairEditorRow({
|
||||
const handleFocus = useCallback(() => onFocus?.(pair), [onFocus, pair]);
|
||||
const handleDelete = useCallback(() => onDelete?.(pair, false), [onDelete, pair]);
|
||||
|
||||
const getDeleteItems = useCallback(
|
||||
const deleteItems = useMemo(
|
||||
(): DropdownItem[] => [
|
||||
{
|
||||
key: 'delete',
|
||||
@@ -525,7 +525,7 @@ function PairEditorRow({
|
||||
onDelete={handleDelete}
|
||||
/>
|
||||
) : (
|
||||
<Dropdown items={getDeleteItems}>
|
||||
<Dropdown items={deleteItems}>
|
||||
<IconButton
|
||||
iconSize="sm"
|
||||
size="xs"
|
||||
|
||||
Reference in New Issue
Block a user