mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-20 00:24:24 +01:00
Refactor debounce and tauri event listeners
This commit is contained in:
12
src-web/hooks/useDebouncedSetState.ts
Normal file
12
src-web/hooks/useDebouncedSetState.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { Dispatch, SetStateAction } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { debounce } from '../lib/debounce';
|
||||
|
||||
export function useDebouncedSetState<T extends string | number>(
|
||||
defaultValue: T,
|
||||
delay?: number,
|
||||
): [T, Dispatch<SetStateAction<T>>] {
|
||||
const [state, setState] = useState<T>(defaultValue);
|
||||
const debouncedSetState = useMemo(() => debounce(setState, delay), [delay]);
|
||||
return [state, debouncedSetState];
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
export function useDebouncedValue<T extends string | number>(value: T, delay = 1000) {
|
||||
const [state, setState] = useState<T>(value);
|
||||
const timeout = useRef<NodeJS.Timeout>();
|
||||
|
||||
useEffect(() => {
|
||||
clearTimeout(timeout.current ?? 0);
|
||||
timeout.current = setTimeout(() => setState(value), delay);
|
||||
}, [value, delay]);
|
||||
import { useEffect } from 'react';
|
||||
import { useDebouncedSetState } from './useDebouncedSetState';
|
||||
|
||||
export function useDebouncedValue<T extends string | number>(value: T, delay?: number) {
|
||||
const [state, setState] = useDebouncedSetState<T>(value, delay);
|
||||
useEffect(() => setState(value), [setState, value]);
|
||||
return state;
|
||||
}
|
||||
|
||||
22
src-web/hooks/useTauriEvent.ts
Normal file
22
src-web/hooks/useTauriEvent.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { EventCallback } from '@tauri-apps/api/event';
|
||||
import { listen as tauriListen } from '@tauri-apps/api/event';
|
||||
import type { DependencyList } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export function useTauriEvent<T>(event: string, fn: EventCallback<T>, deps: DependencyList = []) {
|
||||
useEffect(() => {
|
||||
let unMounted = false;
|
||||
let unsubFn: (() => void) | undefined = undefined;
|
||||
|
||||
tauriListen(event, fn).then((unsub) => {
|
||||
if (unMounted) unsub();
|
||||
else unsubFn = unsub;
|
||||
});
|
||||
|
||||
return () => {
|
||||
unMounted = true;
|
||||
unsubFn?.();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [event, fn, ...deps]);
|
||||
}
|
||||
@@ -1,10 +1,5 @@
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import type { EventCallback } from '@tauri-apps/api/event';
|
||||
import { listen as tauriListen } from '@tauri-apps/api/event';
|
||||
import { appWindow } from '@tauri-apps/api/window';
|
||||
import { useEffect } from 'react';
|
||||
import { matchPath } from 'react-router-dom';
|
||||
import { DEFAULT_FONT_SIZE } from '../lib/constants';
|
||||
import { debounce } from '../lib/debounce';
|
||||
import { NAMESPACE_NO_SYNC } from '../lib/keyValueStore';
|
||||
@@ -14,65 +9,44 @@ import { keyValueQueryKey } from './useKeyValue';
|
||||
import { requestsQueryKey } from './useRequests';
|
||||
import { useRequestUpdateKey } from './useRequestUpdateKey';
|
||||
import { responsesQueryKey } from './useResponses';
|
||||
import { routePaths } from './useRoutes';
|
||||
import { useSidebarHidden } from './useSidebarHidden';
|
||||
import { useTauriEvent } from './useTauriEvent';
|
||||
import { workspacesQueryKey } from './useWorkspaces';
|
||||
|
||||
const unsubFns: (() => void)[] = [];
|
||||
export const UPDATE_DEBOUNCE_MILLIS = 100;
|
||||
|
||||
export function useTauriListeners() {
|
||||
const { toggle } = useSidebarHidden();
|
||||
const queryClient = useQueryClient();
|
||||
const { wasUpdatedExternally } = useRequestUpdateKey(null);
|
||||
|
||||
useEffect(() => {
|
||||
let unmounted = false;
|
||||
useTauriEvent<Model>('created_model', ({ payload, windowLabel }) => {
|
||||
if (windowLabel === appWindow.label && payload.model !== 'http_response') return;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
function listen<T>(event: string, fn: EventCallback<T>) {
|
||||
tauriListen(event, fn).then((unsub) => {
|
||||
if (unmounted) unsub();
|
||||
else unsubFns.push(unsub);
|
||||
});
|
||||
const queryKey =
|
||||
payload.model === 'http_request'
|
||||
? requestsQueryKey(payload)
|
||||
: payload.model === 'http_response'
|
||||
? responsesQueryKey(payload)
|
||||
: payload.model === 'workspace'
|
||||
? workspacesQueryKey(payload)
|
||||
: payload.model === 'key_value'
|
||||
? keyValueQueryKey(payload)
|
||||
: null;
|
||||
|
||||
if (queryKey === null) {
|
||||
if (payload.model) {
|
||||
console.log('Unrecognized created model:', payload);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
function listenDebounced<T>(event: string, fn: EventCallback<T>) {
|
||||
listen(event, debounce(fn, UPDATE_DEBOUNCE_MILLIS));
|
||||
const skipSync = payload.model === 'key_value' && payload.namespace === NAMESPACE_NO_SYNC;
|
||||
|
||||
if (!skipSync) {
|
||||
queryClient.setQueryData<Model[]>(queryKey, (values) => [...(values ?? []), payload]);
|
||||
}
|
||||
});
|
||||
|
||||
listen<void>('toggle_sidebar', toggle);
|
||||
listen<void>('refresh', () => location.reload());
|
||||
|
||||
listenDebounced<Model>('created_model', ({ payload, windowLabel }) => {
|
||||
if (windowLabel === appWindow.label && payload.model !== 'http_response') return;
|
||||
|
||||
const queryKey =
|
||||
payload.model === 'http_request'
|
||||
? requestsQueryKey(payload)
|
||||
: payload.model === 'http_response'
|
||||
? responsesQueryKey(payload)
|
||||
: payload.model === 'workspace'
|
||||
? workspacesQueryKey(payload)
|
||||
: payload.model === 'key_value'
|
||||
? keyValueQueryKey(payload)
|
||||
: null;
|
||||
|
||||
if (queryKey === null) {
|
||||
if (payload.model) {
|
||||
console.log('Unrecognized created model:', payload);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const skipSync = payload.model === 'key_value' && payload.namespace === NAMESPACE_NO_SYNC;
|
||||
|
||||
if (!skipSync) {
|
||||
queryClient.setQueryData<Model[]>(queryKey, (values) => [...(values ?? []), payload]);
|
||||
}
|
||||
});
|
||||
|
||||
listenDebounced<Model>('updated_model', ({ payload, windowLabel }) => {
|
||||
useTauriEvent<Model>(
|
||||
'updated_model',
|
||||
debounce(({ payload, windowLabel }) => {
|
||||
if (windowLabel === appWindow.label && payload.model !== 'http_response') return;
|
||||
|
||||
const queryKey =
|
||||
@@ -104,57 +78,38 @@ export function useTauriListeners() {
|
||||
values?.map((v) => (modelsEq(v, payload) ? payload : v)),
|
||||
);
|
||||
}
|
||||
});
|
||||
}, 500),
|
||||
);
|
||||
|
||||
listen<Model>('deleted_model', ({ payload }) => {
|
||||
function removeById<T extends { id: string }>(model: T) {
|
||||
return (entries: T[] | undefined) => entries?.filter((e) => e.id !== model.id);
|
||||
}
|
||||
useTauriEvent<Model>('deleted_model', ({ payload }) => {
|
||||
function removeById<T extends { id: string }>(model: T) {
|
||||
return (entries: T[] | undefined) => entries?.filter((e) => e.id !== model.id);
|
||||
}
|
||||
|
||||
if (payload.model === 'workspace') {
|
||||
queryClient.setQueryData<Workspace[]>(workspacesQueryKey(), removeById(payload));
|
||||
} else if (payload.model === 'http_request') {
|
||||
queryClient.setQueryData<HttpRequest[]>(requestsQueryKey(payload), removeById(payload));
|
||||
} else if (payload.model === 'http_response') {
|
||||
queryClient.setQueryData<HttpResponse[]>(responsesQueryKey(payload), removeById(payload));
|
||||
} else if (payload.model === 'key_value') {
|
||||
queryClient.setQueryData(keyValueQueryKey(payload), undefined);
|
||||
}
|
||||
});
|
||||
if (payload.model === 'workspace') {
|
||||
queryClient.setQueryData<Workspace[]>(workspacesQueryKey(), removeById(payload));
|
||||
} else if (payload.model === 'http_request') {
|
||||
queryClient.setQueryData<HttpRequest[]>(requestsQueryKey(payload), removeById(payload));
|
||||
} else if (payload.model === 'http_response') {
|
||||
queryClient.setQueryData<HttpResponse[]>(responsesQueryKey(payload), removeById(payload));
|
||||
} else if (payload.model === 'key_value') {
|
||||
queryClient.setQueryData(keyValueQueryKey(payload), undefined);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Just call this from the backend instead of this way
|
||||
listen('send_request', async ({ windowLabel }) => {
|
||||
if (windowLabel !== appWindow.label) return;
|
||||
useTauriEvent<number>('zoom', ({ payload: zoomDelta, windowLabel }) => {
|
||||
if (windowLabel !== appWindow.label) return;
|
||||
const fontSize = parseFloat(window.getComputedStyle(document.documentElement).fontSize);
|
||||
|
||||
const params = matchPath(routePaths.request(), window.location.pathname);
|
||||
const requestId = params?.params.requestId;
|
||||
if (typeof requestId !== 'string') {
|
||||
return;
|
||||
}
|
||||
await invoke('send_request', { requestId });
|
||||
});
|
||||
let newFontSize;
|
||||
if (zoomDelta === 0) {
|
||||
newFontSize = DEFAULT_FONT_SIZE;
|
||||
} else if (zoomDelta > 0) {
|
||||
newFontSize = Math.min(fontSize * 1.1, DEFAULT_FONT_SIZE * 5);
|
||||
} else if (zoomDelta < 0) {
|
||||
newFontSize = Math.max(fontSize * 0.9, DEFAULT_FONT_SIZE * 0.4);
|
||||
}
|
||||
|
||||
listen<number>('zoom', ({ payload: zoomDelta, windowLabel }) => {
|
||||
if (windowLabel !== appWindow.label) return;
|
||||
const fontSize = parseFloat(window.getComputedStyle(document.documentElement).fontSize);
|
||||
|
||||
let newFontSize;
|
||||
if (zoomDelta === 0) {
|
||||
newFontSize = DEFAULT_FONT_SIZE;
|
||||
} else if (zoomDelta > 0) {
|
||||
newFontSize = Math.min(fontSize * 1.1, DEFAULT_FONT_SIZE * 5);
|
||||
} else if (zoomDelta < 0) {
|
||||
newFontSize = Math.max(fontSize * 0.9, DEFAULT_FONT_SIZE * 0.4);
|
||||
}
|
||||
|
||||
document.documentElement.style.fontSize = `${newFontSize}px`;
|
||||
});
|
||||
|
||||
return () => {
|
||||
unmounted = true;
|
||||
for (const unsub of unsubFns) {
|
||||
unsub();
|
||||
}
|
||||
};
|
||||
}, [queryClient, toggle, wasUpdatedExternally]);
|
||||
document.documentElement.style.fontSize = `${newFontSize}px`;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user