mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-25 10:51:57 +01:00
Better tauri listeners and stuff
This commit is contained in:
@@ -1,25 +1,11 @@
|
||||
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { persistQueryClient } from '@tanstack/react-query-persist-client';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { appWindow } from '@tauri-apps/api/window';
|
||||
import { MotionConfig } from 'framer-motion';
|
||||
import { Suspense } from 'react';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
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 { UPDATE_DEBOUNCE_MILLIS } from '../hooks/useTauriListeners';
|
||||
import { workspacesQueryKey } from '../hooks/useWorkspaces';
|
||||
import { DEFAULT_FONT_SIZE } from '../lib/constants';
|
||||
import { debounce } from '../lib/debounce';
|
||||
import { extractKeyValue } from '../lib/keyValueStore';
|
||||
import type { HttpRequest, HttpResponse, KeyValue, Workspace } from '../lib/models';
|
||||
import { AppRouter } from './AppRouter';
|
||||
import { DialogProvider } from './DialogContext';
|
||||
|
||||
@@ -43,106 +29,6 @@ persistQueryClient({
|
||||
maxAge: 1000 * 60 * 60 * 24, // 24 hours
|
||||
});
|
||||
|
||||
await listen(
|
||||
'updated_key_value',
|
||||
debounce(({ payload: keyValue }: { payload: KeyValue }) => {
|
||||
if (keyValue.updatedBy === appWindow.label) return;
|
||||
queryClient.setQueryData(keyValueQueryKey(keyValue), extractKeyValue(keyValue));
|
||||
}, UPDATE_DEBOUNCE_MILLIS),
|
||||
);
|
||||
|
||||
await listen('updated_response', ({ payload: response }: { payload: HttpResponse }) => {
|
||||
queryClient.setQueryData(
|
||||
responsesQueryKey(response.requestId),
|
||||
(responses: HttpResponse[] = []) => {
|
||||
// We want updates from every response
|
||||
// if (response.updatedBy === appWindow.label) return;
|
||||
|
||||
const newResponses = [];
|
||||
let found = false;
|
||||
for (const r of responses) {
|
||||
if (r.id === response.id) {
|
||||
found = true;
|
||||
newResponses.push(response);
|
||||
} else {
|
||||
newResponses.push(r);
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
newResponses.push(response);
|
||||
}
|
||||
return newResponses;
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
await listen(
|
||||
'updated_workspace',
|
||||
debounce(({ payload: workspace }: { payload: Workspace }) => {
|
||||
queryClient.setQueryData(workspacesQueryKey(), (workspaces: Workspace[] = []) => {
|
||||
if (workspace.updatedBy === appWindow.label) return;
|
||||
|
||||
const newWorkspaces = [];
|
||||
let found = false;
|
||||
for (const w of workspaces) {
|
||||
if (w.id === workspace.id) {
|
||||
found = true;
|
||||
newWorkspaces.push(workspace);
|
||||
} else {
|
||||
newWorkspaces.push(w);
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
newWorkspaces.push(workspace);
|
||||
}
|
||||
return newWorkspaces;
|
||||
});
|
||||
}, UPDATE_DEBOUNCE_MILLIS),
|
||||
);
|
||||
|
||||
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(routePaths.request(), window.location.pathname);
|
||||
const requestId = params?.params.requestId;
|
||||
if (typeof requestId !== 'string') {
|
||||
return;
|
||||
}
|
||||
await invoke('send_request', { requestId });
|
||||
});
|
||||
|
||||
await listen('zoom', ({ payload: zoomDelta }: { payload: number }) => {
|
||||
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`;
|
||||
});
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import classnames from 'classnames';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { createGlobalState } from 'react-use';
|
||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||
import { useKeyValue } from '../hooks/useKeyValue';
|
||||
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
|
||||
import { useUpdateRequest } from '../hooks/useUpdateRequest';
|
||||
import { tryFormatJson } from '../lib/formatters';
|
||||
@@ -33,15 +33,14 @@ interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const useActiveTab = createGlobalState<string>('body');
|
||||
|
||||
export const RequestPane = memo(function RequestPane({ style, fullHeight, className }: Props) {
|
||||
const activeRequest = useActiveRequest();
|
||||
const activeRequestId = activeRequest?.id ?? null;
|
||||
const updateRequest = useUpdateRequest(activeRequestId);
|
||||
const [forceUpdateHeaderEditorKey, setForceUpdateHeaderEditorKey] = useState<number>(0);
|
||||
const activeTab = useKeyValue<string>({
|
||||
key: ['active_request_body_tab'],
|
||||
defaultValue: 'body',
|
||||
});
|
||||
const [activeTab, setActiveTab] = useActiveTab();
|
||||
|
||||
const tabs: TabItem[] = useMemo(
|
||||
() =>
|
||||
@@ -136,8 +135,8 @@ export const RequestPane = memo(function RequestPane({ style, fullHeight, classN
|
||||
<>
|
||||
<UrlBar id={activeRequest.id} url={activeRequest.url} method={activeRequest.method} />
|
||||
<Tabs
|
||||
value={activeTab.value}
|
||||
onChangeValue={activeTab.set}
|
||||
value={activeTab}
|
||||
onChangeValue={setActiveTab}
|
||||
tabs={tabs}
|
||||
className="mt-2"
|
||||
label="Request body"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useTheme } from '../hooks/useTheme';
|
||||
import { toggleAppearance } from '../lib/theme/window';
|
||||
import { IconButton } from './core/IconButton';
|
||||
|
||||
export function ToggleThemeButton() {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import { useKeyValue } from '../hooks/useKeyValue';
|
||||
import { useRoutes } from '../hooks/useRoutes';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { Heading } from './core/Heading';
|
||||
|
||||
export default function Workspaces() {
|
||||
const lastWorkspace = useKeyValue<string | null>({ key: 'last_workspace', defaultValue: null });
|
||||
const routes = useRoutes();
|
||||
const workspaces = useWorkspaces();
|
||||
const workspace = workspaces[0];
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import classnames from 'classnames';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useMemo } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { Overlay } from '../Overlay';
|
||||
import { IconButton } from './IconButton';
|
||||
import { HStack, VStack } from './Stacks';
|
||||
import { VStack } from './Stacks';
|
||||
|
||||
export interface DialogProps {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -33,7 +33,6 @@ import {
|
||||
} from '@codemirror/view';
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import { graphql, graphqlLanguageSupport } from 'cm6-graphql';
|
||||
import { render } from 'react-dom';
|
||||
import type { EditorProps } from './index';
|
||||
import { text } from './text/extension';
|
||||
import { twig } from './twig/extension';
|
||||
|
||||
@@ -3,23 +3,28 @@ import type { DropdownItemSeparator, DropdownProps } from './Dropdown';
|
||||
import { Dropdown } from './Dropdown';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
export type RadioDropdownItem =
|
||||
export type RadioDropdownItem<T = string | null> =
|
||||
| {
|
||||
type?: 'default';
|
||||
label: string;
|
||||
shortLabel?: string;
|
||||
value: string | null;
|
||||
value: T;
|
||||
}
|
||||
| DropdownItemSeparator;
|
||||
|
||||
export interface RadioDropdownProps {
|
||||
value: string | null;
|
||||
onChange: (value: string | null) => void;
|
||||
items: RadioDropdownItem[];
|
||||
export interface RadioDropdownProps<T = string | null> {
|
||||
value: T;
|
||||
onChange: (value: T) => void;
|
||||
items: RadioDropdownItem<T>[];
|
||||
children: DropdownProps['children'];
|
||||
}
|
||||
|
||||
export function RadioDropdown({ value, items, onChange, children }: RadioDropdownProps) {
|
||||
export function RadioDropdown<T = string | null>({
|
||||
value,
|
||||
items,
|
||||
onChange,
|
||||
children,
|
||||
}: RadioDropdownProps<T>) {
|
||||
const dropdownItems = useMemo(
|
||||
() =>
|
||||
items.map((item) => {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { useRef } from 'react';
|
||||
import { useMount } from 'react-use';
|
||||
import { Button } from '../components/core/Button';
|
||||
import { HStack } from '../components/core/Stacks';
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export function useDeleteRequest(id: string | null) {
|
||||
},
|
||||
onSuccess: async () => {
|
||||
if (workspaceId === null || id === null) return;
|
||||
await queryClient.invalidateQueries(requestsQueryKey(workspaceId));
|
||||
await queryClient.invalidateQueries(requestsQueryKey({ workspaceId }));
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export function useDeleteResponses(requestId?: string) {
|
||||
},
|
||||
onSuccess: async () => {
|
||||
if (!requestId) return;
|
||||
await queryClient.invalidateQueries(responsesQueryKey(requestId));
|
||||
await queryClient.invalidateQueries(responsesQueryKey({ requestId }));
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { invoke } from '@tauri-apps/api';
|
||||
import type { HttpRequest } from '../lib/models';
|
||||
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
|
||||
|
||||
export function requestsQueryKey(workspaceId: string) {
|
||||
export function requestsQueryKey({ workspaceId }: { workspaceId: string }) {
|
||||
return ['http_requests', { workspaceId }];
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export function useRequests() {
|
||||
return (
|
||||
useQuery({
|
||||
enabled: workspaceId != null,
|
||||
queryKey: requestsQueryKey(workspaceId ?? 'n/a'),
|
||||
queryKey: requestsQueryKey({ workspaceId: workspaceId ?? 'n/a' }),
|
||||
queryFn: async () => {
|
||||
if (workspaceId == null) return [];
|
||||
return (await invoke('requests', { workspaceId })) as HttpRequest[];
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import type { HttpResponse } from '../lib/models';
|
||||
|
||||
export function responsesQueryKey(requestId: string) {
|
||||
export function responsesQueryKey({ requestId }: { requestId: string }) {
|
||||
return ['http_responses', { requestId }];
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export function useResponses(requestId: string | null) {
|
||||
useQuery<HttpResponse[]>({
|
||||
enabled: requestId !== null,
|
||||
initialData: [],
|
||||
queryKey: responsesQueryKey(requestId ?? 'n/a'),
|
||||
queryKey: responsesQueryKey({ requestId: requestId ?? 'n/a' }),
|
||||
queryFn: async () => {
|
||||
return (await invoke('responses', {
|
||||
requestId,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
import { NAMESPACE_NO_SYNC } from '../lib/keyValueStore';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
|
||||
const START_WIDTH = 200;
|
||||
@@ -15,6 +16,7 @@ export interface SidebarDisplay {
|
||||
|
||||
export function useSidebarDisplay() {
|
||||
const display = useKeyValue<SidebarDisplay>({
|
||||
namespace: NAMESPACE_NO_SYNC,
|
||||
key: sidebarDisplayKey,
|
||||
defaultValue: sidebarDisplayDefaultValue,
|
||||
});
|
||||
|
||||
@@ -1,70 +1,136 @@
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import type { EventCallback } from '@tauri-apps/api/event';
|
||||
import { appWindow } from '@tauri-apps/api/window';
|
||||
import { useEffect } from 'react';
|
||||
import { matchPath } from 'react-router-dom';
|
||||
import { useEffectOnce } from 'react-use';
|
||||
import { DEFAULT_FONT_SIZE } from '../lib/constants';
|
||||
import { debounce } from '../lib/debounce';
|
||||
import type { HttpRequest } from '../lib/models';
|
||||
import { extractKeyValue, NAMESPACE_NO_SYNC } from '../lib/keyValueStore';
|
||||
import type { HttpRequest, HttpResponse, KeyValue, Model, Workspace } from '../lib/models';
|
||||
import { modelsEq } from '../lib/models';
|
||||
import { keyValueQueryKey } from './useKeyValue';
|
||||
import { requestsQueryKey } from './useRequests';
|
||||
import { useRequestUpdateKey } from './useRequestUpdateKey';
|
||||
import { responsesQueryKey } from './useResponses';
|
||||
import { routePaths } from './useRoutes';
|
||||
import { useSidebarDisplay } from './useSidebarDisplay';
|
||||
import { workspacesQueryKey } from './useWorkspaces';
|
||||
|
||||
const unsubFns: (() => void)[] = [];
|
||||
export const UPDATE_DEBOUNCE_MILLIS = 1000;
|
||||
export const UPDATE_DEBOUNCE_MILLIS = 100;
|
||||
|
||||
export function useTauriListeners() {
|
||||
const sidebarDisplay = useSidebarDisplay();
|
||||
const queryClient = useQueryClient();
|
||||
const { wasUpdatedExternally } = useRequestUpdateKey(null);
|
||||
|
||||
useEffect(() => {
|
||||
useEffectOnce(() => {
|
||||
let unmounted = false;
|
||||
|
||||
appWindow
|
||||
.listen('toggle_sidebar', async () => {
|
||||
sidebarDisplay.toggle();
|
||||
})
|
||||
.then((unsub) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
function listen<T>(event: string, fn: EventCallback<T>) {
|
||||
appWindow.listen(event, fn).then((unsub) => {
|
||||
if (unmounted) unsub();
|
||||
else unsubFns.push(unsub);
|
||||
});
|
||||
}
|
||||
|
||||
listen('refresh', () => {
|
||||
location.reload();
|
||||
}).then((unsub) => {
|
||||
if (unmounted) unsub();
|
||||
else unsubFns.push(unsub);
|
||||
function listenDebounced<T>(event: string, fn: EventCallback<T>) {
|
||||
listen(event, debounce(fn, UPDATE_DEBOUNCE_MILLIS));
|
||||
}
|
||||
|
||||
listen('toggle_sidebar', sidebarDisplay.toggle);
|
||||
listen('refresh', () => location.reload());
|
||||
|
||||
listenDebounced('updated_key_value', ({ payload: keyValue }: { payload: KeyValue }) => {
|
||||
if (keyValue.namespace !== NAMESPACE_NO_SYNC) {
|
||||
queryClient.setQueryData(keyValueQueryKey(keyValue), extractKeyValue(keyValue));
|
||||
}
|
||||
});
|
||||
|
||||
appWindow
|
||||
.listen(
|
||||
'updated_request',
|
||||
debounce(({ payload: request }: { payload: HttpRequest }) => {
|
||||
queryClient.setQueryData(
|
||||
requestsQueryKey(request.workspaceId),
|
||||
(requests: HttpRequest[] = []) => {
|
||||
const newRequests = [];
|
||||
let found = false;
|
||||
for (const r of requests) {
|
||||
if (r.id === request.id) {
|
||||
found = true;
|
||||
newRequests.push(request);
|
||||
} else {
|
||||
newRequests.push(r);
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
newRequests.push(request);
|
||||
}
|
||||
setTimeout(() => wasUpdatedExternally(request.id), 50);
|
||||
return newRequests;
|
||||
},
|
||||
);
|
||||
}, UPDATE_DEBOUNCE_MILLIS),
|
||||
)
|
||||
.then((unsub) => {
|
||||
if (unmounted) unsub();
|
||||
else unsubFns.push(unsub);
|
||||
});
|
||||
listenDebounced('updated_model', ({ payload }: { payload: Model }) => {
|
||||
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) {
|
||||
throw new Error('Unrecognized updated model ' + payload.model);
|
||||
}
|
||||
|
||||
const skipSync = payload.model === 'key_value' && payload.namespace === NAMESPACE_NO_SYNC;
|
||||
|
||||
if (!skipSync) {
|
||||
queryClient.setQueryData(queryKey, (values: Model[] = []) => {
|
||||
const newValues = [];
|
||||
let found = false;
|
||||
for (const v of values) {
|
||||
if (modelsEq(v, payload)) {
|
||||
found = true;
|
||||
newValues.push(payload);
|
||||
} else {
|
||||
newValues.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
// Doesn't exist already, so add it to the list
|
||||
if (!found) newValues.push(payload);
|
||||
|
||||
if (payload.model === 'http_request') {
|
||||
setTimeout(() => wasUpdatedExternally(payload.id), 50);
|
||||
}
|
||||
|
||||
return newValues;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
listen('deleted_model', ({ payload: model }: { payload: Model }) => {
|
||||
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), removeById<HttpRequest>(model));
|
||||
} else if (model.model === 'http_response') {
|
||||
queryClient.setQueryData(responsesQueryKey(model), removeById<HttpResponse>(model));
|
||||
} else if (model.model === 'key_value') {
|
||||
queryClient.setQueryData(keyValueQueryKey(model), undefined);
|
||||
}
|
||||
});
|
||||
|
||||
listen('send_request', async () => {
|
||||
const params = matchPath(routePaths.request(), window.location.pathname);
|
||||
const requestId = params?.params.requestId;
|
||||
if (typeof requestId !== 'string') {
|
||||
return;
|
||||
}
|
||||
await invoke('send_request', { requestId });
|
||||
});
|
||||
|
||||
listen('zoom', ({ payload: zoomDelta }: { payload: number }) => {
|
||||
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;
|
||||
@@ -72,5 +138,5 @@ export function useTauriListeners() {
|
||||
unsub();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
export function useUniqueKey(len = 10): { key: string; regenerate: () => void } {
|
||||
const [key, setKey] = useState<string>(() => generate(len));
|
||||
return { key, wasUpdatedExternally: () => setKey(generate(len)) };
|
||||
}
|
||||
|
||||
const CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
|
||||
function generate(len: number): string {
|
||||
const chars = [];
|
||||
for (let i = 0; i < len; i++) {
|
||||
chars.push(CHARS[Math.floor(Math.random() * CHARS.length)]);
|
||||
}
|
||||
return chars.join('');
|
||||
}
|
||||
@@ -19,10 +19,8 @@ export function useUpdateAnyRequest() {
|
||||
onMutate: async ({ id, update }) => {
|
||||
const request = await getRequest(id);
|
||||
if (request === null) return;
|
||||
queryClient.setQueryData(
|
||||
requestsQueryKey(request?.workspaceId),
|
||||
(requests: HttpRequest[] | undefined) =>
|
||||
requests?.map((r) => (r.id === request.id ? update(r) : r)),
|
||||
queryClient.setQueryData(requestsQueryKey(request), (requests: HttpRequest[] | undefined) =>
|
||||
requests?.map((r) => (r.id === request.id ? update(r) : r)),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { appWindow } from '@tauri-apps/api/window';
|
||||
import type { HttpRequest } from '../lib/models';
|
||||
import { getRequest } from '../lib/store';
|
||||
import { requestsQueryKey } from './useRequests';
|
||||
@@ -21,14 +20,9 @@ export function useUpdateRequest(id: string | null) {
|
||||
const request = await getRequest(id);
|
||||
if (request === null) return;
|
||||
|
||||
// Sync updatedBy so that the UI doesn't think the update is coming from elsewhere
|
||||
request.updatedBy = appWindow.label;
|
||||
|
||||
const newRequest = typeof v === 'function' ? v(request) : { ...request, ...v };
|
||||
queryClient.setQueryData(
|
||||
requestsQueryKey(request?.workspaceId),
|
||||
(requests: HttpRequest[] | undefined) =>
|
||||
requests?.map((r) => (r.id === newRequest.id ? newRequest : r)),
|
||||
queryClient.setQueryData(requestsQueryKey(request), (requests: HttpRequest[] | undefined) =>
|
||||
requests?.map((r) => (r.id === newRequest.id ? newRequest : r)),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import type { Workspace } from '../lib/models';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
export function workspacesQueryKey() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/ban-types
|
||||
export function workspacesQueryKey(_?: {}) {
|
||||
return ['workspaces'];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import type { KeyValue } from './models';
|
||||
|
||||
const DEFAULT_NAMESPACE = 'app';
|
||||
export const NAMESPACE_GLOBAL = 'global';
|
||||
export const NAMESPACE_NO_SYNC = 'no_sync';
|
||||
|
||||
export async function setKeyValue<T>({
|
||||
namespace = DEFAULT_NAMESPACE,
|
||||
namespace = NAMESPACE_GLOBAL,
|
||||
key,
|
||||
value,
|
||||
}: {
|
||||
@@ -20,7 +21,7 @@ export async function setKeyValue<T>({
|
||||
}
|
||||
|
||||
export async function getKeyValue<T>({
|
||||
namespace = DEFAULT_NAMESPACE,
|
||||
namespace = NAMESPACE_GLOBAL,
|
||||
key,
|
||||
fallback,
|
||||
}: {
|
||||
|
||||
@@ -2,7 +2,6 @@ export interface BaseModel {
|
||||
readonly id: string;
|
||||
readonly createdAt: string;
|
||||
readonly updatedAt: string;
|
||||
updatedBy: string;
|
||||
}
|
||||
|
||||
export interface Workspace extends BaseModel {
|
||||
@@ -26,6 +25,18 @@ export const AUTH_TYPE_NONE = null;
|
||||
export const AUTH_TYPE_BASIC = 'basic';
|
||||
export const AUTH_TYPE_BEARER = 'bearer';
|
||||
|
||||
export type Model = Workspace | HttpRequest | HttpResponse | KeyValue;
|
||||
|
||||
export function modelsEq(a: Model, b: Model) {
|
||||
if (a.model === 'key_value' && b.model === 'key_value') {
|
||||
return a.key === b.key && a.namespace === b.namespace;
|
||||
}
|
||||
if ('id' in a && 'id' in b) {
|
||||
return a.id === b.id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export interface HttpRequest extends BaseModel {
|
||||
readonly workspaceId: string;
|
||||
readonly model: 'http_request';
|
||||
|
||||
Reference in New Issue
Block a user