Performance sweep (#147)

This commit is contained in:
Gregory Schier
2024-12-20 17:31:15 -08:00
committed by GitHub
parent 42bf016e90
commit 27134a52ad
85 changed files with 2337 additions and 1413 deletions

View File

@@ -1,5 +1,6 @@
import { useSearch } from '@tanstack/react-router';
import { useCallback, useEffect, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { Route } from '../routes/workspaces/$workspaceId';
import { useCookieJars } from './useCookieJars';
export const QUERY_COOKIE_JAR_ID = 'cookie_jar_id';
@@ -34,23 +35,18 @@ export function useEnsureActiveCookieJar() {
// There's no active jar, so set it to the first one
console.log('Setting active cookie jar to', firstJar.id);
setActiveCookieJarId(firstJar.id);
setActiveCookieJarId(firstJar.id).catch(console.error);
}, [activeCookieJarId, cookieJars, setActiveCookieJarId]);
}
function useActiveCookieJarId() {
// NOTE: This query param is accessed from Rust side, so do not change
const [params, setParams] = useSearchParams();
const id = params.get(QUERY_COOKIE_JAR_ID);
const navigate = Route.useNavigate();
const { cookieJarId: id } = useSearch({ strict: false });
const setId = useCallback(
(id: string) => {
setParams((p) => {
const existing = Object.fromEntries(p);
return { ...existing, [QUERY_COOKIE_JAR_ID]: id };
});
},
[setParams],
(id: string) => navigate({ search: (prev) => ({ ...prev, cookieJarId: id }) }),
[navigate],
);
return [id, setId] as const;

View File

@@ -1,5 +1,6 @@
import { useSearch } from '@tanstack/react-router';
import { useCallback, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { Route } from '../routes/workspaces/$workspaceId';
import { useEnvironments } from './useEnvironments';
export function useActiveEnvironment() {
@@ -16,22 +17,13 @@ export const QUERY_ENVIRONMENT_ID = 'environment_id';
function useActiveEnvironmentId() {
// NOTE: This query param is accessed from Rust side, so do not change
const [params, setParams] = useSearchParams();
const id = params.get(QUERY_ENVIRONMENT_ID);
const navigate = Route.useNavigate();
const { environmentId: id } = useSearch({ strict: false });
const setId = useCallback(
(id: string | null) => {
setParams((p) => {
const existing = Object.fromEntries(p);
if (id == null) {
delete existing[QUERY_ENVIRONMENT_ID];
} else {
existing[QUERY_ENVIRONMENT_ID] = id;
}
return existing;
});
},
[setParams],
(environment_id: string | null) =>
navigate({ search: (prev) => ({ ...prev, environment_id: environment_id ?? undefined }) }),
[navigate],
);
return [id, setId] as const;

View File

@@ -1,24 +1,30 @@
import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
import { useActiveRequestId } from './useActiveRequestId';
import { useRequests } from './useRequests';
import { atom, useAtomValue } from 'jotai';
import { jotaiStore } from '../routes/__root';
import { activeRequestIdAtom } from './useActiveRequestId';
import { grpcRequestsAtom } from './useGrpcRequests';
import { httpRequestsAtom } from './useHttpRequests';
interface TypeMap {
http_request: HttpRequest;
grpc_request: GrpcRequest;
}
export const activeRequestAtom = atom<HttpRequest | GrpcRequest | null>((get) => {
const activeRequestId = get(activeRequestIdAtom);
const requests = [...get(httpRequestsAtom), ...get(grpcRequestsAtom)];
return requests.find((r) => r.id === activeRequestId) ?? null;
});
export function getActiveRequest() {
return jotaiStore.get(activeRequestAtom);
}
export function useActiveRequest<T extends keyof TypeMap>(
model?: T | undefined,
): TypeMap[T] | null {
const requestId = useActiveRequestId();
const requests = useRequests();
for (const request of requests) {
const modelMatch = model == null ? true : request.model === model;
if (modelMatch && request.id === requestId) {
return request as TypeMap[T];
}
}
const activeRequest = useAtomValue(activeRequestAtom);
if (model == null) return activeRequest as TypeMap[T];
if (activeRequest?.model === model) return activeRequest as TypeMap[T];
return null;
}

View File

@@ -1,6 +1,17 @@
import { useParams } from 'react-router-dom';
import { useParams } from '@tanstack/react-router';
import { atom, useAtomValue } from 'jotai';
import { useEffect } from 'react';
import { jotaiStore } from '../routes/__root';
export const activeRequestIdAtom = atom<string>();
export function useActiveRequestId(): string | null {
const { requestId } = useParams();
return requestId ?? null;
return useAtomValue(activeRequestIdAtom) ?? null;
}
export function useSubscribeActiveRequestId() {
const { requestId } = useParams({ strict: false });
useEffect(() => {
jotaiStore.set(activeRequestIdAtom, requestId);
}, [requestId]);
}

View File

@@ -1,19 +1,25 @@
import { useParams } from '@tanstack/react-router';
import type { Workspace } from '@yaakapp-internal/models';
import { useMemo } from 'react';
import { useParams } from 'react-router-dom';
import { atom, useAtomValue } from 'jotai/index';
import { useEffect } from 'react';
import { jotaiStore } from '../routes/__root';
import { useWorkspaces } from './useWorkspaces';
export const activeWorkspaceIdAtom = atom<string>();
export function useActiveWorkspace(): Workspace | null {
const workspaceId = useActiveWorkspaceId();
const workspaces = useWorkspaces();
return useMemo(
() => workspaces.find((w) => w.id === workspaceId) ?? null,
[workspaces, workspaceId],
);
return workspaces.find((w) => w.id === workspaceId) ?? null;
}
function useActiveWorkspaceId(): string | null {
const { workspaceId } = useParams();
return workspaceId ?? null;
return useAtomValue(activeWorkspaceIdAtom) ?? null;
}
export function useSubscribeActiveWorkspaceId() {
const { workspaceId } = useParams({ strict: false });
useEffect(() => {
jotaiStore.set(activeWorkspaceIdAtom, workspaceId);
}, [workspaceId]);
}

View File

@@ -1,76 +0,0 @@
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { SettingsTab } from '../components/Settings/Settings';
import { QUERY_COOKIE_JAR_ID } from './useActiveCookieJar';
import { QUERY_ENVIRONMENT_ID } from './useActiveEnvironment';
export type RouteParamsWorkspace = {
workspaceId: string;
environmentId: string | null;
cookieJarId: string | null;
};
export type RouteParamsRequest = RouteParamsWorkspace & {
requestId: string;
};
export type RouteParamsSettings = {
workspaceId: string;
tab?: SettingsTab;
};
export const paths = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
workspaces(_ = {}) {
return '/workspaces';
},
workspaceSettings({ workspaceId, tab } = { workspaceId: ':workspaceId' } as RouteParamsSettings) {
return `/workspaces/${workspaceId}/settings?tab=${tab ?? SettingsTab.General}`;
},
workspace(
{ workspaceId, environmentId, cookieJarId } = {
workspaceId: ':workspaceId',
environmentId: ':environmentId',
cookieJarId: ':cookieJarId',
} as RouteParamsWorkspace,
) {
const path = `/workspaces/${workspaceId}`;
const params = new URLSearchParams();
if (environmentId != null) params.set(QUERY_ENVIRONMENT_ID, environmentId);
if (cookieJarId != null) params.set(QUERY_COOKIE_JAR_ID, cookieJarId);
return `${path}?${params}`;
},
request(
{ workspaceId, environmentId, requestId, cookieJarId } = {
workspaceId: ':workspaceId',
environmentId: ':environmentId',
requestId: ':requestId',
} as RouteParamsRequest,
) {
const path = `/workspaces/${workspaceId}/requests/${requestId}`;
const params = new URLSearchParams();
if (environmentId != null) params.set(QUERY_ENVIRONMENT_ID, environmentId);
if (cookieJarId != null) params.set(QUERY_COOKIE_JAR_ID, cookieJarId);
return `${path}?${params}`;
},
};
export function useAppRoutes() {
const nav = useNavigate();
const navigate = useCallback(
<T extends keyof typeof paths>(path: T, ...params: Parameters<(typeof paths)[T]>) => {
// Not sure how to make TS work here, but it's good from the
// outside caller perspective.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const resolvedPath = paths[path](...(params as any));
nav(resolvedPath);
},
[nav],
);
return {
paths,
navigate,
};
}

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import { event } from '@tauri-apps/api';
import { trackEvent } from '../lib/analytics';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import { InlineCode } from '../components/core/InlineCode';
import { minPromiseMillis } from '../lib/minPromiseMillis';
import { invokeCmd } from '../lib/tauri';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { HttpResponse } from '@yaakapp-internal/models';
import { useCopy } from './useCopy';
import { getResponseBodyText } from '../lib/responseBody';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { CookieJar } from '@yaakapp-internal/models';
import {useSetAtom} from "jotai";
import { trackEvent } from '../lib/analytics';

View File

@@ -15,9 +15,9 @@ export function useCreateDropdownItems({
hideIcons?: boolean;
folderId?: string | null;
} = {}): DropdownItem[] {
const createHttpRequest = useCreateHttpRequest();
const createGrpcRequest = useCreateGrpcRequest();
const createFolder = useCreateFolder();
const { mutate: createHttpRequest } = useCreateHttpRequest();
const { mutate: createGrpcRequest } = useCreateGrpcRequest();
const { mutate: createFolder } = useCreateFolder();
return useMemo<DropdownItem[]>(
() => [
@@ -25,14 +25,14 @@ export function useCreateDropdownItems({
key: 'create-http-request',
label: 'HTTP Request',
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: () => createHttpRequest.mutate({ folderId }),
onSelect: () => createHttpRequest({ folderId }),
},
{
key: 'create-graphql-request',
label: 'GraphQL Query',
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: () =>
createHttpRequest.mutate({
createHttpRequest({
folderId,
bodyType: BODY_TYPE_GRAPHQL,
method: 'POST',
@@ -43,7 +43,7 @@ export function useCreateDropdownItems({
key: 'create-grpc-request',
label: 'gRPC Call',
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: () => createGrpcRequest.mutate({ folderId }),
onSelect: () => createGrpcRequest({ folderId }),
},
...((hideFolder
? []
@@ -55,7 +55,7 @@ export function useCreateDropdownItems({
key: 'create-folder',
label: 'Folder',
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: () => createFolder.mutate({ folderId }),
onSelect: () => createFolder({ folderId }),
},
]) as DropdownItem[]),
],

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { Environment } from '@yaakapp-internal/models';
import {useSetAtom} from "jotai";
import { trackEvent } from '../lib/analytics';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { Folder } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai';
import { trackEvent } from '../lib/analytics';
@@ -20,6 +20,7 @@ export function useCreateFolder() {
>({
mutationKey: ['create_folder'],
mutationFn: async (patch) => {
console.log("FOLDER", workspace);
if (workspace === null) {
throw new Error("Cannot create folder when there's no active workspace");
}

View File

@@ -1,22 +1,17 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { GrpcRequest } from '@yaakapp-internal/models';
import {useSetAtom} from "jotai";
import { useSetAtom } from 'jotai';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import {useActiveCookieJar} from "./useActiveCookieJar";
import { useActiveEnvironment } from './useActiveEnvironment';
import { useActiveRequest } from './useActiveRequest';
import { router } from '../main';
import { Route } from '../routes/workspaces/$workspaceId/requests/$requestId';
import { getActiveRequest } from './useActiveRequest';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAppRoutes } from './useAppRoutes';
import {grpcRequestsAtom} from "./useGrpcRequests";
import {updateModelList} from "./useSyncModelStores";
import { grpcRequestsAtom } from './useGrpcRequests';
import { updateModelList } from './useSyncModelStores';
export function useCreateGrpcRequest() {
const workspace = useActiveWorkspace();
const [activeEnvironment] = useActiveEnvironment();
const [activeCookieJar] = useActiveCookieJar();
const activeRequest = useActiveRequest();
const routes = useAppRoutes();
const setGrpcRequests = useSetAtom(grpcRequestsAtom);
return useMutation<
@@ -29,6 +24,7 @@ export function useCreateGrpcRequest() {
if (workspace === null) {
throw new Error("Cannot create grpc request when there's no active workspace");
}
const activeRequest = getActiveRequest();
if (patch.sortPriority === undefined) {
if (activeRequest != null) {
// Place above currently active request
@@ -50,11 +46,13 @@ export function useCreateGrpcRequest() {
// Optimistic update
setGrpcRequests(updateModelList(request));
routes.navigate('request', {
workspaceId: request.workspaceId,
requestId: request.id,
environmentId: activeEnvironment?.id ?? null,
cookieJarId: activeCookieJar?.id ?? null,
router.navigate({
to: Route.fullPath,
params: {
workspaceId: request.workspaceId,
requestId: request.id,
},
search: (prev) => ({ ...prev }),
});
},
});

View File

@@ -1,28 +1,24 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { HttpRequest } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai/index';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { useActiveCookieJar } from './useActiveCookieJar';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useActiveRequest } from './useActiveRequest';
import { router } from '../main';
import { Route } from '../routes/workspaces/$workspaceId/requests/$requestId';
import { getActiveRequest } from './useActiveRequest';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAppRoutes } from './useAppRoutes';
import { httpRequestsAtom } from './useHttpRequests';
import { updateModelList } from './useSyncModelStores';
export function useCreateHttpRequest() {
const workspace = useActiveWorkspace();
const [activeEnvironment] = useActiveEnvironment();
const [activeCookieJar] = useActiveCookieJar();
const activeRequest = useActiveRequest();
const routes = useAppRoutes();
const activeWorkspace = useActiveWorkspace();
const setHttpRequests = useSetAtom(httpRequestsAtom);
return useMutation<HttpRequest, unknown, Partial<HttpRequest>>({
mutationKey: ['create_http_request'],
mutationFn: async (patch = {}) => {
if (workspace === null) {
const activeRequest = getActiveRequest();
if (activeWorkspace === null) {
throw new Error("Cannot create request when there's no active workspace");
}
if (patch.sortPriority === undefined) {
@@ -36,7 +32,7 @@ export function useCreateHttpRequest() {
}
patch.folderId = patch.folderId || activeRequest?.folderId;
return invokeCmd<HttpRequest>('cmd_create_http_request', {
request: { workspaceId: workspace.id, ...patch },
request: { workspaceId: activeWorkspace.id, ...patch },
});
},
onSettled: () => trackEvent('http_request', 'create'),
@@ -44,11 +40,10 @@ export function useCreateHttpRequest() {
// Optimistic update
setHttpRequests(updateModelList(request));
routes.navigate('request', {
workspaceId: request.workspaceId,
requestId: request.id,
environmentId: activeEnvironment?.id ?? null,
cookieJarId: activeCookieJar?.id ?? null,
await router.navigate({
to: Route.fullPath,
params: { workspaceId: request.workspaceId, requestId: request.id },
search: (prev) => ({ ...prev }),
});
},
});

View File

@@ -1,14 +1,14 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { Workspace } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai/index';
import { invokeCmd } from '../lib/tauri';
import { useAppRoutes } from './useAppRoutes';
import { router } from '../main';
import { Route } from '../routes/workspaces/$workspaceId';
import { usePrompt } from './usePrompt';
import { updateModelList } from './useSyncModelStores';
import { workspacesAtom } from './useWorkspaces';
export function useCreateWorkspace() {
const routes = useAppRoutes();
const prompt = usePrompt();
const setWorkspaces = useSetAtom(workspacesAtom);
@@ -34,10 +34,9 @@ export function useCreateWorkspace() {
// Optimistic update
setWorkspaces(updateModelList(workspace));
routes.navigate('workspace', {
workspaceId: workspace.id,
environmentId: null,
cookieJarId: null,
router.navigate({
to: Route.fullPath,
params: { workspaceId: workspace.id },
});
},
});

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { GrpcRequest } from '@yaakapp-internal/models';
import {useSetAtom} from "jotai";
import { InlineCode } from '../components/core/InlineCode';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { HttpRequest } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai';
import { InlineCode } from '../components/core/InlineCode';

View File

@@ -1,12 +1,12 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { CookieJar } from '@yaakapp-internal/models';
import {useSetAtom} from "jotai";
import { useSetAtom } from 'jotai';
import { InlineCode } from '../components/core/InlineCode';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { useConfirm } from './useConfirm';
import {cookieJarsAtom} from "./useCookieJars";
import {removeModelById} from "./useSyncModelStores";
import { cookieJarsAtom } from './useCookieJars';
import { removeModelById } from './useSyncModelStores';
export function useDeleteCookieJar(cookieJar: CookieJar | null) {
const confirm = useConfirm();
@@ -33,6 +33,6 @@ export function useDeleteCookieJar(cookieJar: CookieJar | null) {
if (cookieJar == null) return;
setCookieJars(removeModelById(cookieJar));
}
},
});
}

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { Environment } from '@yaakapp-internal/models';
import {useSetAtom} from "jotai";
import { InlineCode } from '../components/core/InlineCode';

View File

@@ -1,4 +1,3 @@
import { useMutation } from '@tanstack/react-query';
import type { Folder } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai';
import { InlineCode } from '../components/core/InlineCode';
@@ -8,6 +7,7 @@ import { invokeCmd } from '../lib/tauri';
import { useConfirm } from './useConfirm';
import { foldersAtom } from './useFolders';
import { removeModelById } from './useSyncModelStores';
import { useMutation } from './useMutation';
export function useDeleteFolder(id: string | null) {
const confirm = useConfirm();

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { GrpcConnection } from '@yaakapp-internal/models';
import {useSetAtom} from "jotai";
import { trackEvent } from '../lib/analytics';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import { useSetAtom } from 'jotai';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { HttpResponse } from '@yaakapp-internal/models';
import {useSetAtom} from "jotai";
import { trackEvent } from '../lib/analytics';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import { useSetAtom } from 'jotai';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';

View File

@@ -1,6 +1,6 @@
import { useMutation } from '@tanstack/react-query';
import { useDeleteAnyGrpcRequest } from './useDeleteAnyGrpcRequest';
import { useDeleteAnyHttpRequest } from './useDeleteAnyHttpRequest';
import { useMutation } from './useMutation';
export function useDeleteRequest(id: string | null) {
const deleteAnyHttpRequest = useDeleteAnyHttpRequest();

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import { useSetAtom } from 'jotai/index';
import { count } from '../lib/pluralize';
import { invokeCmd } from '../lib/tauri';

View File

@@ -1,18 +1,18 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { Workspace } from '@yaakapp-internal/models';
import {useSetAtom} from "jotai";
import { useSetAtom } from 'jotai';
import { InlineCode } from '../components/core/InlineCode';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { router } from '../main';
import { Route } from '../routes/workspaces';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAppRoutes } from './useAppRoutes';
import { useConfirm } from './useConfirm';
import {removeModelById} from "./useSyncModelStores";
import {workspacesAtom} from "./useWorkspaces";
import { removeModelById } from './useSyncModelStores';
import { workspacesAtom } from './useWorkspaces';
export function useDeleteWorkspace(workspace: Workspace | null) {
const activeWorkspace = useActiveWorkspace();
const routes = useAppRoutes();
const confirm = useConfirm();
const setWorkspaces = useSetAtom(workspacesAtom);
@@ -41,7 +41,7 @@ export function useDeleteWorkspace(workspace: Workspace | null) {
const { id: workspaceId } = workspace;
if (workspaceId === activeWorkspace?.id) {
routes.navigate('workspaces');
router.navigate({ to: Route.fullPath });
}
},
});

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';

View File

@@ -1,11 +1,9 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { GrpcRequest } from '@yaakapp-internal/models';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import {useActiveCookieJar} from "./useActiveCookieJar";
import { useActiveEnvironment } from './useActiveEnvironment';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAppRoutes } from './useAppRoutes';
import { router } from '../main';
import { Route } from '../routes/workspaces/$workspaceId/requests/$requestId';
import { getGrpcProtoFiles, setGrpcProtoFiles } from './useGrpcProtoFiles';
export function useDuplicateGrpcRequest({
@@ -15,11 +13,6 @@ export function useDuplicateGrpcRequest({
id: string | null;
navigateAfter: boolean;
}) {
const activeWorkspace = useActiveWorkspace();
const [activeEnvironment] = useActiveEnvironment();
const [activeCookieJar] = useActiveCookieJar();
const routes = useAppRoutes();
return useMutation<GrpcRequest, string>({
mutationKey: ['duplicate_grpc_request', id],
mutationFn: async () => {
@@ -34,12 +27,11 @@ export function useDuplicateGrpcRequest({
const protoFiles = await getGrpcProtoFiles(id);
await setGrpcProtoFiles(request.id, protoFiles);
if (navigateAfter && activeWorkspace !== null) {
routes.navigate('request', {
workspaceId: activeWorkspace.id,
requestId: request.id,
environmentId: activeEnvironment?.id ?? null,
cookieJarId: activeCookieJar?.id ?? null,
if (navigateAfter) {
await router.navigate({
to: Route.fullPath,
params: { workspaceId: request.workspaceId, requestId: request.id },
search: (prev) => ({ ...prev }),
});
}
},

View File

@@ -1,11 +1,9 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { HttpRequest } from '@yaakapp-internal/models';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import {useActiveCookieJar} from "./useActiveCookieJar";
import { useActiveEnvironment } from './useActiveEnvironment';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAppRoutes } from './useAppRoutes';
import { router } from '../main';
import { Route } from '../routes/workspaces/$workspaceId/requests/$requestId';
export function useDuplicateHttpRequest({
id,
@@ -14,10 +12,6 @@ export function useDuplicateHttpRequest({
id: string | null;
navigateAfter: boolean;
}) {
const activeWorkspace = useActiveWorkspace();
const [activeEnvironment] = useActiveEnvironment();
const [activeCookieJar] = useActiveCookieJar();
const routes = useAppRoutes();
return useMutation<HttpRequest, string>({
mutationKey: ['duplicate_http_request', id],
mutationFn: async () => {
@@ -26,12 +20,14 @@ export function useDuplicateHttpRequest({
},
onSettled: () => trackEvent('http_request', 'duplicate'),
onSuccess: async (request) => {
if (navigateAfter && activeWorkspace !== null) {
routes.navigate('request', {
workspaceId: activeWorkspace.id,
requestId: request.id,
environmentId: activeEnvironment?.id ?? null,
cookieJarId: activeCookieJar?.id ?? null,
if (navigateAfter) {
router.navigate({
to: Route.fullPath,
params: {
workspaceId: request.workspaceId,
requestId: request.id,
},
search: (prev) => ({ ...prev }),
});
}
},

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import { useDialog } from '../components/DialogContext';
import { ExportDataDialog } from '../components/ExportDataDialog';
import { useActiveWorkspace } from './useActiveWorkspace';

View File

@@ -7,6 +7,7 @@ import type {
} from '@yaakapp-internal/plugin';
import { invokeCmd } from '../lib/tauri';
import { usePluginsKey } from './usePlugins';
import { useMemo } from 'react';
export type CallableHttpRequestAction = Pick<HttpRequestAction, 'key' | 'label' | 'icon'> & {
call: (httpRequest: HttpRequest) => Promise<void>;
@@ -15,32 +16,35 @@ export type CallableHttpRequestAction = Pick<HttpRequestAction, 'key' | 'label'
export function useHttpRequestActions() {
const pluginsKey = usePluginsKey();
const httpRequestActions = useQuery({
const actionsResult = useQuery<CallableHttpRequestAction[]>({
queryKey: ['http_request_actions', pluginsKey],
queryFn: async () => {
const responses = (await invokeCmd(
const responses = await invokeCmd<GetHttpRequestActionsResponse[]>(
'cmd_http_request_actions',
)) as GetHttpRequestActionsResponse[];
return responses;
);
return responses.flatMap((r) =>
r.actions.map((a) => ({
key: a.key,
label: a.label,
icon: a.icon,
call: async (httpRequest: HttpRequest) => {
const payload: CallHttpRequestActionRequest = {
key: a.key,
pluginRefId: r.pluginRefId,
args: { httpRequest },
};
await invokeCmd('cmd_call_http_request_action', { req: payload });
},
})),
);
},
});
const actions: CallableHttpRequestAction[] =
httpRequestActions.data?.flatMap((r) =>
r.actions.map((a) => ({
key: a.key,
label: a.label,
icon: a.icon,
call: async (httpRequest: HttpRequest) => {
const payload: CallHttpRequestActionRequest = {
key: a.key,
pluginRefId: r.pluginRefId,
args: { httpRequest },
};
await invokeCmd('cmd_call_http_request_action', { req: payload });
},
})),
) ?? [];
const actions = useMemo(() => {
return actionsResult.data ?? [];
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(actionsResult.data)]);
return actions;
}

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { HttpRequest } from '@yaakapp-internal/models';
import { useToast } from '../components/ToastContext';
import { invokeCmd } from '../lib/tauri';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type {
Environment,
Folder,
@@ -13,12 +13,12 @@ import { useDialog } from '../components/DialogContext';
import { ImportDataDialog } from '../components/ImportDataDialog';
import { count } from '../lib/pluralize';
import { invokeCmd } from '../lib/tauri';
import { Route } from '../routes/workspaces/$workspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAlert } from './useAlert';
import { useAppRoutes } from './useAppRoutes';
import { router } from '../main';
export function useImportData() {
const routes = useAppRoutes();
const dialog = useDialog();
const alert = useAlert();
const activeWorkspace = useActiveWorkspace();
@@ -64,10 +64,11 @@ export function useImportData() {
});
if (importedWorkspace != null) {
routes.navigate('workspace', {
workspaceId: importedWorkspace.id,
environmentId: imported.environments[0]?.id ?? null,
cookieJarId: null,
const environmentId = imported.environments[0]?.id ?? null;
router.navigate({
to: Route.fullPath,
params: { workspaceId: importedWorkspace.id },
search: { environmentId },
});
}

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { HttpUrlParameter } from '@yaakapp-internal/models';
import { useToast } from '../components/ToastContext';
import { pluralize } from '../lib/pluralize';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';

View File

@@ -49,7 +49,7 @@ export function useKeyValue<T extends object | boolean | number | string | null>
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[fallback, key, namespace],
[typeof key === 'string' ? key : key.join('::'), namespace],
);
const reset = useCallback(async () => mutate.mutateAsync(fallback), [mutate, fallback]);

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import React from 'react';
import { useDialog } from '../components/DialogContext';
import { MoveToWorkspaceDialog } from '../components/MoveToWorkspaceDialog';

View File

@@ -0,0 +1,42 @@
import type { MutationKey } from '@tanstack/react-query';
import { useCallback } from 'react';
export function useMutation<TData = unknown, TError = unknown, TVariables = void>({
mutationKey,
mutationFn,
onSuccess,
onSettled,
}: {
mutationKey: MutationKey;
mutationFn: (vars: TVariables) => Promise<TData>;
onSettled?: () => void;
onSuccess?: (data: TData) => void;
}) {
const mutateAsync = useCallback(
async (variables: TVariables) => {
try {
const data = await mutationFn(variables);
onSuccess?.(data);
} catch (err: unknown) {
const e = err as TError;
console.log('MUTATION FAILED', mutationKey, e);
} finally {
onSettled?.();
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
mutationKey,
);
const mutate = useCallback(
(variables: TVariables) => {
setTimeout(() => mutateAsync(variables));
},
[mutateAsync],
);
return {
mutate,
mutateAsync,
};
}

View File

@@ -1,21 +1,27 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import { SettingsTab } from '../components/Settings/Settings';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { router } from '../main';
import { Route as SettingsRoute } from '../routes/workspaces/settings';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAppRoutes } from './useAppRoutes';
export function useOpenSettings(tab = SettingsTab.General) {
const routes = useAppRoutes();
const workspace = useActiveWorkspace();
return useMutation({
mutationKey: ['open_settings'],
mutationFn: async () => {
if (workspace == null) return;
trackEvent('dialog', 'show', { id: 'settings', tab: `${tab}` });
const location = router.buildLocation({
to: SettingsRoute.fullPath,
params: { workspaceId: workspace.id },
search: { tab },
});
await invokeCmd('cmd_new_child_window', {
url: routes.paths.workspaceSettings({ workspaceId: workspace.id, tab }),
url: location,
label: 'settings',
title: 'Yaak Settings',
innerSize: [600, 550],

View File

@@ -1,13 +1,13 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import { invokeCmd } from '../lib/tauri';
import { useAppRoutes } from './useAppRoutes';
import { router } from '../main';
import { Route as WorkspaceRoute } from '../routes/workspaces/$workspaceId';
import { Route as RequestRoute } from '../routes/workspaces/$workspaceId/requests/$requestId';
import { getRecentCookieJars } from './useRecentCookieJars';
import { getRecentEnvironments } from './useRecentEnvironments';
import { getRecentRequests } from './useRecentRequests';
export function useOpenWorkspace() {
const routes = useAppRoutes();
return useMutation({
mutationKey: ['open_workspace'],
mutationFn: async ({
@@ -17,22 +17,25 @@ export function useOpenWorkspace() {
workspaceId: string;
inNewWindow: boolean;
}) => {
const environmentId = (await getRecentEnvironments(workspaceId))[0] ?? null;
const requestId = (await getRecentRequests(workspaceId))[0] ?? null;
const cookieJarId = (await getRecentCookieJars(workspaceId))[0] ?? null;
const baseArgs = { workspaceId, environmentId, cookieJarId } as const;
const environmentId = (await getRecentEnvironments(workspaceId))[0] ?? undefined;
const requestId = (await getRecentRequests(workspaceId))[0] ?? undefined;
const cookieJarId = (await getRecentCookieJars(workspaceId))[0] ?? undefined;
const search = { environmentId, cookieJarId };
if (inNewWindow) {
const path =
requestId != null
? routes.paths.request({ ...baseArgs, requestId })
: routes.paths.workspace({ ...baseArgs });
await invokeCmd('cmd_new_main_window', { url: path });
const location = router.buildLocation({
to: WorkspaceRoute.fullPath,
params: { workspaceId },
search,
});
await invokeCmd('cmd_new_main_window', { url: location });
return;
}
if (requestId != null) {
router.navigate({ to: RequestRoute.fullPath, params: { workspaceId, requestId }, search });
} else {
if (requestId != null) {
routes.navigate('request', { ...baseArgs, requestId });
} else {
routes.navigate('workspace', { ...baseArgs });
}
router.navigate({ to: WorkspaceRoute.fullPath, params: { workspaceId }, search });
}
},
});

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { Plugin } from '@yaakapp-internal/models';
import { atom, useAtomValue, useSetAtom } from 'jotai';
import { minPromiseMillis } from '../lib/minPromiseMillis';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
import { InlineCode } from '../components/core/InlineCode';
import { usePrompt } from './usePrompt';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import { save } from '@tauri-apps/plugin-dialog';
import mime from 'mime';
import slugify from 'slugify';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { HttpResponse } from '@yaakapp-internal/models';
import { trackEvent } from '../lib/analytics';
import { getHttpRequest } from '../lib/store';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import { useSendAnyHttpRequest } from './useSendAnyHttpRequest';
export function useSendManyRequests() {

View File

@@ -1,15 +1,14 @@
import { emit } from '@tauri-apps/api/event';
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
import { useEffect } from 'react';
import { fallbackRequestName } from '../lib/fallbackRequestName';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useActiveRequest } from './useActiveRequest';
import { getActiveRequest } from './useActiveRequest';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAppInfo } from './useAppInfo';
import { useOsInfo } from './useOsInfo';
import { emit } from '@tauri-apps/api/event';
export function useSyncWorkspaceRequestTitle() {
const activeRequest = useActiveRequest();
const activeWorkspace = useActiveWorkspace();
const [activeEnvironment] = useActiveEnvironment();
const osInfo = useOsInfo();
@@ -24,6 +23,7 @@ export function useSyncWorkspaceRequestTitle() {
if (activeEnvironment) {
newTitle += ` [${activeEnvironment.name}]`;
}
const activeRequest = getActiveRequest();
if (activeRequest) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
newTitle += ` ${fallbackRequestName(activeRequest)}`;
@@ -40,5 +40,5 @@ export function useSyncWorkspaceRequestTitle() {
} else {
emit('yaak_title_changed', newTitle).catch(console.error);
}
}, [activeEnvironment, activeRequest, activeWorkspace, appInfo.isDev, osInfo.osType]);
}, [activeEnvironment, activeWorkspace, appInfo.isDev, osInfo.osType]);
}

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { Plugin } from '@yaakapp-internal/models';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { Folder } from '@yaakapp-internal/models';
import {useSetAtom} from "jotai/index";
import { getFolder } from '../lib/store';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { GrpcRequest } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai/index';
import { getGrpcRequest } from '../lib/store';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { HttpRequest } from '@yaakapp-internal/models';
import {useSetAtom} from "jotai/index";
import { getHttpRequest } from '../lib/store';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { CookieJar } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai/index';
import { getCookieJar } from '../lib/store';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { Environment } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai/index';
import { getEnvironment } from '../lib/store';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { Settings } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai';
import { getSettings } from '../lib/store';

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation } from './useMutation';
import type { Workspace } from '@yaakapp-internal/models';
import {useSetAtom} from "jotai/index";
import { getWorkspace } from '../lib/store';