Refactor hooks to be easier to use

This commit is contained in:
Gregory Schier
2023-03-13 23:25:41 -07:00
parent 4bf22d8a60
commit 7d4e9894c3
19 changed files with 314 additions and 258 deletions

View File

@@ -1,6 +1,6 @@
import classnames from 'classnames';
import { useEffect, useState } from 'react';
import { useRequestUpdate } from '../hooks/useRequest';
import { useUpdateRequest } from '../hooks/useUpdateRequest';
import type { HttpHeader, HttpRequest } from '../lib/models';
import { IconButton } from './IconButton';
import { Input } from './Input';
@@ -14,7 +14,7 @@ interface Props {
type PairWithId = { header: Partial<HttpHeader>; id: string };
export function HeaderEditor({ request, className }: Props) {
const updateRequest = useRequestUpdate(request);
const updateRequest = useUpdateRequest(request);
const saveHeaders = (pairs: PairWithId[]) => {
const headers = pairs.map((p) => ({ name: '', value: '', ...p.header }));
updateRequest.mutate({ headers });

View File

@@ -1,27 +1,31 @@
import classnames from 'classnames';
import { useRequestUpdate, useSendRequest } from '../hooks/useRequest';
import type { HttpRequest } from '../lib/models';
import { useActiveRequest } from '../hooks/useActiveRequest';
import { useSendRequest } from '../hooks/useSendRequest';
import { useUpdateRequest } from '../hooks/useUpdateRequest';
import { Editor } from './Editor';
import { HeaderEditor } from './HeaderEditor';
import { TabContent, Tabs } from './Tabs';
import { UrlBar } from './UrlBar';
interface Props {
request: HttpRequest;
fullHeight: boolean;
className?: string;
}
export function RequestPane({ fullHeight, request, className }: Props) {
const updateRequest = useRequestUpdate(request ?? null);
const sendRequest = useSendRequest(request ?? null);
export function RequestPane({ fullHeight, className }: Props) {
const activeRequest = useActiveRequest();
const updateRequest = useUpdateRequest(activeRequest);
const sendRequest = useSendRequest(activeRequest);
if (activeRequest === null) return null;
return (
<div className={classnames(className, 'py-2 grid grid-rows-[auto_minmax(0,1fr)] grid-cols-1')}>
<div className="pl-2">
<UrlBar
key={request.id}
method={request.method}
url={request.url}
key={activeRequest.id}
method={activeRequest.method}
url={activeRequest.url}
loading={sendRequest.isLoading}
onMethodChange={(method) => updateRequest.mutate({ method })}
onUrlChange={(url) => updateRequest.mutate({ url })}
@@ -41,15 +45,15 @@ export function RequestPane({ fullHeight, request, className }: Props) {
label="Request body"
>
<TabContent value="headers" className="pl-2">
<HeaderEditor key={request.id} request={request} />
<HeaderEditor key={activeRequest.id} request={activeRequest} />
</TabContent>
<TabContent value="body">
<Editor
key={request.id}
key={activeRequest.id}
className="!bg-gray-50"
heightMode={fullHeight ? 'full' : 'auto'}
useTemplating
defaultValue={request.body ?? ''}
defaultValue={activeRequest.body ?? ''}
contentType="application/graphql+json"
onChange={(body) => updateRequest.mutate({ body })}
/>

View File

@@ -1,6 +1,8 @@
import classnames from 'classnames';
import { memo, useEffect, useMemo, useState } from 'react';
import { useDeleteAllResponses, useDeleteResponse, useResponses } from '../hooks/useResponses';
import { useDeleteResponses } from '../hooks/useDeleteResponses';
import { useDeleteResponse } from '../hooks/useResponseDelete';
import { useResponses } from '../hooks/useResponses';
import { tryFormatJson } from '../lib/formatters';
import { Dropdown } from './Dropdown';
import { Editor } from './Editor';
@@ -11,23 +13,22 @@ import { StatusColor } from './StatusColor';
import { Webview } from './Webview';
interface Props {
requestId: string;
className?: string;
}
export const ResponsePane = memo(function ResponsePane({ requestId, className }: Props) {
export const ResponsePane = memo(function ResponsePane({ className }: Props) {
const [activeResponseId, setActiveResponseId] = useState<string | null>(null);
const [viewMode, setViewMode] = useState<'pretty' | 'raw'>('pretty');
const responses = useResponses(requestId);
const responses = useResponses();
const response = activeResponseId
? responses.data.find((r) => r.id === activeResponseId)
: responses.data[responses.data.length - 1];
? responses.find((r) => r.id === activeResponseId)
: responses[responses.length - 1];
const deleteResponse = useDeleteResponse(response);
const deleteAllResponses = useDeleteAllResponses(response?.requestId);
const deleteAllResponses = useDeleteResponses(response?.requestId);
useEffect(() => {
setActiveResponseId(null);
}, [responses.data?.length]);
}, [responses.length]);
const contentType = useMemo(
() =>
@@ -35,10 +36,6 @@ export const ResponsePane = memo(function ResponsePane({ requestId, className }:
[response],
);
if (!response) {
return null;
}
return (
<div className="p-2">
<div
@@ -51,56 +48,56 @@ export const ResponsePane = memo(function ResponsePane({ requestId, className }:
>
{/*<HStack as={WindowDragRegion} items="center" className="pl-1.5 pr-1">*/}
{/*</HStack>*/}
<HStack
alignItems="center"
className="italic text-gray-700 text-sm w-full mb-1 flex-shrink-0 pl-2"
>
{response && response.status > 0 && (
<div className="whitespace-nowrap">
<StatusColor statusCode={response.status}>
{response.status}
{response.statusReason && ` ${response.statusReason}`}
</StatusColor>
&nbsp;&bull;&nbsp;
{response.elapsed}ms &nbsp;&bull;&nbsp;
{Math.round(response.body.length / 1000)} KB
</div>
)}
<HStack alignItems="center" className="ml-auto h-8">
<IconButton
icon={viewMode === 'pretty' ? 'eye' : 'code'}
size="sm"
className="ml-1"
onClick={() => setViewMode((m) => (m === 'pretty' ? 'raw' : 'pretty'))}
/>
<Dropdown
items={[
{
label: 'Clear Response',
onSelect: deleteResponse.mutate,
disabled: responses.length === 0,
},
{
label: 'Clear All Responses',
onSelect: deleteAllResponses.mutate,
disabled: responses.length === 0,
},
'-----',
...responses.slice(0, 10).map((r) => ({
label: r.status + ' - ' + r.elapsed + ' ms',
leftSlot: response?.id === r.id ? <Icon icon="check" /> : <></>,
onSelect: () => setActiveResponseId(r.id),
})),
]}
>
<IconButton icon="clock" className="ml-auto" size="sm" />
</Dropdown>
</HStack>
</HStack>
{response && (
<>
<HStack
alignItems="center"
className="italic text-gray-700 text-sm w-full mb-1 flex-shrink-0 pl-2"
>
{response.status > 0 && (
<div className="whitespace-nowrap">
<StatusColor statusCode={response.status}>
{response.status}
{response.statusReason && ` ${response.statusReason}`}
</StatusColor>
&nbsp;&bull;&nbsp;
{response.elapsed}ms &nbsp;&bull;&nbsp;
{Math.round(response.body.length / 1000)} KB
</div>
)}
<HStack alignItems="center" className="ml-auto h-8">
<IconButton
icon={viewMode === 'pretty' ? 'eye' : 'code'}
size="sm"
className="ml-1"
onClick={() => setViewMode((m) => (m === 'pretty' ? 'raw' : 'pretty'))}
/>
<Dropdown
items={[
{
label: 'Clear Response',
onSelect: deleteResponse.mutate,
disabled: responses.data.length === 0,
},
{
label: 'Clear All Responses',
onSelect: deleteAllResponses.mutate,
disabled: responses.data.length === 0,
},
'-----',
...responses.data.slice(0, 10).map((r) => ({
label: r.status + ' - ' + r.elapsed + ' ms',
leftSlot: response?.id === r.id ? <Icon icon="check" /> : <></>,
onSelect: () => setActiveResponseId(r.id),
})),
]}
>
<IconButton icon="clock" className="ml-auto" size="sm" />
</Dropdown>
</HStack>
</HStack>
{response?.error ? (
<div className="p-1">
<div className="text-white bg-red-500 px-3 py-2 rounded">{response.error}</div>

View File

@@ -1,7 +1,10 @@
import classnames from 'classnames';
import { useState } from 'react';
import { useRequestCreate, useRequestUpdate } from '../hooks/useRequest';
import { useActiveRequest } from '../hooks/useActiveRequest';
import { useCreateRequest } from '../hooks/useCreateRequest';
import { useRequests } from '../hooks/useRequests';
import { useTheme } from '../hooks/useTheme';
import { useUpdateRequest } from '../hooks/useUpdateRequest';
import type { HttpRequest } from '../lib/models';
import { ButtonLink } from './ButtonLink';
import { IconButton } from './IconButton';
@@ -9,14 +12,13 @@ import { HStack, VStack } from './Stacks';
import { WindowDragRegion } from './WindowDragRegion';
interface Props {
workspaceId: string;
requests: HttpRequest[];
activeRequestId?: string;
className?: string;
}
export function Sidebar({ className, activeRequestId, workspaceId, requests }: Props) {
const createRequest = useRequestCreate({ workspaceId, navigateAfter: true });
export function Sidebar({ className }: Props) {
const requests = useRequests();
const activeRequest = useActiveRequest();
const createRequest = useCreateRequest({ navigateAfter: true });
const { appearance, toggleAppearance } = useTheme();
return (
<div
@@ -36,7 +38,7 @@ export function Sidebar({ className, activeRequestId, workspaceId, requests }: P
</HStack>
<VStack as="ul" className="py-3 px-2 overflow-auto h-full" space={1}>
{requests.map((r) => (
<SidebarItem key={r.id} request={r} active={r.id === activeRequestId} />
<SidebarItem key={r.id} request={r} active={r.id === activeRequest?.id} />
))}
{/*<Colors />*/}
@@ -53,7 +55,7 @@ export function Sidebar({ className, activeRequestId, workspaceId, requests }: P
}
function SidebarItem({ request, active }: { request: HttpRequest; active: boolean }) {
const updateRequest = useRequestUpdate(request);
const updateRequest = useUpdateRequest(request);
const [editing, setEditing] = useState<boolean>(false);
const handleSubmitNameEdit = async (el: HTMLInputElement) => {
await updateRequest.mutate({ name: el.value });