Better sidebar collapse, debuonce container uqeries, fix recent requests

This commit is contained in:
Gregory Schier
2024-02-15 15:07:15 -08:00
parent f45c898be0
commit 4566ede184
10 changed files with 36 additions and 28 deletions

View File

@@ -1,7 +1,8 @@
import useResizeObserver from '@react-hook/resize-observer'; import useResizeObserver from '@react-hook/resize-observer';
import classNames from 'classnames'; import classNames from 'classnames';
import type { CSSProperties, FormEvent } from 'react'; import type { CSSProperties, FormEvent } from 'react';
import React, { useCallback, useMemo, useRef, useState } from 'react'; import React, { useCallback, useMemo, useRef } from 'react';
import { useDebouncedState } from '../hooks/useDebouncedState';
import type { ReflectResponseService } from '../hooks/useGrpc'; import type { ReflectResponseService } from '../hooks/useGrpc';
import { useGrpcConnections } from '../hooks/useGrpcConnections'; import { useGrpcConnections } from '../hooks/useGrpcConnections';
import { useUpdateGrpcRequest } from '../hooks/useUpdateGrpcRequest'; import { useUpdateGrpcRequest } from '../hooks/useUpdateGrpcRequest';
@@ -57,7 +58,7 @@ export function GrpcConnectionSetupPane({
const activeConnection = connections[0] ?? null; const activeConnection = connections[0] ?? null;
const isStreaming = activeConnection?.elapsed === 0; const isStreaming = activeConnection?.elapsed === 0;
const [paneSize, setPaneSize] = useState(99999); const [paneSize, setPaneSize] = useDebouncedState(99999, 100);
const urlContainerEl = useRef<HTMLDivElement>(null); const urlContainerEl = useRef<HTMLDivElement>(null);
useResizeObserver<HTMLDivElement>(urlContainerEl.current, (entry) => { useResizeObserver<HTMLDivElement>(urlContainerEl.current, (entry) => {
setPaneSize(entry.contentRect.width); setPaneSize(entry.contentRect.width);

View File

@@ -7,8 +7,8 @@ import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId';
import { useAppRoutes } from '../hooks/useAppRoutes'; import { useAppRoutes } from '../hooks/useAppRoutes';
import { useGrpcRequests } from '../hooks/useGrpcRequests'; import { useGrpcRequests } from '../hooks/useGrpcRequests';
import { useHotKey } from '../hooks/useHotKey'; import { useHotKey } from '../hooks/useHotKey';
import { useRecentRequests } from '../hooks/useRecentRequests';
import { useHttpRequests } from '../hooks/useHttpRequests'; import { useHttpRequests } from '../hooks/useHttpRequests';
import { useRecentRequests } from '../hooks/useRecentRequests';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { fallbackRequestName } from '../lib/fallbackRequestName';
import type { ButtonProps } from './core/Button'; import type { ButtonProps } from './core/Button';
import { Button } from './core/Button'; import { Button } from './core/Button';
@@ -77,10 +77,11 @@ export function RecentRequestsDropdown({ className }: Pick<ButtonProps, 'classNa
if (recentRequestItems.length === 0) { if (recentRequestItems.length === 0) {
return [ return [
{ {
key: 'no-recent-requests',
label: 'No recent requests', label: 'No recent requests',
disabled: true, disabled: true,
}, },
] as DropdownItem[]; ];
} }
return recentRequestItems.slice(0, 20); return recentRequestItems.slice(0, 20);

View File

@@ -40,17 +40,17 @@ export const SidebarActions = memo(function SidebarActions() {
label: 'HTTP Request', label: 'HTTP Request',
onSelect: () => createHttpRequest.mutate({}), onSelect: () => createHttpRequest.mutate({}),
}, },
{
key: 'create-grpc-request',
label: 'GRPC Request',
onSelect: () => createGrpcRequest.mutate({}),
},
{ {
key: 'create-graphql-request', key: 'create-graphql-request',
label: 'GraphQL Request', label: 'GraphQL Query',
onSelect: () => onSelect: () =>
createHttpRequest.mutate({ bodyType: BODY_TYPE_GRAPHQL, method: 'POST' }), createHttpRequest.mutate({ bodyType: BODY_TYPE_GRAPHQL, method: 'POST' }),
}, },
{
key: 'create-grpc-request',
label: 'GRPC Call',
onSelect: () => createGrpcRequest.mutate({}),
},
{ {
key: 'create-folder', key: 'create-folder',
label: 'Folder', label: 'Folder',

View File

@@ -69,7 +69,7 @@ export const UrlBar = memo(function UrlBar({
<RequestMethodDropdown <RequestMethodDropdown
method={method} method={method}
onChange={onMethodChange} onChange={onMethodChange}
className="mx-0.5 my-0.5" className="!h-auto my-0.5 mr-0.5"
/> />
) )
} }
@@ -80,7 +80,7 @@ export const UrlBar = memo(function UrlBar({
iconSize="md" iconSize="md"
title="Send Request" title="Send Request"
type="submit" type="submit"
className="w-8 mr-0.5 my-0.5" className="w-8 !h-auto my-0.5 mr-0.5"
icon={isLoading ? 'update' : submitIcon} icon={isLoading ? 'update' : submitIcon}
spin={isLoading} spin={isLoading}
hotkeyAction="http_request.send" hotkeyAction="http_request.send"

View File

@@ -71,7 +71,7 @@ export default function Workspace() {
move: async (e: MouseEvent) => { move: async (e: MouseEvent) => {
e.preventDefault(); // Prevent text selection and things e.preventDefault(); // Prevent text selection and things
const newWidth = startWidth + (e.clientX - mouseStartX); const newWidth = startWidth + (e.clientX - mouseStartX);
if (newWidth < 100) { if (newWidth < 50) {
await hide(); await hide();
resetWidth(); resetWidth();
} else { } else {

View File

@@ -5,6 +5,7 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
import { useLocalStorage } from 'react-use'; import { useLocalStorage } from 'react-use';
import { useActiveRequestId } from '../../hooks/useActiveRequestId'; import { useActiveRequestId } from '../../hooks/useActiveRequestId';
import { useActiveWorkspaceId } from '../../hooks/useActiveWorkspaceId'; import { useActiveWorkspaceId } from '../../hooks/useActiveWorkspaceId';
import { useDebouncedState } from '../../hooks/useDebouncedState';
import { clamp } from '../../lib/clamp'; import { clamp } from '../../lib/clamp';
import { ResizeHandle } from '../ResizeHandle'; import { ResizeHandle } from '../ResizeHandle';
import { HotKeyList } from './HotKeyList'; import { HotKeyList } from './HotKeyList';
@@ -44,7 +45,7 @@ export function SplitLayout({
minWidthPx = 10, minWidthPx = 10,
}: Props) { }: Props) {
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const [vertical, setVertical] = useState<boolean>(false); const [vertical, setVertical] = useDebouncedState<boolean>(false, 100);
const [widthRaw, setWidth] = useLocalStorage<number>(`${name}_width::${useActiveWorkspaceId()}`); const [widthRaw, setWidth] = useLocalStorage<number>(`${name}_width::${useActiveWorkspaceId()}`);
const [heightRaw, setHeight] = useLocalStorage<number>( const [heightRaw, setHeight] = useLocalStorage<number>(
`${name}_height::${useActiveWorkspaceId()}`, `${name}_height::${useActiveWorkspaceId()}`,

View File

@@ -4,7 +4,7 @@ import { debounce } from '../lib/debounce';
export function useDebouncedState<T>( export function useDebouncedState<T>(
defaultValue: T, defaultValue: T,
delay?: number, delay = 500,
): [T, Dispatch<SetStateAction<T>>, Dispatch<SetStateAction<T>>] { ): [T, Dispatch<SetStateAction<T>>, Dispatch<SetStateAction<T>>] {
const [state, setState] = useState<T>(defaultValue); const [state, setState] = useState<T>(defaultValue);
const debouncedSetState = useMemo(() => debounce(setState, delay), [delay]); const debouncedSetState = useMemo(() => debounce(setState, delay), [delay]);

View File

@@ -45,11 +45,13 @@ export function useKeyValue<T extends Object | null>({
if (newV === kv) return; if (newV === kv) return;
return mutate.mutateAsync(newV); return mutate.mutateAsync(newV);
}); });
} else if (value !== query.data) { } else {
// TODO: Make this only update if the value is different. I tried this but it seems query.data
// is stale.
await mutate.mutateAsync(value); await mutate.mutateAsync(value);
} }
}, },
[defaultValue, key, mutate, namespace, query.data], [defaultValue, key, mutate, namespace],
); );
const reset = useCallback(async () => mutate.mutateAsync(defaultValue), [mutate, defaultValue]); const reset = useCallback(async () => mutate.mutateAsync(defaultValue), [mutate, defaultValue]);

View File

@@ -1,14 +1,13 @@
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import { createGlobalState, useEffectOnce } from 'react-use'; import { createGlobalState } from 'react-use';
import { getKeyValue, NAMESPACE_GLOBAL } from '../lib/keyValueStore'; import { getKeyValue, NAMESPACE_GLOBAL } from '../lib/keyValueStore';
import { useActiveRequestId } from './useActiveRequestId'; import { useActiveRequestId } from './useActiveRequestId';
import { useActiveWorkspaceId } from './useActiveWorkspaceId'; import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useGrpcRequests } from './useGrpcRequests'; import { useGrpcRequests } from './useGrpcRequests';
import { useHttpRequest } from './useHttpRequest';
import { useHttpRequests } from './useHttpRequests'; import { useHttpRequests } from './useHttpRequests';
import { useKeyValue } from './useKeyValue'; import { useKeyValue } from './useKeyValue';
const useHistoryState = createGlobalState<string[]>([]); const useHistoryState = createGlobalState<string[] | null>(null);
const kvKey = (workspaceId: string) => 'recent_requests::' + workspaceId; const kvKey = (workspaceId: string) => 'recent_requests::' + workspaceId;
const namespace = NAMESPACE_GLOBAL; const namespace = NAMESPACE_GLOBAL;
@@ -29,29 +28,33 @@ export function useRecentRequests() {
}); });
// Load local storage state on initial render // Load local storage state on initial render
useEffectOnce(() => { useEffect(() => {
if (kv.value) { if (kv.value) {
console.log('SET HISTORY', kv.value);
setHistory(kv.value); setHistory(kv.value);
} }
}); }, [kv.isLoading]);
// Update local storage state when history changes // Update local storage state when history changes
useEffect(() => { useEffect(() => {
if (history == null) return;
console.log('SET KV', history);
kv.set(history); kv.set(history);
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [history]); }, [history]);
// Set history when active request changes // Set history when active request changes
useEffect(() => { useEffect(() => {
setHistory((currentHistory: string[]) => { setHistory((currentHistory) => {
if (activeRequestId === null) return currentHistory; console.log('ACTIVE REQUEST CHANGED', kv.isLoading, activeRequestId, currentHistory);
if (activeRequestId === null || currentHistory == null) return currentHistory;
const withoutCurrentRequest = currentHistory.filter((id) => id !== activeRequestId); const withoutCurrentRequest = currentHistory.filter((id) => id !== activeRequestId);
return [activeRequestId, ...withoutCurrentRequest]; return [activeRequestId, ...withoutCurrentRequest];
}); });
}, [activeRequestId, setHistory]); }, [activeRequestId, kv.isLoading, setHistory]);
const onlyValidIds = useMemo( const onlyValidIds = useMemo(
() => history.filter((id) => requests.some((r) => r.id === id)), () => history?.filter((id) => requests.some((r) => r.id === id)) ?? [],
[history, requests], [history, requests],
); );

View File

@@ -4,7 +4,7 @@ import { useActiveWorkspaceId } from './useActiveWorkspaceId';
export function useSidebarWidth() { export function useSidebarWidth() {
const activeWorkspaceId = useActiveWorkspaceId(); const activeWorkspaceId = useActiveWorkspaceId();
const [width, setWidth] = useLocalStorage<number>(`sidebar_width::${activeWorkspaceId}`, 220); const [width, setWidth] = useLocalStorage<number>(`sidebar_width::${activeWorkspaceId}`, 250);
const resetWidth = useCallback(() => setWidth(220), [setWidth]); const resetWidth = useCallback(() => setWidth(250), [setWidth]);
return useMemo(() => ({ width, setWidth, resetWidth }), [width, setWidth, resetWidth]); return useMemo(() => ({ width, setWidth, resetWidth }), [width, setWidth, resetWidth]);
} }