Start on plugin ctx API (#64)

This commit is contained in:
Gregory Schier
2024-08-14 06:42:54 -07:00
committed by GitHub
parent e47a2c5fab
commit 12f4c2c668
106 changed files with 1086 additions and 1219 deletions

View File

@@ -1,36 +1,87 @@
import { useEffect, useMemo } from 'react';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useCallback, useEffect, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useCookieJars } from './useCookieJars';
import { useKeyValue } from './useKeyValue';
export const QUERY_COOKIE_JAR_ID = 'cookie_jar_id';
export function useActiveCookieJar() {
const workspaceId = useActiveWorkspaceId();
const [activeCookieJarId, setActiveCookieJarId] = useActiveCookieJarId();
const cookieJars = useCookieJars();
const activeCookieJar = useMemo(() => {
if (cookieJars.data == null) return undefined;
return cookieJars.data.find((cookieJar) => cookieJar.id === activeCookieJarId) ?? null;
}, [activeCookieJarId, cookieJars.data]);
return [activeCookieJar ?? null, setActiveCookieJarId] as const;
}
export function useEnsureActiveCookieJar() {
const cookieJars = useCookieJars();
const [activeCookieJarId, setActiveCookieJarId] = useActiveCookieJarId();
useEffect(() => {
if (cookieJars.data == null) return;
if (cookieJars.data.find((j) => j.id === activeCookieJarId)) {
return; // There's an active jar
}
const firstJar = cookieJars.data[0];
if (firstJar == null) {
console.log("Workspace doesn't have any cookie jars to activate");
return;
}
// There's no active jar, so set it to the first one
console.log('Setting active cookie jar to', firstJar.id);
setActiveCookieJarId(firstJar.id);
}, [activeCookieJarId, cookieJars, cookieJars.data, setActiveCookieJarId]);
}
export function useMigrateActiveCookieJarId() {
const workspace = useActiveWorkspace();
const [, setActiveCookieJarId] = useActiveCookieJarId();
const {
set: setActiveCookieJarId,
value: activeCookieJarId,
isLoading: isLoadingActiveCookieJarId,
set: setLegacyActiveCookieJarId,
value: legacyActiveCookieJarId,
isLoading: isLoadingLegacyActiveCookieJarId,
} = useKeyValue<string | null>({
namespace: 'global',
key: ['activeCookieJar', workspaceId ?? 'n/a'],
key: ['activeCookieJar', workspace?.id ?? 'n/a'],
fallback: null,
});
const activeCookieJar = useMemo(
() => cookieJars.find((cookieJar) => cookieJar.id === activeCookieJarId),
[activeCookieJarId, cookieJars],
useEffect(() => {
if (legacyActiveCookieJarId == null || isLoadingLegacyActiveCookieJarId) return;
console.log('Migrating active cookie jar ID to query param', legacyActiveCookieJarId);
setActiveCookieJarId(legacyActiveCookieJarId);
setLegacyActiveCookieJarId(null).catch(console.error);
}, [
workspace,
isLoadingLegacyActiveCookieJarId,
legacyActiveCookieJarId,
setActiveCookieJarId,
setLegacyActiveCookieJarId,
]);
}
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 setId = useCallback(
(id: string) => {
setParams((p) => {
const existing = Object.fromEntries(p);
return { ...existing, [QUERY_COOKIE_JAR_ID]: id };
});
},
[setParams],
);
// TODO: Make this not be called so many times (move to GlobalHooks?)
useEffect(() => {
if (!isLoadingActiveCookieJarId && activeCookieJar == null && cookieJars.length > 0) {
setActiveCookieJarId(cookieJars[0]?.id ?? null).catch(console.error);
}
}, [activeCookieJar, cookieJars, isLoadingActiveCookieJarId, setActiveCookieJarId]);
return {
activeCookieJar: activeCookieJar ?? null,
setActiveCookieJarId: setActiveCookieJarId,
};
return [id, setId] as const;
}

View File

@@ -1,10 +1,38 @@
import { useMemo } from 'react';
import type { Environment } from '@yaakapp/api';
import { useActiveEnvironmentId } from './useActiveEnvironmentId';
import { useCallback, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useEnvironments } from './useEnvironments';
export function useActiveEnvironment(): Environment | null {
const id = useActiveEnvironmentId();
export function useActiveEnvironment() {
const [id, setId] = useActiveEnvironmentId();
const environments = useEnvironments();
return useMemo(() => environments.find((w) => w.id === id) ?? null, [environments, id]);
const environment = useMemo(
() => environments.find((w) => w.id === id) ?? null,
[environments, id],
);
return [environment, setId] as const;
}
export const QUERY_ENVIRONMENT_ID = 'environment_id';
export 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 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],
);
return [id, setId] as const;
}

View File

@@ -1,13 +0,0 @@
import { useSearchParams } from 'react-router-dom';
export const QUERY_ENVIRONMENT_ID = 'environment_id';
export function useActiveEnvironmentId(): string | null {
const [params] = useSearchParams();
const environmentId = params.get(QUERY_ENVIRONMENT_ID);
if (environmentId == null) {
return null;
}
return environmentId;
}

View File

@@ -1,6 +1,7 @@
import { useMemo } from 'react';
import type { Workspace } from '@yaakapp/api';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useParams } from 'react-router-dom';
import type { RouteParamsWorkspace } from './useAppRoutes';
import { useWorkspaces } from './useWorkspaces';
export function useActiveWorkspace(): Workspace | null {
@@ -11,3 +12,8 @@ export function useActiveWorkspace(): Workspace | null {
[workspaces, workspaceId],
);
}
function useActiveWorkspaceId(): string | null {
const { workspaceId } = useParams<RouteParamsWorkspace>();
return workspaceId ?? null;
}

View File

