mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-17 23:14:03 +01:00
Typesafe routing and CM line height issue
This commit is contained in:
@@ -10,12 +10,13 @@ import { matchPath } from 'react-router-dom';
|
||||
import { keyValueQueryKey } from '../hooks/useKeyValue';
|
||||
import { requestsQueryKey } from '../hooks/useRequests';
|
||||
import { responsesQueryKey } from '../hooks/useResponses';
|
||||
import { routePaths } from '../hooks/useRoutes';
|
||||
import { workspacesQueryKey } from '../hooks/useWorkspaces';
|
||||
import { DEFAULT_FONT_SIZE } from '../lib/constants';
|
||||
import { extractKeyValue } from '../lib/keyValueStore';
|
||||
import type { HttpRequest, HttpResponse, KeyValue, Workspace } from '../lib/models';
|
||||
import { convertDates } from '../lib/models';
|
||||
import { AppRouter, WORKSPACE_REQUEST_PATH } from './AppRouter';
|
||||
import { AppRouter } from './AppRouter';
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
@@ -45,12 +46,6 @@ await listen('updated_request', ({ payload: request }: { payload: HttpRequest })
|
||||
);
|
||||
});
|
||||
|
||||
await listen('deleted_request', ({ payload: request }: { payload: HttpRequest }) => {
|
||||
queryClient.setQueryData(requestsQueryKey(request.workspaceId), (requests: HttpRequest[] = []) =>
|
||||
requests.filter((r) => r.id !== request.id),
|
||||
);
|
||||
});
|
||||
|
||||
await listen('updated_response', ({ payload: response }: { payload: HttpResponse }) => {
|
||||
queryClient.setQueryData(
|
||||
responsesQueryKey(response.requestId),
|
||||
@@ -92,8 +87,27 @@ await listen('updated_workspace', ({ payload: workspace }: { payload: Workspace
|
||||
});
|
||||
});
|
||||
|
||||
await listen(
|
||||
'deleted_model',
|
||||
({ payload: model }: { payload: Workspace | HttpRequest | HttpResponse | KeyValue }) => {
|
||||
function removeById<T extends { id: string }>(model: T) {
|
||||
return (entries: T[] | undefined) => entries?.filter((e) => e.id !== model.id);
|
||||
}
|
||||
|
||||
if (model.model === 'workspace') {
|
||||
queryClient.setQueryData(workspacesQueryKey(), removeById<Workspace>(model));
|
||||
} else if (model.model === 'http_request') {
|
||||
queryClient.setQueryData(requestsQueryKey(model.workspaceId), removeById<HttpRequest>(model));
|
||||
} else if (model.model === 'http_response') {
|
||||
queryClient.setQueryData(responsesQueryKey(model.requestId), removeById<HttpResponse>(model));
|
||||
} else if (model.model === 'key_value') {
|
||||
queryClient.setQueryData(keyValueQueryKey(model), undefined);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
await listen('send_request', async () => {
|
||||
const params = matchPath(WORKSPACE_REQUEST_PATH, window.location.pathname);
|
||||
const params = matchPath(routePaths.request(), window.location.pathname);
|
||||
const requestId = params?.params.requestId;
|
||||
if (typeof requestId !== 'string') {
|
||||
return;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
||||
import { createBrowserRouter, Navigate, RouterProvider } from 'react-router-dom';
|
||||
import { routePaths } from '../hooks/useRoutes';
|
||||
|
||||
const Workspaces = lazy(() => import('./Workspaces'));
|
||||
const Workspace = lazy(() => import('./Workspace'));
|
||||
const RouteError = lazy(() => import('./RouteError'));
|
||||
|
||||
export const WORKSPACE_PATH = '/workspaces/:workspaceId';
|
||||
export const WORKSPACE_REQUEST_PATH = '/workspaces/:workspaceId/requests/:requestId';
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
@@ -15,14 +13,21 @@ const router = createBrowserRouter([
|
||||
children: [
|
||||
{
|
||||
path: '/',
|
||||
element: <Navigate to={routePaths.workspaces()} replace={true} />,
|
||||
},
|
||||
{
|
||||
path: routePaths.workspaces(),
|
||||
element: <Workspaces />,
|
||||
},
|
||||
{
|
||||
path: WORKSPACE_PATH,
|
||||
path: routePaths.workspace({ workspaceId: ':workspaceId' }),
|
||||
element: <Workspace />,
|
||||
},
|
||||
{
|
||||
path: WORKSPACE_REQUEST_PATH,
|
||||
path: routePaths.request({
|
||||
workspaceId: ':workspaceId',
|
||||
requestId: ':requestId',
|
||||
}),
|
||||
element: <Workspace />,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -2,7 +2,7 @@ import classnames from 'classnames';
|
||||
import { memo, useEffect, useMemo, useState } from 'react';
|
||||
import { useActiveRequestId } from '../hooks/useActiveRequestId';
|
||||
import { useDeleteResponses } from '../hooks/useDeleteResponses';
|
||||
import { useDeleteResponse } from '../hooks/useResponseDelete';
|
||||
import { useDeleteResponse } from '../hooks/useDeleteResponse';
|
||||
import { useResponses } from '../hooks/useResponses';
|
||||
import { useResponseViewMode } from '../hooks/useResponseViewMode';
|
||||
import { tryFormatJson } from '../lib/formatters';
|
||||
@@ -21,18 +21,18 @@ interface Props {
|
||||
}
|
||||
|
||||
export const ResponsePane = memo(function ResponsePane({ className }: Props) {
|
||||
const [activeResponseId, setActiveResponseId] = useState<string | null>(null);
|
||||
const [pinnedResponseId, setPinnedResponseId] = useState<string | null>(null);
|
||||
const activeRequestId = useActiveRequestId();
|
||||
const responses = useResponses(activeRequestId);
|
||||
const activeResponse: HttpResponse | null = activeResponseId
|
||||
? responses.find((r) => r.id === activeResponseId) ?? null
|
||||
const activeResponse: HttpResponse | null = pinnedResponseId
|
||||
? responses.find((r) => r.id === pinnedResponseId) ?? null
|
||||
: responses[responses.length - 1] ?? null;
|
||||
const [viewMode, toggleViewMode] = useResponseViewMode(activeResponse?.requestId);
|
||||
const deleteResponse = useDeleteResponse(activeResponse);
|
||||
const deleteResponse = useDeleteResponse(activeResponse?.id ?? null);
|
||||
const deleteAllResponses = useDeleteResponses(activeResponse?.requestId);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveResponseId(null);
|
||||
setPinnedResponseId(null);
|
||||
}, [responses.length]);
|
||||
|
||||
const contentType = useMemo(
|
||||
@@ -92,7 +92,7 @@ export const ResponsePane = memo(function ResponsePane({ className }: Props) {
|
||||
...responses.slice(0, 10).map((r) => ({
|
||||
label: r.status + ' - ' + r.elapsed + ' ms',
|
||||
leftSlot: activeResponse?.id === r.id ? <Icon icon="check" /> : <></>,
|
||||
onSelect: () => setActiveResponseId(r.id),
|
||||
onSelect: () => setPinnedResponseId(r.id),
|
||||
})),
|
||||
]}
|
||||
>
|
||||
|
||||
@@ -29,7 +29,7 @@ export const UrlBar = memo(function UrlBar({ id: requestId, url, method, classNa
|
||||
);
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className={classnames(className, 'w-full flex items-center')}>
|
||||
<form onSubmit={handleSubmit} className={className}>
|
||||
<Input
|
||||
key={requestId}
|
||||
hideLabel
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import classnames from 'classnames';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
||||
import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId';
|
||||
import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
|
||||
import { useDeleteWorkspace } from '../hooks/useDeleteWorkspace';
|
||||
import { useRoutes } from '../hooks/useRoutes';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { Button } from './core/Button';
|
||||
import type { DropdownItem } from './core/Dropdown';
|
||||
@@ -15,11 +16,12 @@ type Props = {
|
||||
};
|
||||
|
||||
export const WorkspaceDropdown = memo(function WorkspaceDropdown({ className }: Props) {
|
||||
const navigate = useNavigate();
|
||||
const routes = useRoutes();
|
||||
const workspaces = useWorkspaces();
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const activeWorkspaceId = useActiveWorkspaceId();
|
||||
const createWorkspace = useCreateWorkspace({ navigateAfter: true });
|
||||
const deleteWorkspace = useDeleteWorkspace(activeWorkspaceId);
|
||||
|
||||
const items: DropdownItem[] = useMemo(() => {
|
||||
const workspaceItems = workspaces.map((w) => ({
|
||||
@@ -27,7 +29,7 @@ export const WorkspaceDropdown = memo(function WorkspaceDropdown({ className }:
|
||||
leftSlot: activeWorkspaceId === w.id ? <Icon icon="check" /> : <Icon icon="empty" />,
|
||||
onSelect: () => {
|
||||
if (w.id === activeWorkspaceId) return;
|
||||
navigate(`/workspaces/${w.id}`);
|
||||
routes.navigate('workspace', { workspaceId: w.id });
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -36,10 +38,14 @@ export const WorkspaceDropdown = memo(function WorkspaceDropdown({ className }:
|
||||
'-----',
|
||||
{
|
||||
label: 'New Workspace',
|
||||
value: 'new',
|
||||
leftSlot: <Icon icon="plus" />,
|
||||
onSelect: () => createWorkspace.mutate({ name: 'New Workspace' }),
|
||||
},
|
||||
{
|
||||
label: 'Delete Workspace',
|
||||
leftSlot: <Icon icon="trash" />,
|
||||
onSelect: () => deleteWorkspace.mutate(),
|
||||
},
|
||||
];
|
||||
}, [workspaces, activeWorkspaceId]);
|
||||
|
||||
|
||||
@@ -20,10 +20,9 @@ export type DropdownItem =
|
||||
export interface DropdownProps {
|
||||
children: ReactElement<HTMLAttributes<HTMLButtonElement>>;
|
||||
items: DropdownItem[];
|
||||
ignoreClick?: boolean;
|
||||
}
|
||||
|
||||
export function Dropdown({ children, items, ignoreClick }: DropdownProps) {
|
||||
export function Dropdown({ children, items }: DropdownProps) {
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
const ref = useRef<HTMLButtonElement>(null);
|
||||
const child = useMemo(() => {
|
||||
@@ -36,7 +35,6 @@ export function Dropdown({ children, items, ignoreClick }: DropdownProps) {
|
||||
onClick:
|
||||
existingChild.props?.onClick ??
|
||||
((e: MouseEvent<HTMLButtonElement>) => {
|
||||
console.log('CLICK INSIDE');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setOpen((o) => !o);
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
@apply text-placeholder;
|
||||
}
|
||||
|
||||
.cm-scroller {
|
||||
/* Inherit line-height from outside */
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
/* Don't show selection on blurred input */
|
||||
.cm-selectionBackground {
|
||||
@apply bg-transparent;
|
||||
@@ -54,17 +59,15 @@
|
||||
|
||||
&.cm-singleline {
|
||||
.cm-editor {
|
||||
@apply h-full w-full;
|
||||
@apply w-full h-auto;
|
||||
}
|
||||
|
||||
.cm-scroller {
|
||||
@apply font-mono flex text-[0.8rem];
|
||||
align-items: center !important;
|
||||
overflow: hidden !important;
|
||||
@apply font-mono text-[0.8rem] overflow-hidden;
|
||||
}
|
||||
|
||||
.cm-line {
|
||||
@apply px-0;
|
||||
@apply px-2 overflow-hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,11 +99,12 @@
|
||||
}
|
||||
|
||||
.cm-editor .cm-gutterElement {
|
||||
@apply flex items-center;
|
||||
transition: color var(--transition-duration);
|
||||
}
|
||||
|
||||
.cm-editor .fold-gutter-icon {
|
||||
@apply pt-[0.3em] pl-[0.4em] px-[0.4em] h-4 cursor-pointer rounded;
|
||||
@apply pt-[0.25em] pl-[0.4em] px-[0.4em] h-4 cursor-pointer rounded;
|
||||
}
|
||||
|
||||
.cm-editor .fold-gutter-icon::after {
|
||||
@@ -109,7 +113,7 @@
|
||||
}
|
||||
|
||||
.cm-editor .fold-gutter-icon[data-open] {
|
||||
@apply pt-[0.4em] pl-[0.3em];
|
||||
@apply pt-[0.38em] pl-[0.3em];
|
||||
}
|
||||
|
||||
.cm-editor .fold-gutter-icon[data-open]::after {
|
||||
|
||||
@@ -46,7 +46,7 @@ export function Input({
|
||||
const id = `input-${name}`;
|
||||
const inputClassName = classnames(
|
||||
className,
|
||||
'!bg-transparent pl-3 pr-2 min-w-0 h-full w-full focus:outline-none placeholder:text-placeholder',
|
||||
'!bg-transparent min-w-0 h-full w-full focus:outline-none placeholder:text-placeholder',
|
||||
!!leftSlot && '!pl-0.5',
|
||||
!!rightSlot && '!pr-0.5',
|
||||
);
|
||||
@@ -81,8 +81,8 @@ export function Input({
|
||||
'relative w-full rounded-md text-gray-900',
|
||||
'border border-gray-200 focus-within:border-focus',
|
||||
!isValid && '!border-invalid',
|
||||
size === 'md' && 'h-md',
|
||||
size === 'sm' && 'h-sm',
|
||||
size === 'md' && 'h-md leading-md',
|
||||
size === 'sm' && 'h-sm leading-sm',
|
||||
)}
|
||||
>
|
||||
{leftSlot}
|
||||
|
||||
Reference in New Issue
Block a user