mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-25 10:18:31 +02:00
Better sidebar collapse, debuonce container uqeries, fix recent requests
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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()}`,
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|||||||
@@ -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],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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]);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user