@@ -3,7 +3,7 @@ import { InlineCode } from '../components/core/InlineCode';
import { useToast } from '../components/ToastContext';
import { useActiveWorkspace } from './useActiveWorkspace';
export function useAtiveWorkspaceChangedToast() {
export function useActiveWorkspaceChangedToast() {
const toast = useToast();
const activeWorkspace = useActiveWorkspace();
const [id, setId] = useState<string | null>(activeWorkspace?.id ?? null);

View File

@@ -1,7 +0,0 @@
import { useParams } from 'react-router-dom';
import type { RouteParamsWorkspace } from './useAppRoutes';
export function useActiveWorkspaceId(): string | null {
const { workspaceId } = useParams<RouteParamsWorkspace>();
return workspaceId ?? null;
}

View File

@@ -1,13 +1,15 @@
import type { Environment } from '@yaakapp/api';
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import type { Environment } from '@yaakapp/api';
import { QUERY_ENVIRONMENT_ID } from './useActiveEnvironmentId';
import { QUERY_COOKIE_JAR_ID } from './useActiveCookieJar';
import { QUERY_ENVIRONMENT_ID } from './useActiveEnvironment';
import { useActiveRequestId } from './useActiveRequestId';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
export type RouteParamsWorkspace = {
workspaceId: string;
environmentId?: string;
cookieJarId?: string;
};
export type RouteParamsRequest = RouteParamsWorkspace & {
@@ -22,34 +24,35 @@ export const routePaths = {
return `/workspaces/${workspaceId}/settings`;
},
workspace(
{ workspaceId, environmentId } = {
{ workspaceId, environmentId, cookieJarId } = {
workspaceId: ':workspaceId',
environmentId: ':environmentId',
cookieJarId: ':cookieJarId',
} as RouteParamsWorkspace,
) {
let path = `/workspaces/${workspaceId}`;
if (environmentId != null) {
path += `?${QUERY_ENVIRONMENT_ID}=${encodeURIComponent(environmentId)}`;
}
return path;
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 } = {
{ workspaceId, environmentId, requestId, cookieJarId } = {
workspaceId: ':workspaceId',
environmentId: ':environmentId',
requestId: ':requestId',
} as RouteParamsRequest,
) {
let path = `/workspaces/${workspaceId}/requests/${requestId}`;
if (environmentId != null) {
path += `?${QUERY_ENVIRONMENT_ID}=${encodeURIComponent(environmentId)}`;
}
return path;
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 activeWorkspaceId = useActiveWorkspaceId();
const activeWorkspace = useActiveWorkspace();
const requestId = useActiveRequestId();
const nav = useNavigate();
@@ -66,22 +69,22 @@ export function useAppRoutes() {
const setEnvironment = useCallback(
(environment: Environment | null) => {
if (activeWorkspaceId == null) {
if (activeWorkspace == null) {
navigate('workspaces');
} else if (requestId == null) {
navigate('workspace', {
workspaceId: activeWorkspaceId,
workspaceId: activeWorkspace.id,
environmentId: environment == null ? undefined : environment.id,
});
} else {
navigate('request', {
workspaceId: activeWorkspaceId,
workspaceId: activeWorkspace.id,
environmentId: environment == null ? undefined : environment.id,
requestId,
});
}
},
[navigate, activeWorkspaceId, requestId],
[navigate, activeWorkspace, requestId],
);
return {

View File

@@ -1,22 +1,22 @@
import { useQuery } from '@tanstack/react-query';
import type { CookieJar } from '../lib/models';
import { invokeCmd } from '../lib/tauri';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
export function cookieJarsQueryKey({ workspaceId }: { workspaceId: string }) {
return ['cookie_jars', { workspaceId }];
}
export function useCookieJars() {
const workspaceId = useActiveWorkspaceId();
return (
useQuery({
enabled: workspaceId != null,
queryKey: cookieJarsQueryKey({ workspaceId: workspaceId ?? 'n/a' }),
queryFn: async () => {
if (workspaceId == null) return [];
return (await invokeCmd('cmd_list_cookie_jars', { workspaceId })) as CookieJar[];
},
}).data ?? []
);
const workspace = useActiveWorkspace();
return useQuery({
enabled: workspace != null,
queryKey: cookieJarsQueryKey({ workspaceId: workspace?.id ?? 'n/a' }),
queryFn: async () => {
if (workspace == null) return [];
return (await invokeCmd('cmd_list_cookie_jars', {
workspaceId: workspace.id,
})) as CookieJar[];
},
});
}

View File

@@ -1,15 +1,18 @@
import { useMutation } from '@tanstack/react-query';
import { invokeCmd } from '../lib/tauri';
import { useActiveEnvironmentId } from './useActiveEnvironmentId';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useCopy } from './useCopy';
export function useCopyAsCurl(requestId: string) {
const copy = useCopy();
const environmentId = useActiveEnvironmentId();
const [environment] = useActiveEnvironment();
return useMutation({
mutationKey: ['copy_as_curl', requestId],
mutationFn: async () => {
const cmd: string = await invokeCmd('cmd_request_to_curl', { requestId, environmentId });
const cmd: string = await invokeCmd('cmd_request_to_curl', {
requestId,
environmentId: environment?.id,
});
copy(cmd);
return cmd;
},

View File

@@ -1,20 +1,18 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import { trackEvent } from '../lib/analytics';
import type { CookieJar } from '../lib/models';
import { invokeCmd } from '../lib/tauri';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { cookieJarsQueryKey } from './useCookieJars';
import { useActiveWorkspace } from './useActiveWorkspace';
import { usePrompt } from './usePrompt';
export function useCreateCookieJar() {
const workspaceId = useActiveWorkspaceId();
const queryClient = useQueryClient();
const workspace = useActiveWorkspace();
const prompt = usePrompt();
return useMutation<CookieJar>({
mutationKey: ['create_cookie_jar'],
mutationFn: async () => {
if (workspaceId === null) {
if (workspace === null) {
throw new Error("Cannot create cookie jar when there's no active workspace");
}
const name = await prompt({
@@ -25,14 +23,8 @@ export function useCreateCookieJar() {
label: 'Name',
defaultValue: 'My Jar',
});
return invokeCmd('cmd_create_cookie_jar', { workspaceId, name });
return invokeCmd('cmd_create_cookie_jar', { workspaceId: workspace.id, name });
},
onSettled: () => trackEvent('cookie_jar', 'create'),
onSuccess: async (cookieJar) => {
queryClient.setQueryData<CookieJar[]>(
cookieJarsQueryKey({ workspaceId: cookieJar.workspaceId }),
(items) => [...(items ?? []), cookieJar],
);
},
});
}

View File

@@ -1,17 +1,15 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { trackEvent } from '../lib/analytics';
import { useMutation } from '@tanstack/react-query';
import type { Environment } from '@yaakapp/api';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useAppRoutes } from './useAppRoutes';
import { environmentsQueryKey } from './useEnvironments';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useActiveWorkspace } from './useActiveWorkspace';
import { usePrompt } from './usePrompt';
export function useCreateEnvironment() {
const routes = useAppRoutes();
const [, setActiveEnvironmentId] = useActiveEnvironment();
const prompt = usePrompt();
const workspaceId = useActiveWorkspaceId();
const queryClient = useQueryClient();
const workspace = useActiveWorkspace();
return useMutation<Environment, unknown, void>({
mutationKey: ['create_environment'],
@@ -25,16 +23,16 @@ export function useCreateEnvironment() {
placeholder: 'My Environment',
defaultValue: 'My Environment',
});
return invokeCmd('cmd_create_environment', { name, variables: [], workspaceId });
return invokeCmd('cmd_create_environment', {
name,
variables: [],
workspaceId: workspace?.id,
});
},
onSettled: () => trackEvent('environment', 'create'),
onSuccess: async (environment) => {
if (workspaceId == null) return;
routes.setEnvironment(environment);
queryClient.setQueryData<Environment[]>(
environmentsQueryKey({ workspaceId }),
(environments) => [...(environments ?? []), environment],
);
if (workspace == null) return;
setActiveEnvironmentId(environment.id);
},
});
}

View File

@@ -1,22 +1,20 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { trackEvent } from '../lib/analytics';
import { useMutation } from '@tanstack/react-query';
import type { Folder } from '@yaakapp/api';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { useActiveRequest } from './useActiveRequest';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { foldersQueryKey } from './useFolders';
import { useActiveWorkspace } from './useActiveWorkspace';
import { usePrompt } from './usePrompt';
export function useCreateFolder() {
const workspaceId = useActiveWorkspaceId();
const workspace = useActiveWorkspace();
const activeRequest = useActiveRequest();
const queryClient = useQueryClient();
const prompt = usePrompt();
return useMutation<Folder, unknown, Partial<Pick<Folder, 'name' | 'sortPriority' | 'folderId'>>>({
mutationKey: ['create_folder'],
mutationFn: async (patch) => {
if (workspaceId === null) {
if (workspace === null) {
throw new Error("Cannot create folder when there's no active workspace");
}
patch.name =
@@ -32,13 +30,8 @@ export function useCreateFolder() {
}));
patch.sortPriority = patch.sortPriority || -Date.now();
patch.folderId = patch.folderId || activeRequest?.folderId;
return invokeCmd('cmd_create_folder', { workspaceId, ...patch });
return invokeCmd('cmd_create_folder', { workspaceId: workspace.id, ...patch });
},
onSettled: () => trackEvent('folder', 'create'),
onSuccess: async (request) => {
await queryClient.invalidateQueries({
queryKey: foldersQueryKey({ workspaceId: request.workspaceId }),
});
},
});
}

View File

@@ -1,15 +1,15 @@
import { useMutation } from '@tanstack/react-query';
import { trackEvent } from '../lib/analytics';
import type { GrpcRequest } from '@yaakapp/api';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { useActiveEnvironmentId } from './useActiveEnvironmentId';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useActiveRequest } from './useActiveRequest';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAppRoutes } from './useAppRoutes';
export function useCreateGrpcRequest() {
const workspaceId = useActiveWorkspaceId();
const activeEnvironmentId = useActiveEnvironmentId();
const workspace = useActiveWorkspace();
const [activeEnvironment] = useActiveEnvironment();
const activeRequest = useActiveRequest();
const routes = useAppRoutes();
@@ -20,12 +20,12 @@ export function useCreateGrpcRequest() {
>({
mutationKey: ['create_grpc_request'],
mutationFn: (patch) => {
if (workspaceId === null) {
if (workspace === null) {
throw new Error("Cannot create grpc request when there's no active workspace");
}
if (patch.sortPriority === undefined) {
if (activeRequest != null) {
// Place above currently-active request
// Place above currently active request
patch.sortPriority = activeRequest.sortPriority + 0.0001;
} else {
// Place at the very top
@@ -33,14 +33,18 @@ export function useCreateGrpcRequest() {
}
}
patch.folderId = patch.folderId || activeRequest?.folderId;
return invokeCmd('cmd_create_grpc_request', { workspaceId, name: '', ...patch });
return invokeCmd('cmd_create_grpc_request', {
workspaceId: workspace.id,
name: '',
...patch,
});
},
onSettled: () => trackEvent('grpc_request', 'create'),
onSuccess: async (request) => {
routes.navigate('request', {
workspaceId: request.workspaceId,
requestId: request.id,
environmentId: activeEnvironmentId ?? undefined,
environmentId: activeEnvironment?.id,
});
},
});

View File

@@ -1,22 +1,22 @@
import { useMutation } from '@tanstack/react-query';
import { trackEvent } from '../lib/analytics';
import type { HttpRequest } from '@yaakapp/api';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { useActiveEnvironmentId } from './useActiveEnvironmentId';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useActiveRequest } from './useActiveRequest';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAppRoutes } from './useAppRoutes';
export function useCreateHttpRequest() {
const workspaceId = useActiveWorkspaceId();
const activeEnvironmentId = useActiveEnvironmentId();
const workspace = useActiveWorkspace();
const [activeEnvironment] = useActiveEnvironment();
const activeRequest = useActiveRequest();
const routes = useAppRoutes();
return useMutation<HttpRequest, unknown, Partial<HttpRequest>>({
mutationKey: ['create_http_request'],
mutationFn: (patch = {}) => {
if (workspaceId === null) {
if (workspace === null) {
throw new Error("Cannot create request when there's no active workspace");
}
if (patch.sortPriority === undefined) {
@@ -29,14 +29,16 @@ export function useCreateHttpRequest() {
}
}
patch.folderId = patch.folderId || activeRequest?.folderId;
return invokeCmd('cmd_create_http_request', { request: { workspaceId, ...patch } });
return invokeCmd('cmd_create_http_request', {
request: { workspaceId: workspace.id, ...patch },
});
},
onSettled: () => trackEvent('http_request', 'create'),
onSuccess: async (request) => {
routes.navigate('request', {
workspaceId: request.workspaceId,
requestId: request.id,
environmentId: activeEnvironmentId ?? undefined,
environmentId: activeEnvironment?.id,
});
},
});

View File

@@ -1,15 +1,13 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { GrpcRequest } from '@yaakapp/api';
import { InlineCode } from '../components/core/InlineCode';
import { trackEvent } from '../lib/analytics';
import { fallbackRequestName } from '../lib/fallbackRequestName';
import type { GrpcRequest } from '@yaakapp/api';
import { getGrpcRequest } from '../lib/store';
import { invokeCmd } from '../lib/tauri';
import { useConfirm } from './useConfirm';
import { grpcRequestsQueryKey } from './useGrpcRequests';
export function useDeleteAnyGrpcRequest() {
const queryClient = useQueryClient();
const confirm = useConfirm();
return useMutation<GrpcRequest | null, string, string>({
@@ -32,13 +30,5 @@ export function useDeleteAnyGrpcRequest() {
return invokeCmd('cmd_delete_grpc_request', { requestId: id });
},
onSettled: () => trackEvent('grpc_request', 'delete'),
onSuccess: async (request) => {
if (request === null) return;
const { workspaceId, id: requestId } = request;
queryClient.setQueryData<GrpcRequest[]>(grpcRequestsQueryKey({ workspaceId }), (requests) =>
(requests ?? []).filter((r) => r.id !== requestId),
);
},
});
}

View File

@@ -1,16 +1,13 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { HttpRequest } from '@yaakapp/api';
import { InlineCode } from '../components/core/InlineCode';
import { trackEvent } from '../lib/analytics';
import { fallbackRequestName } from '../lib/fallbackRequestName';
import type { HttpRequest } from '@yaakapp/api';
import { getHttpRequest } from '../lib/store';
import { invokeCmd } from '../lib/tauri';
import { useConfirm } from './useConfirm';
import { httpRequestsQueryKey } from './useHttpRequests';
import { httpResponsesQueryKey } from './useHttpResponses';
export function useDeleteAnyHttpRequest() {
const queryClient = useQueryClient();
const confirm = useConfirm();
return useMutation<HttpRequest | null, string, string>({
@@ -33,15 +30,5 @@ export function useDeleteAnyHttpRequest() {
return invokeCmd('cmd_delete_http_request', { requestId: id });
},
onSettled: () => trackEvent('http_request', 'delete'),
onSuccess: async (request) => {
// Was it cancelled?
if (request === null) return;
const { workspaceId, id: requestId } = request;
queryClient.setQueryData(httpResponsesQueryKey({ requestId }), []); // Responses were deleted
queryClient.setQueryData<HttpRequest[]>(httpRequestsQueryKey({ workspaceId }), (requests) =>
(requests ?? []).filter((r) => r.id !== requestId),
);
},
});
}

View File

@@ -1,13 +1,11 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import { InlineCode } from '../components/core/InlineCode';
import { trackEvent } from '../lib/analytics';
import type { CookieJar } from '../lib/models';
import { invokeCmd } from '../lib/tauri';
import { useConfirm } from './useConfirm';
import { cookieJarsQueryKey } from './useCookieJars';
export function useDeleteCookieJar(cookieJar: CookieJar | null) {
const queryClient = useQueryClient();
const confirm = useConfirm();
return useMutation<CookieJar | null, string>({
@@ -27,13 +25,5 @@ export function useDeleteCookieJar(cookieJar: CookieJar | null) {
return invokeCmd('cmd_delete_cookie_jar', { cookieJarId: cookieJar?.id });
},
onSettled: () => trackEvent('cookie_jar', 'delete'),
onSuccess: async (cookieJar) => {
if (cookieJar === null) return;
const { id: cookieJarId, workspaceId } = cookieJar;
queryClient.setQueryData<CookieJar[]>(cookieJarsQueryKey({ workspaceId }), (cookieJars) =>
cookieJars?.filter((e) => e.id !== cookieJarId),
);
},
});
}

View File

@@ -1,13 +1,11 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { Environment } from '@yaakapp/api';
import { InlineCode } from '../components/core/InlineCode';
import { trackEvent } from '../lib/analytics';
import type { Environment, Workspace } from '@yaakapp/api';
import { invokeCmd } from '../lib/tauri';
import { useConfirm } from './useConfirm';
import { environmentsQueryKey } from './useEnvironments';
export function useDeleteEnvironment(environment: Environment | null) {
const queryClient = useQueryClient();
const confirm = useConfirm();
return useMutation<Environment | null, string>({
@@ -27,13 +25,5 @@ export function useDeleteEnvironment(environment: Environment | null) {
return invokeCmd('cmd_delete_environment', { environmentId: environment?.id });
},
onSettled: () => trackEvent('environment', 'delete'),
onSuccess: async (environment) => {
if (environment === null) return;
const { id: environmentId, workspaceId } = environment;
queryClient.setQueryData<Workspace[]>(environmentsQueryKey({ workspaceId }), (environments) =>
environments?.filter((e) => e.id !== environmentId),
);
},
});
}

View File

@@ -1,15 +1,12 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { Folder } from '@yaakapp/api';
import { InlineCode } from '../components/core/InlineCode';
import { trackEvent } from '../lib/analytics';
import type { Folder } from '@yaakapp/api';
import { getFolder } from '../lib/store';
import { invokeCmd } from '../lib/tauri';
import { useConfirm } from './useConfirm';
import { foldersQueryKey } from './useFolders';
import { httpRequestsQueryKey } from './useHttpRequests';
export function useDeleteFolder(id: string | null) {
const queryClient = useQueryClient();
const confirm = useConfirm();
return useMutation<Folder | null, string>({
@@ -30,15 +27,5 @@ export function useDeleteFolder(id: string | null) {
return invokeCmd('cmd_delete_folder', { folderId: id });
},
onSettled: () => trackEvent('folder', 'delete'),
onSuccess: async (folder) => {
// Was it cancelled?
if (folder === null) return;
const { workspaceId } = folder;
// Nesting makes it hard to clean things up, so just clear everything that could have been deleted
await queryClient.invalidateQueries({ queryKey: httpRequestsQueryKey({ workspaceId }) });
await queryClient.invalidateQueries({ queryKey: foldersQueryKey({ workspaceId }) });
},
});
}

View File

@@ -1,22 +1,14 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { trackEvent } from '../lib/analytics';
import { useMutation } from '@tanstack/react-query';
import type { GrpcConnection } from '@yaakapp/api';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { grpcConnectionsQueryKey } from './useGrpcConnections';
export function useDeleteGrpcConnection(id: string | null) {
const queryClient = useQueryClient();
return useMutation<GrpcConnection>({
mutationKey: ['delete_grpc_connection', id],
mutationFn: async () => {
return await invokeCmd('cmd_delete_grpc_connection', { id: id });
},
onSettled: () => trackEvent('grpc_connection', 'delete'),
onSuccess: ({ requestId, id: connectionId }) => {
queryClient.setQueryData<GrpcConnection[]>(
grpcConnectionsQueryKey({ requestId }),
(connections) => (connections ?? []).filter((c) => c.id !== connectionId),
);
},
});
}

View File

@@ -1,10 +1,8 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { grpcConnectionsQueryKey } from './useGrpcConnections';
export function useDeleteGrpcConnections(requestId?: string) {
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['delete_grpc_connections', requestId],
mutationFn: async () => {
@@ -12,9 +10,5 @@ export function useDeleteGrpcConnections(requestId?: string) {
await invokeCmd('cmd_delete_all_grpc_connections', { requestId });
},
onSettled: () => trackEvent('grpc_connection', 'delete_many'),
onSuccess: async () => {
if (requestId === undefined) return;
queryClient.setQueryData(grpcConnectionsQueryKey({ requestId }), []);
},
});
}

View File

@@ -1,21 +1,14 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { trackEvent } from '../lib/analytics';
import { useMutation } from '@tanstack/react-query';
import type { HttpResponse } from '@yaakapp/api';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { httpResponsesQueryKey } from './useHttpResponses';
export function useDeleteHttpResponse(id: string | null) {
const queryClient = useQueryClient();
return useMutation<HttpResponse>({
mutationKey: ['delete_http_response', id],
mutationFn: async () => {
return await invokeCmd('cmd_delete_http_response', { id: id });
},
onSettled: () => trackEvent('http_response', 'delete'),
onSuccess: ({ requestId, id: responseId }) => {
queryClient.setQueryData<HttpResponse[]>(httpResponsesQueryKey({ requestId }), (responses) =>
(responses ?? []).filter((response) => response.id !== responseId),
);
},
});
}

View File

@@ -1,10 +1,8 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { httpResponsesQueryKey } from './useHttpResponses';
export function useDeleteHttpResponses(requestId?: string) {
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['delete_http_responses', requestId],
mutationFn: async () => {
@@ -12,9 +10,5 @@ export function useDeleteHttpResponses(requestId?: string) {
await invokeCmd('cmd_delete_all_http_responses', { requestId });
},
onSettled: () => trackEvent('http_response', 'delete_many'),
onSuccess: async () => {
if (requestId === undefined) return;
queryClient.setQueryData(httpResponsesQueryKey({ requestId }), []);
},
});
}

View File

@@ -1,17 +1,14 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { Workspace } from '@yaakapp/api';
import { InlineCode } from '../components/core/InlineCode';
import { trackEvent } from '../lib/analytics';
import type { Workspace } from '@yaakapp/api';
import { invokeCmd } from '../lib/tauri';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAppRoutes } from './useAppRoutes';
import { useConfirm } from './useConfirm';
import { httpRequestsQueryKey } from './useHttpRequests';
import { workspacesQueryKey } from './useWorkspaces';
export function useDeleteWorkspace(workspace: Workspace | null) {
const queryClient = useQueryClient();
const activeWorkspaceId = useActiveWorkspaceId();
const activeWorkspace = useActiveWorkspace();
const routes = useAppRoutes();
const confirm = useConfirm();
@@ -36,16 +33,9 @@ export function useDeleteWorkspace(workspace: Workspace | null) {
if (workspace === null) return;
const { id: workspaceId } = workspace;
queryClient.setQueryData<Workspace[]>(workspacesQueryKey({}), (workspaces) =>
workspaces?.filter((workspace) => workspace.id !== workspaceId),
);
if (workspaceId === activeWorkspaceId) {
if (workspaceId === activeWorkspace?.id) {
routes.navigate('workspaces');
}
// Also clean up other things that may have been deleted
queryClient.setQueryData(httpRequestsQueryKey({ workspaceId }), []);
await queryClient.invalidateQueries({ queryKey: httpRequestsQueryKey({ workspaceId }) });
},
});
}

View File

@@ -1,10 +1,10 @@
import { useMutation } from '@tanstack/react-query';
import type { GrpcRequest } from '@yaakapp/api';
import { trackEvent } from '../lib/analytics';
import { setKeyValue } from '../lib/keyValueStore';
import type { GrpcRequest } from '@yaakapp/api';
import { invokeCmd } from '../lib/tauri';
import { useActiveEnvironmentId } from './useActiveEnvironmentId';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAppRoutes } from './useAppRoutes';
import { protoFilesArgs, useGrpcProtoFiles } from './useGrpcProtoFiles';
@@ -15,8 +15,8 @@ export function useDuplicateGrpcRequest({
id: string | null;
navigateAfter: boolean;
}) {
const activeWorkspaceId = useActiveWorkspaceId();
const activeEnvironmentId = useActiveEnvironmentId();
const activeWorkspace = useActiveWorkspace();
const [activeEnvironment] = useActiveEnvironment();
const routes = useAppRoutes();
const protoFiles = useGrpcProtoFiles(id);
return useMutation<GrpcRequest, string>({
@@ -30,11 +30,11 @@ export function useDuplicateGrpcRequest({
// Also copy proto files to new request
await setKeyValue({ ...protoFilesArgs(request.id), value: protoFiles.value ?? [] });
if (navigateAfter && activeWorkspaceId !== null) {
if (navigateAfter && activeWorkspace !== null) {
routes.navigate('request', {
workspaceId: activeWorkspaceId,
workspaceId: activeWorkspace.id,
requestId: request.id,
environmentId: activeEnvironmentId ?? undefined,
environmentId: activeEnvironment?.id,
});
}
},

View File

@@ -1,9 +1,9 @@
import { useMutation } from '@tanstack/react-query';
import { trackEvent } from '../lib/analytics';
import type { HttpRequest } from '@yaakapp/api';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { useActiveEnvironmentId } from './useActiveEnvironmentId';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAppRoutes } from './useAppRoutes';
export function useDuplicateHttpRequest({
@@ -13,8 +13,8 @@ export function useDuplicateHttpRequest({
id: string | null;
navigateAfter: boolean;
}) {
const activeWorkspaceId = useActiveWorkspaceId();
const activeEnvironmentId = useActiveEnvironmentId();
const activeWorkspace = useActiveWorkspace();
const [activeEnvironment] = useActiveEnvironment();
const routes = useAppRoutes();
return useMutation<HttpRequest, string>({
mutationKey: ['duplicate_http_request', id],
@@ -24,11 +24,11 @@ export function useDuplicateHttpRequest({
},
onSettled: () => trackEvent('http_request', 'duplicate'),
onSuccess: async (request) => {
if (navigateAfter && activeWorkspaceId !== null) {
if (navigateAfter && activeWorkspace !== null) {
routes.navigate('request', {
workspaceId: activeWorkspaceId,
workspaceId: activeWorkspace.id,
requestId: request.id,
environmentId: activeEnvironmentId ?? undefined,
environmentId: activeEnvironment?.id,
});
}
},

View File

@@ -1,21 +1,23 @@
import { useQuery } from '@tanstack/react-query';
import type { Environment } from '@yaakapp/api';
import { invokeCmd } from '../lib/tauri';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
export function environmentsQueryKey({ workspaceId }: { workspaceId: string }) {
return ['environments', { workspaceId }];
}
export function useEnvironments() {
const workspaceId = useActiveWorkspaceId();
const workspace = useActiveWorkspace();
return (
useQuery({
enabled: workspaceId != null,
queryKey: environmentsQueryKey({ workspaceId: workspaceId ?? 'n/a' }),
enabled: workspace != null,
queryKey: environmentsQueryKey({ workspaceId: workspace?.id ?? 'n/a' }),
queryFn: async () => {
if (workspaceId == null) return [];
return (await invokeCmd('cmd_list_environments', { workspaceId })) as Environment[];
if (workspace == null) return [];
return (await invokeCmd('cmd_list_environments', {
workspaceId: workspace.id,
})) as Environment[];
},
}).data ?? []
);

View File

@@ -1,11 +1,11 @@
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useKeyValue } from './useKeyValue';
export function useFloatingSidebarHidden() {
const activeWorkspaceId = useActiveWorkspaceId();
const activeWorkspace = useActiveWorkspace();
const { set, value } = useKeyValue<boolean>({
namespace: 'no_sync',
key: ['floating_sidebar_hidden', activeWorkspaceId ?? 'n/a'],
key: ['floating_sidebar_hidden', activeWorkspace?.id ?? 'n/a'],
fallback: false,
});

View File

@@ -1,21 +1,21 @@
import { useQuery } from '@tanstack/react-query';
import type { Folder } from '@yaakapp/api';
import { invokeCmd } from '../lib/tauri';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
export function foldersQueryKey({ workspaceId }: { workspaceId: string }) {
return ['folders', { workspaceId }];
}
export function useFolders() {
const workspaceId = useActiveWorkspaceId();
const workspace = useActiveWorkspace();
return (
useQuery({
enabled: workspaceId != null,
queryKey: foldersQueryKey({ workspaceId: workspaceId ?? 'n/a' }),
enabled: workspace != null,
queryKey: foldersQueryKey({ workspaceId: workspace?.id ?? 'n/a' }),
queryFn: async () => {
if (workspaceId == null) return [];
return (await invokeCmd('cmd_list_folders', { workspaceId })) as Folder[];
if (workspace == null) return [];
return (await invokeCmd('cmd_list_folders', { workspaceId: workspace.id })) as Folder[];
},
}).data ?? []
);

View File

@@ -1,10 +1,10 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import { emit } from '@tauri-apps/api/event';
import type { GrpcConnection, GrpcRequest } from '@yaakapp/api';
import { trackEvent } from '../lib/analytics';
import { minPromiseMillis } from '../lib/minPromiseMillis';
import type { GrpcConnection, GrpcRequest } from '@yaakapp/api';
import { invokeCmd } from '../lib/tauri';
import { useActiveEnvironmentId } from './useActiveEnvironmentId';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useDebouncedValue } from './useDebouncedValue';
export interface ReflectResponseService {
@@ -18,12 +18,12 @@ export function useGrpc(
protoFiles: string[],
) {
const requestId = req?.id ?? 'n/a';
const environmentId = useActiveEnvironmentId();
const [environment] = useActiveEnvironment();
const go = useMutation<void, string>({
mutationKey: ['grpc_go', conn?.id],
mutationFn: async () =>
await invokeCmd('cmd_grpc_go', { requestId, environmentId, protoFiles }),
await invokeCmd('cmd_grpc_go', { requestId, environmentId: environment?.id, protoFiles }),
onSettled: () => trackEvent('grpc_request', 'send'),
});

View File

@@ -1,21 +1,23 @@
import { useQuery } from '@tanstack/react-query';
import type { GrpcRequest } from '@yaakapp/api';
import { invokeCmd } from '../lib/tauri';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
export function grpcRequestsQueryKey({ workspaceId }: { workspaceId: string }) {
return ['grpc_requests', { workspaceId }];
}
export function useGrpcRequests() {
const workspaceId = useActiveWorkspaceId();
const workspace = useActiveWorkspace();
return (
useQuery({
enabled: workspaceId != null,
queryKey: grpcRequestsQueryKey({ workspaceId: workspaceId ?? 'n/a' }),
enabled: workspace != null,
queryKey: grpcRequestsQueryKey({ workspaceId: workspace?.id ?? 'n/a' }),
queryFn: async () => {
if (workspaceId == null) return [];
return (await invokeCmd('cmd_list_grpc_requests', { workspaceId })) as GrpcRequest[];
if (workspace == null) return [];
return (await invokeCmd('cmd_list_grpc_requests', {
workspaceId: workspace.id,
})) as GrpcRequest[];
},
}).data ?? []
);

View File

@@ -1,21 +1,23 @@
import { useQuery } from '@tanstack/react-query';
import type { HttpRequest } from '@yaakapp/api';
import { invokeCmd } from '../lib/tauri';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
export function httpRequestsQueryKey({ workspaceId }: { workspaceId: string }) {
return ['http_requests', { workspaceId }];
}
export function useHttpRequests() {
const workspaceId = useActiveWorkspaceId();
const workspace = useActiveWorkspace();
return (
useQuery({
enabled: workspaceId != null,
queryKey: httpRequestsQueryKey({ workspaceId: workspaceId ?? 'n/a' }),
enabled: workspace != null,
queryKey: httpRequestsQueryKey({ workspaceId: workspace?.id ?? 'n/a' }),
queryFn: async () => {
if (workspaceId == null) return [];
return (await invokeCmd('cmd_list_http_requests', { workspaceId })) as HttpRequest[];
if (workspace == null) return [];
return (await invokeCmd('cmd_list_http_requests', {
workspaceId: workspace.id,
})) as HttpRequest[];
},
}).data ?? []
);

View File

@@ -1,13 +1,13 @@
import { useMutation } from '@tanstack/react-query';
import { useToast } from '../components/ToastContext';
import { invokeCmd } from '../lib/tauri';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useCreateHttpRequest } from './useCreateHttpRequest';
import { useRequestUpdateKey } from './useRequestUpdateKey';
import { useUpdateAnyHttpRequest } from './useUpdateAnyHttpRequest';
export function useImportCurl() {
const workspaceId = useActiveWorkspaceId();
const workspace = useActiveWorkspace();
const updateRequest = useUpdateAnyHttpRequest();
const createRequest = useCreateHttpRequest();
const { wasUpdatedExternally } = useRequestUpdateKey(null);
@@ -24,7 +24,7 @@ export function useImportCurl() {
}) => {
const request: Record<string, unknown> = await invokeCmd('cmd_curl_to_request', {
command,
workspaceId,
workspaceId: workspace?.id,
});
delete request.id;

View File

@@ -1,13 +1,13 @@
import { useMutation } from '@tanstack/react-query';
import type { Environment, Folder, GrpcRequest, HttpRequest, Workspace } from '@yaakapp/api';
import { Button } from '../components/core/Button';
import { FormattedError } from '../components/core/FormattedError';
import { VStack } from '../components/core/Stacks';
import { useDialog } from '../components/DialogContext';
import { ImportDataDialog } from '../components/ImportDataDialog';
import type { Environment, Folder, GrpcRequest, HttpRequest, Workspace } from '@yaakapp/api';
import { count } from '../lib/pluralize';
import { invokeCmd } from '../lib/tauri';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAlert } from './useAlert';
import { useAppRoutes } from './useAppRoutes';
@@ -15,7 +15,7 @@ export function useImportData() {
const routes = useAppRoutes();
const dialog = useDialog();
const alert = useAlert();
const activeWorkspaceId = useActiveWorkspaceId();
const activeWorkspace = useActiveWorkspace();
const importData = async (filePath: string): Promise<boolean> => {
const imported: {
@@ -26,7 +26,7 @@ export function useImportData() {
grpcRequests: GrpcRequest[];
} = await invokeCmd('cmd_import_data', {
filePath,
workspaceId: activeWorkspaceId,
workspaceId: activeWorkspace?.id,
});
const importedWorkspace = imported.workspaces[0];

View File

@@ -1,11 +1,11 @@
import type { HttpRequest } from '@yaakapp/api';
import type { IntrospectionQuery } from 'graphql';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { buildClientSchema, getIntrospectionQuery } from '../components/core/Editor';
import { minPromiseMillis } from '../lib/minPromiseMillis';
import type { HttpRequest } from '@yaakapp/api';
import { getResponseBodyText } from '../lib/responseBody';
import { sendEphemeralRequest } from '../lib/sendEphemeralRequest';
import { useActiveEnvironmentId } from './useActiveEnvironmentId';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useDebouncedValue } from './useDebouncedValue';
import { useKeyValue } from './useKeyValue';
@@ -18,7 +18,7 @@ export function useIntrospectGraphQL(baseRequest: HttpRequest) {
// Debounce the request because it can change rapidly and we don't
// want to send so too many requests.
const request = useDebouncedValue(baseRequest);
const activeEnvironmentId = useActiveEnvironmentId();
const [activeEnvironment] = useActiveEnvironment();
const [refetchKey, setRefetchKey] = useState<number>(0);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [error, setError] = useState<string>();
@@ -40,7 +40,10 @@ export function useIntrospectGraphQL(baseRequest: HttpRequest) {
bodyType: 'application/json',
body: { text: introspectionRequestBody },
};
const response = await minPromiseMillis(sendEphemeralRequest(args, activeEnvironmentId), 700);
const response = await minPromiseMillis(
sendEphemeralRequest(args, activeEnvironment?.id ?? null),
700,
);
if (response.error) {
throw new Error(response.error);
@@ -72,7 +75,7 @@ export function useIntrospectGraphQL(baseRequest: HttpRequest) {
runIntrospection(); // Run immediately
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [request.id, request.url, request.method, refetchKey, activeEnvironmentId]);
}, [request.id, request.url, request.method, refetchKey, activeEnvironment?.id]);
const refetch = useCallback(() => {
setRefetchKey((k) => k + 1);

View File

@@ -1,4 +1,4 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useMutation, useQuery } from '@tanstack/react-query';
import { useCallback, useMemo } from 'react';
import { buildKeyValueKey, getKeyValue, setKeyValue } from '../lib/keyValueStore';
@@ -24,7 +24,6 @@ export function useKeyValue<T extends Object | null>({
key: string | string[];
fallback: T;
}) {
const queryClient = useQueryClient();
const query = useQuery<T>({
queryKey: keyValueQueryKey({ namespace, key }),
queryFn: async () => getKeyValue({ namespace, key, fallback }),
@@ -34,8 +33,6 @@ export function useKeyValue<T extends Object | null>({
const mutate = useMutation<void, unknown, T>({
mutationKey: ['set_key_value', namespace, key],
mutationFn: (value) => setKeyValue<T>({ namespace, key, value }),
// k/v should be as fast as possible, so optimistically update the cache
onMutate: (value) => queryClient.setQueryData<T>(keyValueQueryKey({ namespace, key }), value),
});
const set = useCallback(

View File

@@ -2,19 +2,19 @@ import { useMutation } from '@tanstack/react-query';
import React from 'react';
import { useDialog } from '../components/DialogContext';
import { MoveToWorkspaceDialog } from '../components/MoveToWorkspaceDialog';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useRequests } from './useRequests';
export function useMoveToWorkspace(id: string) {
const dialog = useDialog();
const requests = useRequests();
const request = requests.find((r) => r.id === id);
const activeWorkspaceId = useActiveWorkspaceId();
const activeWorkspace = useActiveWorkspace();
return useMutation<void, unknown>({
mutationKey: ['move_workspace', id],
mutationFn: async () => {
if (request == null || activeWorkspaceId == null) return;
if (request == null || activeWorkspace == null) return;
dialog.show({
id: 'change-workspace',
@@ -24,7 +24,7 @@ export function useMoveToWorkspace(id: string) {
<MoveToWorkspaceDialog
onDone={hide}
request={request}
activeWorkspaceId={activeWorkspaceId}
activeWorkspaceId={activeWorkspace.id}
/>
),
});

View File

@@ -1,6 +1,7 @@
import { useMutation } from '@tanstack/react-query';
import { invokeCmd } from '../lib/tauri';
import { useAppRoutes } from './useAppRoutes';
import { getRecentCookieJars } from './useRecentCookieJars';
import { getRecentEnvironments } from './useRecentEnvironments';
import { getRecentRequests } from './useRecentRequests';
@@ -16,29 +17,21 @@ export function useOpenWorkspace() {
workspaceId: string;
inNewWindow: boolean;
}) => {
const environmentId = (await getRecentEnvironments(workspaceId))[0];
const requestId = (await getRecentRequests(workspaceId))[0];
const cookieJarId = (await getRecentCookieJars(workspaceId))[0];
const baseArgs = { workspaceId, environmentId, cookieJarId } as const;
if (inNewWindow) {
const environmentId = (await getRecentEnvironments(workspaceId))[0];
const requestId = (await getRecentRequests(workspaceId))[0];
const path =
requestId != null
? routes.paths.request({
workspaceId,
environmentId,
requestId,
})
: routes.paths.workspace({ workspaceId, environmentId });
? routes.paths.request({ ...baseArgs, requestId })
: routes.paths.workspace({ ...baseArgs });
await invokeCmd('cmd_new_window', { url: path });
} else {
const environmentId = (await getRecentEnvironments(workspaceId))[0];
const requestId = (await getRecentRequests(workspaceId))[0];
if (requestId != null) {
routes.navigate('request', {
workspaceId: workspaceId,
environmentId,
requestId,
});
routes.navigate('request', { ...baseArgs, requestId });
} else {
routes.navigate('workspace', { workspaceId, environmentId });
routes.navigate('workspace', { ...baseArgs });
}
}
},

View File

@@ -1,10 +1,10 @@
import { useEffect, useState } from 'react';
import type { Appearance } from '../lib/theme/appearance';
import {
getCSSAppearance,
getWindowAppearance,
subscribeToWindowAppearanceChange,
} from '../lib/theme/appearance';
import { type Appearance } from '../lib/theme/window';
export function usePreferredAppearance() {
const [preferredAppearance, setPreferredAppearance] = useState<Appearance>(getCSSAppearance());

View File

@@ -0,0 +1,47 @@
import { useEffect, useMemo } from 'react';
import { getKeyValue } from '../lib/keyValueStore';
import { useActiveCookieJar } from './useActiveCookieJar';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useCookieJars } from './useCookieJars';
import { useKeyValue } from './useKeyValue';
const kvKey = (workspaceId: string) => 'recent_cookie_jars::' + workspaceId;
const namespace = 'global';
const fallback: string[] = [];
export function useRecentCookieJars() {
const cookieJars = useCookieJars();
const activeWorkspace = useActiveWorkspace();
const [activeCookieJar] = useActiveCookieJar();
const activeCookieJarId = activeCookieJar?.id ?? null;
const kv = useKeyValue<string[]>({
key: kvKey(activeWorkspace?.id ?? 'n/a'),
namespace,
fallback,
});
// Set history when active request changes
useEffect(() => {
kv.set((currentHistory: string[]) => {
if (activeCookieJarId === null) return currentHistory;
const withoutCurrent = currentHistory.filter((id) => id !== activeCookieJarId);
return [activeCookieJarId, ...withoutCurrent];
}).catch(console.error);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeCookieJarId]);
const onlyValidIds = useMemo(
() => kv.value?.filter((id) => cookieJars.data?.some((e) => e.id === id)) ?? [],
[kv.value, cookieJars],
);
return onlyValidIds;
}
export async function getRecentCookieJars(workspaceId: string) {
return getKeyValue<string[]>({
namespace,
key: kvKey(workspaceId),
fallback,
});
}

View File

@@ -1,7 +1,7 @@
import { useEffect, useMemo } from 'react';
import { getKeyValue } from '../lib/keyValueStore';
import { useActiveEnvironmentId } from './useActiveEnvironmentId';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useEnvironments } from './useEnvironments';
import { useKeyValue } from './useKeyValue';
@@ -11,10 +11,10 @@ const fallback: string[] = [];
export function useRecentEnvironments() {
const environments = useEnvironments();
const activeWorkspaceId = useActiveWorkspaceId();
const activeEnvironmentId = useActiveEnvironmentId();
const activeWorkspace = useActiveWorkspace();
const [activeEnvironment] = useActiveEnvironment();
const kv = useKeyValue<string[]>({
key: kvKey(activeWorkspaceId ?? 'n/a'),
key: kvKey(activeWorkspace?.id ?? 'n/a'),
namespace,
fallback,
});
@@ -22,12 +22,12 @@ export function useRecentEnvironments() {
// Set history when active request changes
useEffect(() => {
kv.set((currentHistory: string[]) => {
if (activeEnvironmentId === null) return currentHistory;
const withoutCurrentEnvironment = currentHistory.filter((id) => id !== activeEnvironmentId);
return [activeEnvironmentId, ...withoutCurrentEnvironment];
if (activeEnvironment === null) return currentHistory;
const withoutCurrentEnvironment = currentHistory.filter((id) => id !== activeEnvironment.id);
return [activeEnvironment.id, ...withoutCurrentEnvironment];
}).catch(console.error);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeEnvironmentId]);
}, [activeEnvironment?.id]);
const onlyValidIds = useMemo(
() => kv.value?.filter((id) => environments.some((e) => e.id === id)) ?? [],

View File

@@ -1,7 +1,7 @@
import { useEffect, useMemo } from 'react';
import { getKeyValue } from '../lib/keyValueStore';
import { useActiveRequestId } from './useActiveRequestId';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useKeyValue } from './useKeyValue';
import { useRequests } from './useRequests';
@@ -11,11 +11,11 @@ const fallback: string[] = [];
export function useRecentRequests() {
const requests = useRequests();
const activeWorkspaceId = useActiveWorkspaceId();
const activeWorkspace = useActiveWorkspace();
const activeRequestId = useActiveRequestId();
const kv = useKeyValue<string[]>({
key: kvKey(activeWorkspaceId ?? 'n/a'),
key: kvKey(activeWorkspace?.id ?? 'n/a'),
namespace,
fallback,
});

View File

@@ -1,6 +1,6 @@
import { useEffect, useMemo } from 'react';
import { getKeyValue } from '../lib/keyValueStore';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useKeyValue } from './useKeyValue';
import { useWorkspaces } from './useWorkspaces';
@@ -10,7 +10,7 @@ const fallback: string[] = [];
export function useRecentWorkspaces() {
const workspaces = useWorkspaces();
const activeWorkspaceId = useActiveWorkspaceId();
const activeWorkspace = useActiveWorkspace();
const kv = useKeyValue<string[]>({
key: kvKey(),
namespace,
@@ -20,12 +20,12 @@ export function useRecentWorkspaces() {
// Set history when active request changes
useEffect(() => {
kv.set((currentHistory: string[]) => {
if (activeWorkspaceId === null) return currentHistory;
const withoutCurrent = currentHistory.filter((id) => id !== activeWorkspaceId);
return [activeWorkspaceId, ...withoutCurrent];
if (activeWorkspace === null) return currentHistory;
const withoutCurrent = currentHistory.filter((id) => id !== activeWorkspace.id);
return [activeWorkspace.id, ...withoutCurrent];
}).catch(console.error);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeWorkspaceId]);
}, [activeWorkspace]);
const onlyValidIds = useMemo(
() => kv.value?.filter((id) => workspaces.some((w) => w.id === id)) ?? [],

View File

@@ -1,18 +1,16 @@
import { useMutation } from '@tanstack/react-query';
import { save } from '@tauri-apps/plugin-dialog';
import slugify from 'slugify';
import { trackEvent } from '../lib/analytics';
import type { HttpResponse } from '@yaakapp/api';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
import { useActiveCookieJar } from './useActiveCookieJar';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useAlert } from './useAlert';
import { useHttpRequests } from './useHttpRequests';
export function useSendAnyHttpRequest(options: { download?: boolean } = {}) {
const environment = useActiveEnvironment();
export function useSendAnyHttpRequest() {
const [environment] = useActiveEnvironment();
const alert = useAlert();
const { activeCookieJar } = useActiveCookieJar();
const [activeCookieJar] = useActiveCookieJar();
const requests = useHttpRequests();
return useMutation<HttpResponse | null, string, string | null>({
mutationKey: ['send_any_request'],
@@ -22,21 +20,9 @@ export function useSendAnyHttpRequest(options: { download?: boolean } = {}) {
return null;
}
let downloadDir: string | null = null;
if (options.download) {
downloadDir = await save({
title: 'Select Download Destination',
defaultPath: slugify(request.name, { lower: true, trim: true, strict: true }),
});
if (downloadDir == null) {
return null;
}
}
return invokeCmd('cmd_send_http_request', {
request,
environmentId: environment?.id,
downloadDir: downloadDir,
cookieJarId: activeCookieJar?.id,
});
},

View File

@@ -1,11 +1,11 @@
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useKeyValue } from './useKeyValue';
export function useSidebarHidden() {
const activeWorkspaceId = useActiveWorkspaceId();
const activeWorkspace = useActiveWorkspace();
const { set, value } = useKeyValue<boolean>({
namespace: 'no_sync',
key: ['sidebar_hidden', activeWorkspaceId ?? 'n/a'],
key: ['sidebar_hidden', activeWorkspace?.id ?? 'n/a'],
fallback: false,
});

View File

@@ -1,10 +1,13 @@
import { useCallback, useMemo } from 'react';
import { useLocalStorage } from 'react-use';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useActiveWorkspace } from './useActiveWorkspace';
export function useSidebarWidth() {
const activeWorkspaceId = useActiveWorkspaceId();
const [width, setWidth] = useLocalStorage<number>(`sidebar_width::${activeWorkspaceId}`, 250);
const activeWorkspace = useActiveWorkspace();
const [width, setWidth] = useLocalStorage<number>(
`sidebar_width::${activeWorkspace?.id ?? 'n/a'}`,
250,
);
const resetWidth = useCallback(() => setWidth(250), [setWidth]);
return useMemo(() => ({ width, setWidth, resetWidth }), [width, setWidth, resetWidth]);
}

View File

@@ -18,6 +18,6 @@ export function useSyncThemeToDocument() {
}
function emitBgChange(t: YaakTheme) {
if (t.background == null) return;
emit('yaak_bg_changed', t.background.hex()).catch(console.error);
if (t.surface == null) return;
emit('yaak_bg_changed', t.surface.hexNoAlpha()).catch(console.error);
}

View File

@@ -11,7 +11,7 @@ import { emit } from '@tauri-apps/api/event';
export function useSyncWorkspaceRequestTitle() {
const activeRequest = useActiveRequest();
const activeWorkspace = useActiveWorkspace();
const activeEnvironment = useActiveEnvironment();
const [activeEnvironment] = useActiveEnvironment();
const osInfo = useOsInfo();
const appInfo = useAppInfo();

View File

@@ -1,12 +1,9 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { Folder } from '@yaakapp/api';
import { getFolder } from '../lib/store';
import { invokeCmd } from '../lib/tauri';
import { foldersQueryKey } from './useFolders';
export function useUpdateAnyFolder() {
const queryClient = useQueryClient();
return useMutation<void, unknown, { id: string; update: (r: Folder) => Folder }>({
mutationKey: ['update_any_folder'],
mutationFn: async ({ id, update }) => {
@@ -17,12 +14,5 @@ export function useUpdateAnyFolder() {
await invokeCmd('cmd_update_folder', { folder: update(folder) });
},
onMutate: async ({ id, update }) => {
const folder = await getFolder(id);
if (folder === null) return;
queryClient.setQueryData<Folder[]>(foldersQueryKey(folder), (folders) =>
(folders ?? []).map((f) => (f.id === folder.id ? update(f) : f)),
);
},
});
}

View File

@@ -1,12 +1,9 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { GrpcRequest } from '@yaakapp/api';
import { getGrpcRequest } from '../lib/store';
import { invokeCmd } from '../lib/tauri';
import { grpcRequestsQueryKey } from './useGrpcRequests';
export function useUpdateAnyGrpcRequest() {
const queryClient = useQueryClient();
return useMutation<
void,
unknown,
@@ -23,14 +20,5 @@ export function useUpdateAnyGrpcRequest() {
typeof update === 'function' ? update(request) : { ...request, ...update };
await invokeCmd('cmd_update_grpc_request', { request: patchedRequest });
},
onMutate: async ({ id, update }) => {
const request = await getGrpcRequest(id);
if (request === null) return;
const patchedRequest =
typeof update === 'function' ? update(request) : { ...request, ...update };
queryClient.setQueryData<GrpcRequest[]>(grpcRequestsQueryKey(request), (requests) =>
(requests ?? []).map((r) => (r.id === patchedRequest.id ? patchedRequest : r)),
);
},
});
}

View File

@@ -1,12 +1,9 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { HttpRequest } from '@yaakapp/api';
import { getHttpRequest } from '../lib/store';
import { invokeCmd } from '../lib/tauri';
import { httpRequestsQueryKey } from './useHttpRequests';
export function useUpdateAnyHttpRequest() {
const queryClient = useQueryClient();
return useMutation<
void,
unknown,
@@ -23,14 +20,5 @@ export function useUpdateAnyHttpRequest() {
typeof update === 'function' ? update(request) : { ...request, ...update };
await invokeCmd('cmd_update_http_request', { request: patchedRequest });
},
onMutate: async ({ id, update }) => {
const request = await getHttpRequest(id);
if (request === null) return;
const patchedRequest =
typeof update === 'function' ? update(request) : { ...request, ...update };
queryClient.setQueryData<HttpRequest[]>(httpRequestsQueryKey(request), (requests) =>
(requests ?? []).map((r) => (r.id === patchedRequest.id ? patchedRequest : r)),
);
},
});
}

View File

@@ -1,11 +1,9 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { CookieJar } from '../lib/models';
import { getCookieJar } from '../lib/store';
import { invokeCmd } from '../lib/tauri';
import { cookieJarsQueryKey } from './useCookieJars';
export function useUpdateCookieJar(id: string | null) {
const queryClient = useQueryClient();
return useMutation<void, unknown, Partial<CookieJar> | ((j: CookieJar) => CookieJar)>({
mutationKey: ['update_cookie_jar', id],
mutationFn: async (v) => {
@@ -15,17 +13,7 @@ export function useUpdateCookieJar(id: string | null) {
}
const newCookieJar = typeof v === 'function' ? v(cookieJar) : { ...cookieJar, ...v };
console.log('NEW COOKIE JAR', newCookieJar.cookies.length);
await invokeCmd('cmd_update_cookie_jar', { cookieJar: newCookieJar });
},
onMutate: async (v) => {
const cookieJar = await getCookieJar(id);
if (cookieJar === null) return;
const newCookieJar = typeof v === 'function' ? v(cookieJar) : { ...cookieJar, ...v };
queryClient.setQueryData<CookieJar[]>(cookieJarsQueryKey(cookieJar), (cookieJars) =>
(cookieJars ?? []).map((j) => (j.id === newCookieJar.id ? newCookieJar : j)),
);
},
});
}

View File

@@ -1,11 +1,9 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { Environment } from '@yaakapp/api';
import { getEnvironment } from '../lib/store';
import { invokeCmd } from '../lib/tauri';
import { environmentsQueryKey } from './useEnvironments';
export function useUpdateEnvironment(id: string | null) {
const queryClient = useQueryClient();
return useMutation<void, unknown, Partial<Environment> | ((r: Environment) => Environment)>({
mutationKey: ['update_environment', id],
mutationFn: async (v) => {
@@ -17,14 +15,5 @@ export function useUpdateEnvironment(id: string | null) {
const newEnvironment = typeof v === 'function' ? v(environment) : { ...environment, ...v };
await invokeCmd('cmd_update_environment', { environment: newEnvironment });
},
onMutate: async (v) => {
const environment = await getEnvironment(id);
if (environment === null) return;
const newEnvironment = typeof v === 'function' ? v(environment) : { ...environment, ...v };
queryClient.setQueryData<Environment[]>(environmentsQueryKey(environment), (environments) =>
(environments ?? []).map((r) => (r.id === newEnvironment.id ? newEnvironment : r)),
);
},
});
}

View File

@@ -1,11 +1,9 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { Workspace } from '@yaakapp/api';
import { getWorkspace } from '../lib/store';
import { invokeCmd } from '../lib/tauri';
import { workspacesQueryKey } from './useWorkspaces';
export function useUpdateWorkspace(id: string | null) {
const queryClient = useQueryClient();
return useMutation<void, unknown, Partial<Workspace> | ((w: Workspace) => Workspace)>({
mutationKey: ['update_workspace', id],
mutationFn: async (v) => {
@@ -17,14 +15,5 @@ export function useUpdateWorkspace(id: string | null) {
const newWorkspace = typeof v === 'function' ? v(workspace) : { ...workspace, ...v };
await invokeCmd('cmd_update_workspace', { workspace: newWorkspace });
},
onMutate: async (v) => {
const workspace = await getWorkspace(id);
if (workspace === null) return;
const newWorkspace = typeof v === 'function' ? v(workspace) : { ...workspace, ...v };
queryClient.setQueryData<Workspace[]>(workspacesQueryKey(workspace), (workspaces) =>
(workspaces ?? []).map((w) => (w.id === newWorkspace.id ? newWorkspace : w)),
);
},
});
}