mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-05 00:37:04 +02:00
Generalized frontend model store (#193)
This commit is contained in:
@@ -1,12 +1,10 @@
|
||||
import { useSearch } from '@tanstack/react-router';
|
||||
import type { CookieJar } from '@yaakapp-internal/models';
|
||||
import { cookieJarsAtom } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai/index';
|
||||
import { useEffect } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
|
||||
import { cookieJarsAtom, useCookieJars } from './useCookieJars';
|
||||
|
||||
export const QUERY_COOKIE_JAR_ID = 'cookie_jar_id';
|
||||
|
||||
export const activeCookieJarAtom = atom<CookieJar | null>(null);
|
||||
|
||||
@@ -18,6 +16,7 @@ export function useSubscribeActiveCookieJarId() {
|
||||
const search = useSearch({ strict: false });
|
||||
const cookieJarId = search.cookie_jar_id;
|
||||
const cookieJars = useAtomValue(cookieJarsAtom);
|
||||
|
||||
useEffect(() => {
|
||||
if (search == null) return; // Happens during Vite hot reload
|
||||
const activeCookieJar = cookieJars?.find((j) => j.id == cookieJarId) ?? null;
|
||||
@@ -30,7 +29,7 @@ export function getActiveCookieJar() {
|
||||
}
|
||||
|
||||
export function useEnsureActiveCookieJar() {
|
||||
const cookieJars = useCookieJars();
|
||||
const cookieJars = useAtomValue(cookieJarsAtom);
|
||||
const { cookie_jar_id: activeCookieJarId } = useSearch({ from: '/workspaces/$workspaceId/' });
|
||||
|
||||
// Set the active cookie jar to the first one, if none set
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { useSearch } from '@tanstack/react-router';
|
||||
import type { Environment } from '@yaakapp-internal/models';
|
||||
import { environmentsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { atom } from 'jotai/index';
|
||||
import { useEffect } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { environmentsAtom } from './useEnvironments';
|
||||
|
||||
export const QUERY_ENVIRONMENT_ID = 'environment_id';
|
||||
|
||||
export const activeEnvironmentIdAtom = atom<string>();
|
||||
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
import type { EnvironmentVariable } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useMemo } from 'react';
|
||||
import { activeEnvironmentAtom } from './useActiveEnvironment';
|
||||
import { environmentsBreakdownAtom } from './useEnvironments';
|
||||
|
||||
const activeEnvironmentVariablesAtom = atom((get) => {
|
||||
const { baseEnvironment } = get(environmentsBreakdownAtom);
|
||||
const activeEnvironment = get(activeEnvironmentAtom);
|
||||
|
||||
const varMap: Record<string, EnvironmentVariable> = {};
|
||||
const allVariables = [
|
||||
...(baseEnvironment?.variables ?? []),
|
||||
...(activeEnvironment?.variables ?? []),
|
||||
];
|
||||
|
||||
for (const v of allVariables) {
|
||||
if (!v.enabled || !v.name) continue;
|
||||
varMap[v.name] = v;
|
||||
}
|
||||
|
||||
return Object.values(varMap);
|
||||
});
|
||||
import { useEnvironmentsBreakdown } from './useEnvironmentsBreakdown';
|
||||
|
||||
export function useActiveEnvironmentVariables() {
|
||||
return useAtomValue(activeEnvironmentVariablesAtom);
|
||||
const { baseEnvironment } = useEnvironmentsBreakdown();
|
||||
const activeEnvironment = useAtomValue(activeEnvironmentAtom);
|
||||
return useMemo(() => {
|
||||
const varMap: Record<string, EnvironmentVariable> = {};
|
||||
const allVariables = [
|
||||
...(baseEnvironment?.variables ?? []),
|
||||
...(activeEnvironment?.variables ?? []),
|
||||
];
|
||||
|
||||
for (const v of allVariables) {
|
||||
if (!v.enabled || !v.name) continue;
|
||||
varMap[v.name] = v;
|
||||
}
|
||||
|
||||
return Object.values(varMap);
|
||||
}, [activeEnvironment, baseEnvironment]);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { activeRequestIdAtom } from './useActiveRequestId';
|
||||
import { requestsAtom } from './useRequests';
|
||||
import { allRequestsAtom } from './useAllRequests';
|
||||
|
||||
export const activeRequestAtom = atom((get) => {
|
||||
const activeRequestId = get(activeRequestIdAtom);
|
||||
const requests = get(allRequestsAtom);
|
||||
return requests.find((r) => r.id === activeRequestId) ?? null;
|
||||
});
|
||||
|
||||
interface TypeMap {
|
||||
http_request: HttpRequest;
|
||||
@@ -10,16 +15,6 @@ interface TypeMap {
|
||||
websocket_request: WebsocketRequest;
|
||||
}
|
||||
|
||||
export const activeRequestAtom = atom((get) => {
|
||||
const activeRequestId = get(activeRequestIdAtom);
|
||||
const requests = get(requestsAtom);
|
||||
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 {
|
||||
|
||||
@@ -1,31 +1,24 @@
|
||||
import {useParams} from '@tanstack/react-router';
|
||||
import type {Workspace} from '@yaakapp-internal/models';
|
||||
import {atom, useAtomValue} from 'jotai/index';
|
||||
import {useEffect} from 'react';
|
||||
import {jotaiStore} from '../lib/jotai';
|
||||
import {workspacesAtom} from './useWorkspaces';
|
||||
import { useParams } from '@tanstack/react-router';
|
||||
import { workspaceMetasAtom, workspacesAtom } from '@yaakapp-internal/models';
|
||||
import { atom } from 'jotai/index';
|
||||
import { useEffect } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
|
||||
export const activeWorkspaceIdAtom = atom<string>();
|
||||
export const activeWorkspaceIdAtom = atom<string | null>(null);
|
||||
|
||||
export const activeWorkspaceAtom = atom<Workspace | null>((get) => {
|
||||
const activeWorkspaceId = get(activeWorkspaceIdAtom);
|
||||
const workspaces = get(workspacesAtom);
|
||||
return workspaces.find((w) => w.id === activeWorkspaceId) ?? null;
|
||||
export const activeWorkspaceAtom = atom((get) => {
|
||||
const activeWorkspaceId = get(activeWorkspaceIdAtom);
|
||||
const workspaces = get(workspacesAtom);
|
||||
return workspaces.find((w) => w.id === activeWorkspaceId) ?? null;
|
||||
});
|
||||
|
||||
export function useActiveWorkspace(): Workspace | null {
|
||||
return useAtomValue(activeWorkspaceAtom);
|
||||
}
|
||||
|
||||
export function getActiveWorkspaceId() {
|
||||
return jotaiStore.get(activeWorkspaceIdAtom) ?? null;
|
||||
}
|
||||
|
||||
export function getActiveWorkspace() {
|
||||
return jotaiStore.get(activeWorkspaceAtom) ?? null;
|
||||
}
|
||||
export const activeWorkspaceMetaAtom = atom((get) => {
|
||||
const activeWorkspaceId = get(activeWorkspaceIdAtom);
|
||||
const workspaceMetas = get(workspaceMetasAtom);
|
||||
return workspaceMetas.find((m) => m.workspaceId === activeWorkspaceId) ?? null;
|
||||
});
|
||||
|
||||
export function useSubscribeActiveWorkspaceId() {
|
||||
const {workspaceId} = useParams({strict: false});
|
||||
useEffect(() => jotaiStore.set(activeWorkspaceIdAtom, workspaceId), [workspaceId]);
|
||||
const { workspaceId } = useParams({ strict: false });
|
||||
useEffect(() => jotaiStore.set(activeWorkspaceIdAtom, workspaceId ?? null), [workspaceId]);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { showToast } from '../lib/toast';
|
||||
import { activeWorkspaceAtom } from './useActiveWorkspace';
|
||||
|
||||
export function useActiveWorkspaceChangedToast() {
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const activeWorkspace = useAtomValue(activeWorkspaceAtom);
|
||||
const [id, setId] = useState<string | null>(activeWorkspace?.id ?? null);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
14
src-web/hooks/useAllRequests.ts
Normal file
14
src-web/hooks/useAllRequests.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import {
|
||||
grpcRequestsAtom,
|
||||
httpRequestsAtom,
|
||||
websocketRequestsAtom,
|
||||
} from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai/index';
|
||||
|
||||
export const allRequestsAtom = atom(function (get) {
|
||||
return [...get(httpRequestsAtom), ...get(grpcRequestsAtom), ...get(websocketRequestsAtom)];
|
||||
});
|
||||
|
||||
export function useAllRequests() {
|
||||
return useAtomValue(allRequestsAtom);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import type { CookieJar } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
|
||||
export const cookieJarsAtom = atom<CookieJar[] | undefined>();
|
||||
|
||||
export const sortedCookieJars = atom((get) => {
|
||||
return get(cookieJarsAtom)?.sort((a, b) => a.name.localeCompare(b.name));
|
||||
});
|
||||
|
||||
export function useCookieJars() {
|
||||
return useAtomValue(sortedCookieJars);
|
||||
}
|
||||
|
||||
export function getCookieJar(id: string | null) {
|
||||
return jotaiStore.get(cookieJarsAtom)?.find((e) => e.id === id) ?? null;
|
||||
}
|
||||
@@ -1,17 +1,19 @@
|
||||
import type { CookieJar } from '@yaakapp-internal/models';
|
||||
import { createWorkspaceModel } from '@yaakapp-internal/models';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { showPrompt } from '../lib/prompt';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useCreateCookieJar() {
|
||||
return useFastMutation<CookieJar | null>({
|
||||
return useFastMutation({
|
||||
mutationKey: ['create_cookie_jar'],
|
||||
mutationFn: async () => {
|
||||
const workspaceId = getActiveWorkspaceId();
|
||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||
if (workspaceId == null) {
|
||||
throw new Error("Cannot create cookie jar when there's no active workspace");
|
||||
}
|
||||
|
||||
const name = await showPrompt({
|
||||
id: 'new-cookie-jar',
|
||||
title: 'New CookieJar',
|
||||
@@ -22,7 +24,10 @@ export function useCreateCookieJar() {
|
||||
});
|
||||
if (name == null) return null;
|
||||
|
||||
return invokeCmd('cmd_create_cookie_jar', { workspaceId, name });
|
||||
return createWorkspaceModel({ model: 'cookie_jar', workspaceId, name });
|
||||
},
|
||||
onSuccess: async (cookieJarId) => {
|
||||
setWorkspaceSearchParams({ cookie_jar_id: cookieJarId });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { createWorkspaceModel } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useMemo } from 'react';
|
||||
import { createFolder } from '../commands/commands';
|
||||
import { upsertWebsocketRequest } from '../commands/upsertWebsocketRequest';
|
||||
import type { DropdownItem } from '../components/core/Dropdown';
|
||||
import { Icon } from '../components/core/Icon';
|
||||
import { generateId } from '../lib/generateId';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { BODY_TYPE_GRAPHQL } from '../lib/model_util';
|
||||
import { getActiveRequest } from './useActiveRequest';
|
||||
import { getActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useCreateGrpcRequest } from './useCreateGrpcRequest';
|
||||
import { activeRequestAtom } from './useActiveRequest';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useCreateHttpRequest } from './useCreateHttpRequest';
|
||||
|
||||
export function useCreateDropdownItems({
|
||||
@@ -20,14 +21,13 @@ export function useCreateDropdownItems({
|
||||
folderId?: string | null | 'active-folder';
|
||||
} = {}): DropdownItem[] {
|
||||
const { mutate: createHttpRequest } = useCreateHttpRequest();
|
||||
const { mutate: createGrpcRequest } = useCreateGrpcRequest();
|
||||
const activeWorkspace = getActiveWorkspace();
|
||||
const workspaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
|
||||
const items = useMemo((): DropdownItem[] => {
|
||||
const activeRequest = getActiveRequest();
|
||||
const activeRequest = jotaiStore.get(activeRequestAtom);
|
||||
const folderId =
|
||||
(folderIdOption === 'active-folder' ? activeRequest?.folderId : folderIdOption) ?? null;
|
||||
if (activeWorkspace == null) return [];
|
||||
if (workspaceId == null) return [];
|
||||
|
||||
return [
|
||||
{
|
||||
@@ -51,13 +51,12 @@ export function useCreateDropdownItems({
|
||||
{
|
||||
label: 'gRPC',
|
||||
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
|
||||
onSelect: () => createGrpcRequest({ folderId }),
|
||||
onSelect: () => createWorkspaceModel({ model: 'grpc_request', workspaceId, folderId }),
|
||||
},
|
||||
{
|
||||
label: 'WebSocket',
|
||||
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
|
||||
onSelect: () =>
|
||||
upsertWebsocketRequest.mutate({ folderId, workspaceId: activeWorkspace.id }),
|
||||
onSelect: () => createWorkspaceModel({ model: 'websocket_request', workspaceId, folderId }),
|
||||
},
|
||||
...((hideFolder
|
||||
? []
|
||||
@@ -70,14 +69,7 @@ export function useCreateDropdownItems({
|
||||
},
|
||||
]) as DropdownItem[]),
|
||||
];
|
||||
}, [
|
||||
activeWorkspace,
|
||||
createGrpcRequest,
|
||||
createHttpRequest,
|
||||
folderIdOption,
|
||||
hideFolder,
|
||||
hideIcons,
|
||||
]);
|
||||
}, [createHttpRequest, folderIdOption, hideFolder, hideIcons, workspaceId]);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import type { Environment } from '@yaakapp-internal/models';
|
||||
import { createWorkspaceModel } from '@yaakapp-internal/models';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { showPrompt } from '../lib/prompt';
|
||||
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useCreateEnvironment() {
|
||||
return useFastMutation<Environment | null, unknown, Environment | null>({
|
||||
return useFastMutation<string, unknown, Environment | null>({
|
||||
mutationKey: ['create_environment'],
|
||||
mutationFn: async (baseEnvironment) => {
|
||||
if (baseEnvironment == null) {
|
||||
throw new Error('No base environment passed');
|
||||
}
|
||||
|
||||
const workspaceId = getActiveWorkspaceId();
|
||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||
if (workspaceId == null) {
|
||||
throw new Error('Cannot create environment when no active workspace');
|
||||
}
|
||||
|
||||
const name = await showPrompt({
|
||||
id: 'new-environment',
|
||||
title: 'New Environment',
|
||||
@@ -23,18 +28,18 @@ export function useCreateEnvironment() {
|
||||
defaultValue: 'My Environment',
|
||||
confirmText: 'Create',
|
||||
});
|
||||
if (name == null) return null;
|
||||
if (name == null) throw new Error('No name provided to create environment');
|
||||
|
||||
return invokeCmd('cmd_create_environment', {
|
||||
return createWorkspaceModel({
|
||||
model: 'environment',
|
||||
name,
|
||||
variables: [],
|
||||
workspaceId,
|
||||
environmentId: baseEnvironment.id,
|
||||
});
|
||||
},
|
||||
onSuccess: async (environment) => {
|
||||
if (environment == null) return;
|
||||
setWorkspaceSearchParams({ environment_id: environment.id });
|
||||
onSuccess: async (environmentId) => {
|
||||
setWorkspaceSearchParams({ environment_id: environmentId });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||
import { createWorkspaceModel } from '@yaakapp-internal/models';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getActiveRequest } from './useActiveRequest';
|
||||
import { activeWorkspaceAtom } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { router } from '../lib/router';
|
||||
import { activeRequestAtom } from './useActiveRequest';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useCreateGrpcRequest() {
|
||||
return useFastMutation<
|
||||
GrpcRequest,
|
||||
string,
|
||||
unknown,
|
||||
Partial<Pick<GrpcRequest, 'name' | 'sortPriority' | 'folderId'>>
|
||||
>({
|
||||
mutationKey: ['create_grpc_request'],
|
||||
mutationFn: async (patch) => {
|
||||
const workspace = jotaiStore.get(activeWorkspaceAtom);
|
||||
if (workspace === null) {
|
||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||
if (workspaceId === null) {
|
||||
throw new Error("Cannot create grpc request when there's no active workspace");
|
||||
}
|
||||
const activeRequest = getActiveRequest();
|
||||
const activeRequest = jotaiStore.get(activeRequestAtom);
|
||||
if (patch.sortPriority === undefined) {
|
||||
if (activeRequest != null) {
|
||||
// Place above currently active request
|
||||
@@ -29,17 +29,16 @@ export function useCreateGrpcRequest() {
|
||||
}
|
||||
}
|
||||
patch.folderId = patch.folderId || activeRequest?.folderId;
|
||||
return invokeCmd<GrpcRequest>('cmd_create_grpc_request', {
|
||||
workspaceId: workspace.id,
|
||||
name: '',
|
||||
...patch,
|
||||
});
|
||||
return createWorkspaceModel({ model: 'grpc_request', workspaceId, ...patch });
|
||||
},
|
||||
onSuccess: async (request) => {
|
||||
onSuccess: async (requestId) => {
|
||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||
if (workspaceId == null) return;
|
||||
|
||||
await router.navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: request.workspaceId },
|
||||
search: (prev) => ({ ...prev, request_id: request.id }),
|
||||
params: { workspaceId },
|
||||
search: (prev) => ({ ...prev, request_id: requestId }),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||
import { createWorkspaceModel } from '@yaakapp-internal/models';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { router } from '../lib/router';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getActiveRequest } from './useActiveRequest';
|
||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { activeRequestAtom } from './useActiveRequest';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useCreateHttpRequest() {
|
||||
return useFastMutation<HttpRequest, unknown, Partial<HttpRequest>>({
|
||||
return useFastMutation<string, unknown, Partial<HttpRequest>>({
|
||||
mutationKey: ['create_http_request'],
|
||||
mutationFn: async (patch = {}) => {
|
||||
const workspaceId = getActiveWorkspaceId();
|
||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||
if (workspaceId == null) {
|
||||
throw new Error("Cannot create request when there's no active workspace");
|
||||
}
|
||||
|
||||
const activeRequest = getActiveRequest();
|
||||
const activeRequest = jotaiStore.get(activeRequestAtom);
|
||||
if (patch.sortPriority === undefined) {
|
||||
if (activeRequest != null) {
|
||||
// Place above currently active request
|
||||
@@ -25,15 +26,15 @@ export function useCreateHttpRequest() {
|
||||
}
|
||||
}
|
||||
patch.folderId = patch.folderId || activeRequest?.folderId;
|
||||
return invokeCmd<HttpRequest>('cmd_upsert_http_request', {
|
||||
request: { workspaceId, ...patch },
|
||||
});
|
||||
return createWorkspaceModel({ model: 'http_request', workspaceId, ...patch });
|
||||
},
|
||||
onSuccess: async (request) => {
|
||||
onSuccess: async (requestId) => {
|
||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||
if (workspaceId == null) return;
|
||||
await router.navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: request.workspaceId },
|
||||
search: (prev) => ({ ...prev, request_id: request.id }),
|
||||
params: { workspaceId },
|
||||
search: (prev) => ({ ...prev, request_id: requestId }),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import type { Workspace } from '@yaakapp-internal/models';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { showConfirmDelete } from '../lib/confirm';
|
||||
import { router } from '../lib/router';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDeleteActiveWorkspace() {
|
||||
return useFastMutation<Workspace | null, string>({
|
||||
mutationKey: ['delete_workspace'],
|
||||
mutationFn: async () => {
|
||||
const workspace = getActiveWorkspace();
|
||||
const confirmed = await showConfirmDelete({
|
||||
id: 'delete-workspace',
|
||||
title: 'Delete Workspace',
|
||||
description: (
|
||||
<>
|
||||
Permanently delete <InlineCode>{workspace?.name}</InlineCode>?
|
||||
</>
|
||||
),
|
||||
});
|
||||
if (!confirmed) return null;
|
||||
return invokeCmd('cmd_delete_workspace', { workspaceId: workspace?.id });
|
||||
},
|
||||
onSuccess: async (workspace) => {
|
||||
if (workspace === null) return;
|
||||
await router.navigate({ to: '/workspaces' });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { showConfirmDelete } from '../lib/confirm';
|
||||
import { resolvedModelName } from '../lib/resolvedModelName';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDeleteAnyGrpcRequest() {
|
||||
return useFastMutation<GrpcRequest | null, string, GrpcRequest>({
|
||||
mutationKey: ['delete_any_grpc_request'],
|
||||
mutationFn: async (request) => {
|
||||
const confirmed = await showConfirmDelete({
|
||||
id: 'delete-grpc-request',
|
||||
title: 'Delete Request',
|
||||
description: (
|
||||
<>
|
||||
Permanently delete <InlineCode>{resolvedModelName(request)}</InlineCode>?
|
||||
</>
|
||||
),
|
||||
});
|
||||
if (!confirmed) {
|
||||
return null;
|
||||
}
|
||||
return invokeCmd('cmd_delete_grpc_request', { requestId: request.id });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { showConfirmDelete } from '../lib/confirm';
|
||||
import { resolvedModelName } from '../lib/resolvedModelName';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDeleteAnyHttpRequest() {
|
||||
return useFastMutation<HttpRequest | null, string, HttpRequest>({
|
||||
mutationKey: ['delete_any_http_request'],
|
||||
mutationFn: async (request) => {
|
||||
const confirmed = await showConfirmDelete({
|
||||
id: 'delete-request',
|
||||
title: 'Delete Request',
|
||||
description: (
|
||||
<>
|
||||
Permanently delete <InlineCode>{resolvedModelName(request)}</InlineCode>?
|
||||
</>
|
||||
),
|
||||
});
|
||||
if (!confirmed) {
|
||||
return null;
|
||||
}
|
||||
return invokeCmd<HttpRequest>('cmd_delete_http_request', { requestId: request.id });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { deleteWebsocketRequest } from '../commands/deleteWebsocketRequest';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { useDeleteAnyGrpcRequest } from './useDeleteAnyGrpcRequest';
|
||||
import { useDeleteAnyHttpRequest } from './useDeleteAnyHttpRequest';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { requestsAtom } from './useRequests';
|
||||
|
||||
export function useDeleteAnyRequest() {
|
||||
const deleteAnyHttpRequest = useDeleteAnyHttpRequest();
|
||||
const deleteAnyGrpcRequest = useDeleteAnyGrpcRequest();
|
||||
|
||||
return useFastMutation<void, string, string>({
|
||||
mutationKey: ['delete_request'],
|
||||
mutationFn: async (id) => {
|
||||
if (id == null) return;
|
||||
const request = jotaiStore.get(requestsAtom).find((r) => r.id === id);
|
||||
|
||||
if (request?.model === 'websocket_request') {
|
||||
deleteWebsocketRequest.mutate(request);
|
||||
} else if (request?.model === 'http_request') {
|
||||
deleteAnyHttpRequest.mutate(request);
|
||||
} else if (request?.model === 'grpc_request') {
|
||||
deleteAnyGrpcRequest.mutate(request);
|
||||
} else {
|
||||
console.log('Failed to delete request', id, request);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { CookieJar } from '@yaakapp-internal/models';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { showConfirmDelete } from '../lib/confirm';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDeleteCookieJar(cookieJar: CookieJar | null) {
|
||||
return useFastMutation<CookieJar | null, string>({
|
||||
mutationKey: ['delete_cookie_jar', cookieJar?.id],
|
||||
mutationFn: async () => {
|
||||
const confirmed = await showConfirmDelete({
|
||||
id: 'delete-cookie-jar',
|
||||
title: 'Delete CookieJar',
|
||||
description: (
|
||||
<>
|
||||
Permanently delete <InlineCode>{cookieJar?.name}</InlineCode>?
|
||||
</>
|
||||
),
|
||||
});
|
||||
if (!confirmed) return null;
|
||||
return invokeCmd('cmd_delete_cookie_jar', { cookieJarId: cookieJar?.id });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { Environment } from '@yaakapp-internal/models';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { showConfirmDelete } from '../lib/confirm';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDeleteEnvironment(environment: Environment | null) {
|
||||
return useFastMutation<Environment | null, string>({
|
||||
mutationKey: ['delete_environment', environment?.id],
|
||||
mutationFn: async () => {
|
||||
const confirmed = await showConfirmDelete({
|
||||
id: 'delete-environment',
|
||||
title: 'Delete Environment',
|
||||
description: (
|
||||
<>
|
||||
Permanently delete <InlineCode>{environment?.name}</InlineCode>?
|
||||
</>
|
||||
),
|
||||
});
|
||||
if (!confirmed) return null;
|
||||
return invokeCmd('cmd_delete_environment', { environmentId: environment?.id });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import type { Folder } from '@yaakapp-internal/models';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { showConfirmDelete } from '../lib/confirm';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { getFolder } from './useFolders';
|
||||
|
||||
export function useDeleteFolder(id: string | null) {
|
||||
return useFastMutation<Folder | null, string>({
|
||||
mutationKey: ['delete_folder', id],
|
||||
mutationFn: async () => {
|
||||
const folder = getFolder(id);
|
||||
const confirmed = await showConfirmDelete({
|
||||
id: 'delete-folder',
|
||||
title: 'Delete Folder',
|
||||
description: (
|
||||
<>
|
||||
Permanently delete <InlineCode>{folder?.name}</InlineCode> and everything in it?
|
||||
</>
|
||||
),
|
||||
});
|
||||
if (!confirmed) return null;
|
||||
return invokeCmd('cmd_delete_folder', { folderId: id });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import type { GrpcConnection } from '@yaakapp-internal/models';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDeleteGrpcConnection(id: string | null) {
|
||||
return useFastMutation<GrpcConnection>({
|
||||
mutationKey: ['delete_grpc_connection', id],
|
||||
mutationFn: async () => {
|
||||
return await invokeCmd('cmd_delete_grpc_connection', { id: id });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,18 +1,12 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { grpcConnectionsAtom } from './useGrpcConnections';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDeleteGrpcConnections(requestId?: string) {
|
||||
const setGrpcConnections = useSetAtom(grpcConnectionsAtom);
|
||||
return useFastMutation({
|
||||
mutationKey: ['delete_grpc_connections', requestId],
|
||||
mutationFn: async () => {
|
||||
if (requestId === undefined) return;
|
||||
await invokeCmd('cmd_delete_all_grpc_connections', { requestId });
|
||||
},
|
||||
onSuccess: () => {
|
||||
setGrpcConnections((all) => all.filter((r) => r.requestId !== requestId));
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDeleteHttpResponse(id: string | null) {
|
||||
return useFastMutation<HttpResponse>({
|
||||
mutationKey: ['delete_http_response', id],
|
||||
mutationFn: async () => {
|
||||
return await invokeCmd('cmd_delete_http_response', { id: id });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,18 +1,12 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { httpResponsesAtom } from './useHttpResponses';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDeleteHttpResponses(requestId?: string) {
|
||||
const setHttpResponses = useSetAtom(httpResponsesAtom);
|
||||
return useFastMutation({
|
||||
mutationKey: ['delete_http_responses', requestId],
|
||||
mutationFn: async () => {
|
||||
if (requestId === undefined) return;
|
||||
await invokeCmd('cmd_delete_all_http_responses', { requestId });
|
||||
},
|
||||
onSuccess: () => {
|
||||
setHttpResponses((all) => all.filter((r) => r.requestId !== requestId));
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import { useSetAtom } from 'jotai/index';
|
||||
import {
|
||||
grpcConnectionsAtom,
|
||||
httpResponsesAtom,
|
||||
websocketConnectionsAtom,
|
||||
} from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai/index';
|
||||
import { showAlert } from '../lib/alert';
|
||||
import { showConfirmDelete } from '../lib/confirm';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { pluralizeCount } from '../lib/pluralize';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useGrpcConnections } from './useGrpcConnections';
|
||||
import { httpResponsesAtom, useHttpResponses } from './useHttpResponses';
|
||||
import { useWebsocketConnections } from './useWebsocketConnections';
|
||||
|
||||
export function useDeleteSendHistory() {
|
||||
const setHttpResponses = useSetAtom(httpResponsesAtom);
|
||||
const httpResponses = useHttpResponses();
|
||||
const grpcConnections = useGrpcConnections();
|
||||
const websocketConnections = useWebsocketConnections();
|
||||
const httpResponses = useAtomValue(httpResponsesAtom);
|
||||
const grpcConnections = useAtomValue(grpcConnectionsAtom);
|
||||
const websocketConnections = useAtomValue(websocketConnectionsAtom);
|
||||
|
||||
const labels = [
|
||||
httpResponses.length > 0 ? pluralizeCount('Http Response', httpResponses.length) : null,
|
||||
grpcConnections.length > 0 ? pluralizeCount('Grpc Connection', grpcConnections.length) : null,
|
||||
@@ -41,14 +44,9 @@ export function useDeleteSendHistory() {
|
||||
});
|
||||
if (!confirmed) return false;
|
||||
|
||||
const workspaceId = getActiveWorkspaceId();
|
||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||
await invokeCmd('cmd_delete_send_history', { workspaceId });
|
||||
return true;
|
||||
},
|
||||
onSuccess: async (confirmed) => {
|
||||
if (!confirmed) return;
|
||||
const activeWorkspaceId = getActiveWorkspaceId();
|
||||
setHttpResponses((all) => all.filter((r) => r.workspaceId !== activeWorkspaceId));
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
|
||||
export function useDuplicateFolder(id: string) {
|
||||
return useFastMutation<void, string>({
|
||||
mutationKey: ['duplicate_folder', id],
|
||||
mutationFn: () => invokeCmd('cmd_duplicate_folder', { id }),
|
||||
});
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||
import { router } from '../lib/router';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { getGrpcProtoFiles, setGrpcProtoFiles } from './useGrpcProtoFiles';
|
||||
|
||||
export function useDuplicateGrpcRequest({
|
||||
id,
|
||||
navigateAfter,
|
||||
}: {
|
||||
id: string | null;
|
||||
navigateAfter: boolean;
|
||||
}) {
|
||||
return useFastMutation<GrpcRequest, string>({
|
||||
mutationKey: ['duplicate_grpc_request', id],
|
||||
mutationFn: async () => {
|
||||
if (id === null) throw new Error("Can't duplicate a null grpc request");
|
||||
return invokeCmd('cmd_duplicate_grpc_request', { id });
|
||||
},
|
||||
onSuccess: async (request) => {
|
||||
if (id == null) return;
|
||||
|
||||
// Also copy proto files to new request
|
||||
const protoFiles = await getGrpcProtoFiles(id);
|
||||
await setGrpcProtoFiles(request.id, protoFiles);
|
||||
|
||||
if (navigateAfter) {
|
||||
await router.navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: request.workspaceId },
|
||||
search: (prev) => ({ ...prev, request_id: request.id }),
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||
import { router } from '../lib/router';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDuplicateHttpRequest({
|
||||
id,
|
||||
navigateAfter,
|
||||
}: {
|
||||
id: string | null;
|
||||
navigateAfter: boolean;
|
||||
}) {
|
||||
return useFastMutation<HttpRequest, string>({
|
||||
mutationKey: ['duplicate_http_request', id],
|
||||
mutationFn: async () => {
|
||||
if (id === null) throw new Error("Can't duplicate a null request");
|
||||
return invokeCmd('cmd_duplicate_http_request', { id });
|
||||
},
|
||||
onSuccess: async (request) => {
|
||||
if (navigateAfter) {
|
||||
await router.navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: request.workspaceId },
|
||||
search: (prev) => ({ ...prev, request_id: request.id }),
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import type { Environment } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { atom } from 'jotai/index';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
|
||||
export const environmentsAtom = atom<Environment[]>([]);
|
||||
|
||||
export const sortedEnvironmentsAtom = atom((get) =>
|
||||
get(environmentsAtom).sort((a, b) => a.name.localeCompare(b.name)),
|
||||
);
|
||||
|
||||
export const environmentsBreakdownAtom = atom<{
|
||||
baseEnvironment: Environment | null;
|
||||
allEnvironments: Environment[];
|
||||
subEnvironments: Environment[];
|
||||
}>((get) => {
|
||||
const allEnvironments = get(sortedEnvironmentsAtom);
|
||||
const baseEnvironment = allEnvironments.find((e) => e.environmentId == null) ?? null;
|
||||
const subEnvironments =
|
||||
allEnvironments.filter((e) => e.environmentId === (baseEnvironment?.id ?? 'n/a')) ?? [];
|
||||
return { baseEnvironment, subEnvironments, allEnvironments } as const;
|
||||
});
|
||||
|
||||
export function useEnvironments() {
|
||||
return useAtomValue(environmentsBreakdownAtom);
|
||||
}
|
||||
|
||||
export function getEnvironment(id: string | null) {
|
||||
return jotaiStore.get(environmentsAtom).find((e) => e.id === id) ?? null;
|
||||
}
|
||||
13
src-web/hooks/useEnvironmentsBreakdown.ts
Normal file
13
src-web/hooks/useEnvironmentsBreakdown.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { environmentsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai/index';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export function useEnvironmentsBreakdown() {
|
||||
const allEnvironments = useAtomValue(environmentsAtom);
|
||||
return useMemo(() => {
|
||||
const baseEnvironment = allEnvironments.find((e) => e.environmentId == null) ?? null;
|
||||
const subEnvironments =
|
||||
allEnvironments.filter((e) => e.environmentId === (baseEnvironment?.id ?? 'n/a')) ?? [];
|
||||
return { allEnvironments, baseEnvironment, subEnvironments };
|
||||
}, [allEnvironments]);
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { workspacesAtom } from '@yaakapp-internal/models';
|
||||
import { ExportDataDialog } from '../components/ExportDataDialog';
|
||||
import { showAlert } from '../lib/alert';
|
||||
import { showDialog } from '../lib/dialog';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { showToast } from '../lib/toast';
|
||||
import { getActiveWorkspace } from './useActiveWorkspace';
|
||||
import { activeWorkspaceAtom } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { workspacesAtom } from './useWorkspaces';
|
||||
|
||||
export function useExportData() {
|
||||
return useFastMutation({
|
||||
@@ -14,7 +14,7 @@ export function useExportData() {
|
||||
showAlert({ id: 'export-failed', title: 'Export Failed', body: err });
|
||||
},
|
||||
mutationFn: async () => {
|
||||
const activeWorkspace = getActiveWorkspace();
|
||||
const activeWorkspace = jotaiStore.get(activeWorkspaceAtom);
|
||||
const workspaces = jotaiStore.get(workspacesAtom);
|
||||
|
||||
if (activeWorkspace == null || workspaces.length === 0) return;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { activeWorkspaceAtom } from './useActiveWorkspace';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
|
||||
export function useFloatingSidebarHidden() {
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const activeWorkspace = useAtomValue(activeWorkspaceAtom);
|
||||
const { set, value } = useKeyValue<boolean>({
|
||||
namespace: 'no_sync',
|
||||
key: ['floating_sidebar_hidden', activeWorkspace?.id ?? 'n/a'],
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import type { Folder } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { atom } from 'jotai/index';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
|
||||
export const foldersAtom = atom<Folder[]>([]);
|
||||
|
||||
export function useFolders() {
|
||||
return useAtomValue(foldersAtom);
|
||||
}
|
||||
|
||||
export function getFolder(id: string | null) {
|
||||
return jotaiStore.get(foldersAtom).find((v) => v.id === id) ?? null;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import type { GrpcConnection } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai/index';
|
||||
|
||||
export const grpcConnectionsAtom = atom<GrpcConnection[]>([]);
|
||||
|
||||
export function useGrpcConnections() {
|
||||
return useAtomValue(grpcConnectionsAtom);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { GrpcEvent } from '@yaakapp-internal/models';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
|
||||
export function grpcEventsQueryKey({ connectionId }: { connectionId: string }) {
|
||||
return ['grpc_events', { connectionId }];
|
||||
}
|
||||
|
||||
export function useGrpcEvents(connectionId: string | null) {
|
||||
return (
|
||||
useQuery<GrpcEvent[]>({
|
||||
enabled: connectionId !== null,
|
||||
initialData: [],
|
||||
queryKey: grpcEventsQueryKey({ connectionId: connectionId ?? 'n/a' }),
|
||||
queryFn: async () => {
|
||||
return (await invokeCmd('cmd_list_grpc_events', {
|
||||
connectionId,
|
||||
limit: 200,
|
||||
})) as GrpcEvent[];
|
||||
},
|
||||
}).data ?? []
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
|
||||
export function protoFilesArgs(requestId: string | null) {
|
||||
@@ -11,11 +10,3 @@ export function protoFilesArgs(requestId: string | null) {
|
||||
export function useGrpcProtoFiles(activeRequestId: string | null) {
|
||||
return useKeyValue<string[]>({ ...protoFilesArgs(activeRequestId), fallback: [] });
|
||||
}
|
||||
|
||||
export async function getGrpcProtoFiles(requestId: string) {
|
||||
return getKeyValue<string[]>({ ...protoFilesArgs(requestId), fallback: [] });
|
||||
}
|
||||
|
||||
export async function setGrpcProtoFiles(requestId: string, protoFiles: string[]) {
|
||||
return setKeyValue<string[]>({ ...protoFilesArgs(requestId), value: protoFiles });
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||
import { useGrpcRequests } from './useGrpcRequests';
|
||||
|
||||
export function useGrpcRequest(id: string | null): GrpcRequest | null {
|
||||
const requests = useGrpcRequests();
|
||||
return requests.find((r) => r.id === id) ?? null;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
|
||||
export const grpcRequestsAtom = atom<GrpcRequest[]>([]);
|
||||
|
||||
export function useGrpcRequests() {
|
||||
return useAtomValue(grpcRequestsAtom);
|
||||
}
|
||||
|
||||
export function getGrpcRequest(id: string) {
|
||||
return jotaiStore.get(grpcRequestsAtom).find((r) => r.id === id) ?? null;
|
||||
}
|
||||
@@ -15,13 +15,13 @@ export type HotkeyAction =
|
||||
| 'grpc_request.send'
|
||||
| 'hotkeys.showHelp'
|
||||
| 'http_request.create'
|
||||
| 'http_request.delete'
|
||||
| 'http_request.duplicate'
|
||||
| 'http_request.send'
|
||||
| 'request_switcher.next'
|
||||
| 'request_switcher.prev'
|
||||
| 'request_switcher.toggle'
|
||||
| 'settings.show'
|
||||
| 'sidebar.delete_selected_item'
|
||||
| 'sidebar.focus'
|
||||
| 'url_bar.focus'
|
||||
| 'workspace_settings.show';
|
||||
@@ -35,13 +35,13 @@ const hotkeys: Record<HotkeyAction, string[]> = {
|
||||
'grpc_request.send': ['CmdCtrl+Enter', 'CmdCtrl+r'],
|
||||
'hotkeys.showHelp': ['CmdCtrl+Shift+/', 'CmdCtrl+Shift+?'], // when shift is pressed, it might be a question mark
|
||||
'http_request.create': ['CmdCtrl+n'],
|
||||
'http_request.delete': ['Backspace'],
|
||||
'http_request.duplicate': ['CmdCtrl+d'],
|
||||
'http_request.send': ['CmdCtrl+Enter', 'CmdCtrl+r'],
|
||||
'request_switcher.next': ['Control+Shift+Tab'],
|
||||
'request_switcher.prev': ['Control+Tab'],
|
||||
'request_switcher.toggle': ['CmdCtrl+p'],
|
||||
'settings.show': ['CmdCtrl+,'],
|
||||
'sidebar.delete_selected_item': ['Backspace'],
|
||||
'sidebar.focus': ['CmdCtrl+b'],
|
||||
'url_bar.focus': ['CmdCtrl+l'],
|
||||
'workspace_settings.show': ['CmdCtrl+;'],
|
||||
@@ -56,13 +56,13 @@ const hotkeyLabels: Record<HotkeyAction, string> = {
|
||||
'grpc_request.send': 'Send Message',
|
||||
'hotkeys.showHelp': 'Show Keyboard Shortcuts',
|
||||
'http_request.create': 'New Request',
|
||||
'http_request.delete': 'Delete Request',
|
||||
'http_request.duplicate': 'Duplicate Request',
|
||||
'http_request.send': 'Send Request',
|
||||
'request_switcher.next': 'Go To Previous Request',
|
||||
'request_switcher.prev': 'Go To Next Request',
|
||||
'request_switcher.toggle': 'Toggle Request Switcher',
|
||||
'settings.show': 'Open Settings',
|
||||
'sidebar.delete_selected_item': 'Delete Request',
|
||||
'sidebar.focus': 'Focus or Toggle Sidebar',
|
||||
'url_bar.focus': 'Focus URL',
|
||||
'workspace_settings.show': 'Open Workspace Settings',
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models';
|
||||
import { httpResponsesAtom } from '@yaakapp-internal/models';
|
||||
import type { GetHttpAuthenticationConfigResponse, JsonPrimitive } from '@yaakapp-internal/plugins';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { md5 } from 'js-md5';
|
||||
import { useState } from 'react';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useHttpResponses } from './useHttpResponses';
|
||||
|
||||
export function useHttpAuthenticationConfig(
|
||||
authName: string | null,
|
||||
values: Record<string, JsonPrimitive>,
|
||||
requestId: string,
|
||||
) {
|
||||
const responses = useHttpResponses();
|
||||
const responses = useAtomValue(httpResponsesAtom);
|
||||
const [forceRefreshCounter, setForceRefreshCounter] = useState<number>(0);
|
||||
|
||||
// Some auth handlers like OAuth 2.0 show the current token after a successful request. To
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||
import { useHttpRequests } from './useHttpRequests';
|
||||
|
||||
export function useHttpRequest(id: string | null): HttpRequest | null {
|
||||
const requests = useHttpRequests();
|
||||
return requests.find((r) => r.id === id) ?? null;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type {HttpRequest} from '@yaakapp-internal/models';
|
||||
import {atom, useAtomValue} from 'jotai';
|
||||
import {jotaiStore} from "../lib/jotai";
|
||||
|
||||
export const httpRequestsAtom = atom<HttpRequest[]>([]);
|
||||
|
||||
export function useHttpRequests() {
|
||||
return useAtomValue(httpRequestsAtom);
|
||||
}
|
||||
|
||||
export function getHttpRequest(id: string) {
|
||||
return jotaiStore.get(httpRequestsAtom).find(r => r.id === id) ?? null;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { atom } from 'jotai/index';
|
||||
|
||||
export const httpResponsesAtom = atom<HttpResponse[]>([]);
|
||||
|
||||
export function useHttpResponses() {
|
||||
return useAtomValue(httpResponsesAtom);
|
||||
}
|
||||
@@ -1,17 +1,13 @@
|
||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||
import type { HttpRequest} from '@yaakapp-internal/models';
|
||||
import { createWorkspaceModel, patchModelById } from '@yaakapp-internal/models';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { useCreateHttpRequest } from './useCreateHttpRequest';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useRequestUpdateKey } from './useRequestUpdateKey';
|
||||
import { showToast } from '../lib/toast';
|
||||
import { useUpdateAnyHttpRequest } from './useUpdateAnyHttpRequest';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { wasUpdatedExternally } from './useRequestUpdateKey';
|
||||
|
||||
export function useImportCurl() {
|
||||
const updateRequest = useUpdateAnyHttpRequest();
|
||||
const createRequest = useCreateHttpRequest();
|
||||
const { wasUpdatedExternally } = useRequestUpdateKey(null);
|
||||
|
||||
return useFastMutation({
|
||||
mutationKey: ['import_curl'],
|
||||
mutationFn: async ({
|
||||
@@ -21,8 +17,8 @@ export function useImportCurl() {
|
||||
overwriteRequestId?: string;
|
||||
command: string;
|
||||
}) => {
|
||||
const workspaceId = getActiveWorkspaceId();
|
||||
const request: HttpRequest = await invokeCmd('cmd_curl_to_request', {
|
||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||
const importedRequest: HttpRequest = await invokeCmd('cmd_curl_to_request', {
|
||||
command,
|
||||
workspaceId,
|
||||
});
|
||||
@@ -30,21 +26,18 @@ export function useImportCurl() {
|
||||
let verb;
|
||||
if (overwriteRequestId == null) {
|
||||
verb = 'Created';
|
||||
await createRequest.mutateAsync(request);
|
||||
await createWorkspaceModel(importedRequest);
|
||||
} else {
|
||||
verb = 'Updated';
|
||||
await updateRequest.mutateAsync({
|
||||
id: overwriteRequestId,
|
||||
update: (r: HttpRequest) => ({
|
||||
...request,
|
||||
id: r.id,
|
||||
createdAt: r.createdAt,
|
||||
workspaceId: r.workspaceId,
|
||||
folderId: r.folderId,
|
||||
name: r.name,
|
||||
sortPriority: r.sortPriority,
|
||||
}),
|
||||
});
|
||||
await patchModelById(importedRequest.model, overwriteRequestId, (r: HttpRequest) => ({
|
||||
...importedRequest,
|
||||
id: r.id,
|
||||
createdAt: r.createdAt,
|
||||
workspaceId: r.workspaceId,
|
||||
folderId: r.folderId,
|
||||
name: r.name,
|
||||
sortPriority: r.sortPriority,
|
||||
}));
|
||||
|
||||
setTimeout(() => wasUpdatedExternally(overwriteRequestId), 100);
|
||||
}
|
||||
|
||||
@@ -11,15 +11,16 @@ import { VStack } from '../components/core/Stacks';
|
||||
import { ImportDataDialog } from '../components/ImportDataDialog';
|
||||
import { showAlert } from '../lib/alert';
|
||||
import { showDialog } from '../lib/dialog';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { pluralizeCount } from '../lib/pluralize';
|
||||
import { router } from '../lib/router';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getActiveWorkspace } from './useActiveWorkspace';
|
||||
import { activeWorkspaceAtom } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useImportData() {
|
||||
const importData = async (filePath: string): Promise<boolean> => {
|
||||
const activeWorkspace = getActiveWorkspace();
|
||||
const activeWorkspace = jotaiStore.get(activeWorkspaceAtom);
|
||||
const imported: {
|
||||
workspaces: Workspace[];
|
||||
environments: Environment[];
|
||||
|
||||
@@ -1,25 +1,12 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import type { KeyValue } from '@yaakapp-internal/models';
|
||||
import { keyValuesAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { atom } from 'jotai/index';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { buildKeyValueKey, extractKeyValueOrFallback, setKeyValue } from '../lib/keyValueStore';
|
||||
|
||||
const DEFAULT_NAMESPACE = 'global';
|
||||
|
||||
export const keyValuesAtom = atom<KeyValue[] | null>(null);
|
||||
|
||||
export function keyValueQueryKey({
|
||||
namespace = DEFAULT_NAMESPACE,
|
||||
key,
|
||||
}: {
|
||||
namespace?: string;
|
||||
key: string | string[];
|
||||
}) {
|
||||
return ['key_value', { namespace, key: buildKeyValueKey(key) }];
|
||||
}
|
||||
|
||||
export function useKeyValue<T extends object | boolean | number | string | null>({
|
||||
namespace = DEFAULT_NAMESPACE,
|
||||
key,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { GrpcConnection } from '@yaakapp-internal/models';
|
||||
import { useGrpcConnections } from './useGrpcConnections';
|
||||
import type { GrpcConnection} from '@yaakapp-internal/models';
|
||||
import { grpcConnectionsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai/index';
|
||||
|
||||
export function useLatestGrpcConnection(requestId: string | null): GrpcConnection | null {
|
||||
return useGrpcConnections().find((c) => c.requestId === requestId) ?? null;
|
||||
return useAtomValue(grpcConnectionsAtom).find((c) => c.requestId === requestId) ?? null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||
import { useHttpResponses } from './useHttpResponses';
|
||||
import type { HttpResponse} from '@yaakapp-internal/models';
|
||||
import { httpResponsesAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai/index';
|
||||
|
||||
export function useLatestHttpResponse(requestId: string | null): HttpResponse | null {
|
||||
return useHttpResponses().find((r) => r.requestId === requestId) ?? null;
|
||||
return useAtomValue(httpResponsesAtom).find((r) => r.requestId === requestId) ?? null;
|
||||
}
|
||||
|
||||
@@ -2,18 +2,18 @@ import React from 'react';
|
||||
import { MoveToWorkspaceDialog } from '../components/MoveToWorkspaceDialog';
|
||||
import { showDialog } from '../lib/dialog';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { requestsAtom } from './useRequests';
|
||||
import { allRequestsAtom } from './useAllRequests';
|
||||
|
||||
export function useMoveToWorkspace(id: string) {
|
||||
return useFastMutation<void, unknown>({
|
||||
mutationKey: ['move_workspace', id],
|
||||
mutationFn: async () => {
|
||||
const activeWorkspaceId = getActiveWorkspaceId();
|
||||
const activeWorkspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||
if (activeWorkspaceId == null) return;
|
||||
|
||||
const request = jotaiStore.get(requestsAtom).find((r) => r.id === id);
|
||||
const request = jotaiStore.get(allRequestsAtom).find((r) => r.id === id);
|
||||
if (request == null) return;
|
||||
|
||||
showDialog({
|
||||
|
||||
@@ -1,19 +1,72 @@
|
||||
import type { GrpcConnection, GrpcRequest } from '@yaakapp-internal/models';
|
||||
import { useGrpcConnections } from './useGrpcConnections';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
import { useLatestGrpcConnection } from './useLatestGrpcConnection';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import type { GrpcConnection, GrpcEvent } from '@yaakapp-internal/models';
|
||||
import {
|
||||
grpcConnectionsAtom,
|
||||
grpcEventsAtom,
|
||||
replaceModelsInStore,
|
||||
} from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { atom } from 'jotai/index';
|
||||
import { useEffect } from 'react';
|
||||
import { atomWithKVStorage } from '../lib/atoms/atomWithKVStorage';
|
||||
import { activeRequestIdAtom } from './useActiveRequestId';
|
||||
|
||||
export function usePinnedGrpcConnection(activeRequest: GrpcRequest) {
|
||||
const latestConnection = useLatestGrpcConnection(activeRequest.id);
|
||||
const { set: setPinnedConnectionId, value: pinnedConnectionId } = useKeyValue<string | null>({
|
||||
// Key on latest connection instead of activeRequest because connections change out of band of active request
|
||||
key: ['pinned_grpc_connection_id', latestConnection?.id ?? 'n/a'],
|
||||
fallback: null,
|
||||
namespace: 'global',
|
||||
});
|
||||
const connections = useGrpcConnections().filter((c) => c.requestId === activeRequest.id);
|
||||
const activeConnection: GrpcConnection | null =
|
||||
connections.find((r) => r.id === pinnedConnectionId) ?? latestConnection;
|
||||
const pinnedGrpcConnectionIdsAtom = atomWithKVStorage<Record<string, string | null>>(
|
||||
'pinned-grpc-connection-ids',
|
||||
{},
|
||||
);
|
||||
|
||||
return { activeConnection, setPinnedConnectionId, pinnedConnectionId, connections } as const;
|
||||
export const pinnedGrpcConnectionIdAtom = atom(
|
||||
(get) => {
|
||||
const activeRequestId = get(activeRequestIdAtom);
|
||||
const activeConnections = get(activeGrpcConnections);
|
||||
const latestConnection = activeConnections[0] ?? null;
|
||||
if (!activeRequestId) return null;
|
||||
|
||||
const key = recordKey(activeRequestId, latestConnection);
|
||||
return get(pinnedGrpcConnectionIdsAtom)[key] ?? null;
|
||||
},
|
||||
(get, set, id: string | null) => {
|
||||
const activeRequestId = get(activeRequestIdAtom);
|
||||
const activeConnections = get(activeGrpcConnections);
|
||||
const latestConnection = activeConnections[0] ?? null;
|
||||
if (!activeRequestId) return;
|
||||
|
||||
const key = recordKey(activeRequestId, latestConnection);
|
||||
set(pinnedGrpcConnectionIdsAtom, (prev) => ({
|
||||
...prev,
|
||||
[key]: id,
|
||||
}));
|
||||
},
|
||||
);
|
||||
|
||||
function recordKey(activeRequestId: string | null, latestConnection: GrpcConnection | null) {
|
||||
return activeRequestId + '-' + (latestConnection?.id ?? 'none');
|
||||
}
|
||||
|
||||
export const activeGrpcConnections = atom<GrpcConnection[]>((get) => {
|
||||
const activeRequestId = get(activeRequestIdAtom) ?? 'n/a';
|
||||
return get(grpcConnectionsAtom).filter((c) => c.requestId === activeRequestId) ?? [];
|
||||
});
|
||||
|
||||
export const activeGrpcConnectionAtom = atom<GrpcConnection | null>((get) => {
|
||||
const activeRequestId = get(activeRequestIdAtom) ?? 'n/a';
|
||||
const activeConnections = get(activeGrpcConnections);
|
||||
const latestConnection = activeConnections[0] ?? null;
|
||||
const pinnedConnectionId = get(pinnedGrpcConnectionIdsAtom)[
|
||||
recordKey(activeRequestId, latestConnection)
|
||||
];
|
||||
return activeConnections.find((c) => c.id === pinnedConnectionId) ?? activeConnections[0] ?? null;
|
||||
});
|
||||
|
||||
export function useGrpcEvents(connectionId: string | null) {
|
||||
const events = useAtomValue(grpcEventsAtom);
|
||||
|
||||
useEffect(() => {
|
||||
invoke<GrpcEvent[]>('plugin:yaak-models|grpc_events', { connectionId }).then((events) => {
|
||||
replaceModelsInStore('grpc_event', events);
|
||||
});
|
||||
}, [connectionId]);
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||
import { useHttpResponses } from './useHttpResponses';
|
||||
import type { HttpResponse} from '@yaakapp-internal/models';
|
||||
import { httpResponsesAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai/index';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
import { useLatestHttpResponse } from './useLatestHttpResponse';
|
||||
|
||||
@@ -11,7 +12,7 @@ export function usePinnedHttpResponse(activeRequestId: string) {
|
||||
fallback: null,
|
||||
namespace: 'global',
|
||||
});
|
||||
const allResponses = useHttpResponses();
|
||||
const allResponses = useAtomValue(httpResponsesAtom);
|
||||
const responses = allResponses.filter((r) => r.requestId === activeRequestId);
|
||||
const activeResponse: HttpResponse | null =
|
||||
responses.find((r) => r.id === pinnedResponseId) ?? latestResponse;
|
||||
|
||||
@@ -1,18 +1,65 @@
|
||||
import type { WebsocketConnection, WebsocketRequest } from '@yaakapp-internal/models';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
import { useLatestWebsocketConnection, useWebsocketConnections } from './useWebsocketConnections';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import type { WebsocketConnection, WebsocketEvent } from '@yaakapp-internal/models';
|
||||
import {
|
||||
replaceModelsInStore,
|
||||
websocketConnectionsAtom,
|
||||
websocketEventsAtom,
|
||||
} from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai/index';
|
||||
import { useEffect } from 'react';
|
||||
import { atomWithKVStorage } from '../lib/atoms/atomWithKVStorage';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { activeRequestIdAtom } from './useActiveRequestId';
|
||||
|
||||
export function usePinnedWebsocketConnection(activeRequest: WebsocketRequest) {
|
||||
const latestConnection = useLatestWebsocketConnection(activeRequest.id);
|
||||
const { set: setPinnedConnectionId, value: pinnedConnectionId } = useKeyValue<string | null>({
|
||||
// Key on latest connection instead of activeRequest because connections change out of band of active request
|
||||
key: ['pinned_websocket_connection_id', latestConnection?.id ?? 'n/a'],
|
||||
fallback: null,
|
||||
namespace: 'global',
|
||||
});
|
||||
const connections = useWebsocketConnections().filter((c) => c.requestId === activeRequest.id);
|
||||
const activeConnection: WebsocketConnection | null =
|
||||
connections.find((r) => r.id === pinnedConnectionId) ?? latestConnection;
|
||||
const pinnedWebsocketConnectionIdAtom = atomWithKVStorage<Record<string, string | null>>(
|
||||
'pinned-websocket-connection-ids',
|
||||
{},
|
||||
);
|
||||
|
||||
return { activeConnection, setPinnedConnectionId, pinnedConnectionId, connections } as const;
|
||||
function recordKey(activeRequestId: string | null, latestConnection: WebsocketConnection | null) {
|
||||
return activeRequestId + '-' + (latestConnection?.id ?? 'none');
|
||||
}
|
||||
|
||||
export const activeWebsocketConnectionsAtom = atom<WebsocketConnection[]>((get) => {
|
||||
const activeRequestId = get(activeRequestIdAtom) ?? 'n/a';
|
||||
return get(websocketConnectionsAtom).filter((c) => c.requestId === activeRequestId) ?? [];
|
||||
});
|
||||
|
||||
export const activeWebsocketConnectionAtom = atom<WebsocketConnection | null>((get) => {
|
||||
const activeRequestId = get(activeRequestIdAtom) ?? 'n/a';
|
||||
const activeConnections = get(activeWebsocketConnectionsAtom);
|
||||
const latestConnection = activeConnections[0] ?? null;
|
||||
const pinnedConnectionId = get(pinnedWebsocketConnectionIdAtom)[
|
||||
recordKey(activeRequestId, latestConnection)
|
||||
];
|
||||
return activeConnections.find((c) => c.id === pinnedConnectionId) ?? activeConnections[0] ?? null;
|
||||
});
|
||||
|
||||
export const activeWebsocketEventsAtom = atom(async (get) => {
|
||||
const connection = get(activeWebsocketConnectionAtom);
|
||||
return invoke<WebsocketEvent[]>('plugin:yaak-models|websocket_events', {
|
||||
connectionId: connection?.id ?? 'n/a',
|
||||
});
|
||||
});
|
||||
|
||||
export function setPinnedWebsocketConnectionId(id: string | null) {
|
||||
const activeRequestId = jotaiStore.get(activeRequestIdAtom);
|
||||
const activeConnections = jotaiStore.get(activeWebsocketConnectionsAtom);
|
||||
const latestConnection = activeConnections[0] ?? null;
|
||||
if (activeRequestId == null) return;
|
||||
jotaiStore.set(pinnedWebsocketConnectionIdAtom, (prev) => {
|
||||
return { ...prev, [recordKey(activeRequestId, latestConnection)]: id };
|
||||
});
|
||||
}
|
||||
|
||||
export function useWebsocketEvents(connectionId: string | null) {
|
||||
const events = useAtomValue(websocketEventsAtom);
|
||||
|
||||
useEffect(() => {
|
||||
invoke<WebsocketEvent[]>('plugin:yaak-models|websocket_events', { connectionId }).then(
|
||||
(events) => replaceModelsInStore('websocket_event', events),
|
||||
);
|
||||
}, [connectionId]);
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { BootResponse } from '@yaakapp-internal/plugins';
|
||||
import { queryClient } from '../lib/queryClient';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
|
||||
function pluginInfoKey(id: string) {
|
||||
return ['plugin_info', id];
|
||||
}
|
||||
|
||||
export function usePluginInfo(id: string) {
|
||||
return useQuery({
|
||||
queryKey: ['plugin_info', id],
|
||||
queryKey: pluginInfoKey(id),
|
||||
queryFn: async () => {
|
||||
const info = (await invokeCmd('cmd_plugin_info', { id })) as BootResponse;
|
||||
return info;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function invalidateAllPluginInfo() {
|
||||
queryClient.invalidateQueries({ queryKey: ['plugin_info'] }).catch(console.error);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import {useMutation} from "@tanstack/react-query";
|
||||
import type { Plugin } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue, useSetAtom } from 'jotai';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { changeModelStoreWorkspace, pluginsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { minPromiseMillis } from '../lib/minPromiseMillis';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
|
||||
const plugins = await listPlugins();
|
||||
export const pluginsAtom = atom<Plugin[]>(plugins);
|
||||
|
||||
export function usePlugins() {
|
||||
return useAtomValue(pluginsAtom);
|
||||
}
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { invalidateAllPluginInfo } from './usePluginInfo';
|
||||
|
||||
export function usePluginsKey() {
|
||||
return useAtomValue(pluginsAtom)
|
||||
@@ -21,22 +17,17 @@ export function usePluginsKey() {
|
||||
* Reload all plugins and refresh the list of plugins
|
||||
*/
|
||||
export function useRefreshPlugins() {
|
||||
const setPlugins = useSetAtom(pluginsAtom);
|
||||
return useMutation({
|
||||
mutationKey: ['refresh_plugins'],
|
||||
mutationFn: async () => {
|
||||
const plugins = await minPromiseMillis(
|
||||
await minPromiseMillis(
|
||||
(async function () {
|
||||
await invokeCmd('cmd_reload_plugins');
|
||||
return listPlugins();
|
||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||
await changeModelStoreWorkspace(workspaceId); // Force refresh models
|
||||
invalidateAllPluginInfo();
|
||||
})(),
|
||||
);
|
||||
setPlugins(plugins);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function listPlugins(): Promise<Plugin[]> {
|
||||
const plugins: Plugin[] = (await invokeCmd('cmd_list_plugins')) ?? [];
|
||||
return plugins;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { cookieJarsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
|
||||
import {activeCookieJarAtom} from "./useActiveCookieJar";
|
||||
import { activeWorkspaceIdAtom, useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useCookieJars } from './useCookieJars';
|
||||
import { activeCookieJarAtom } from './useActiveCookieJar';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
|
||||
const kvKey = (workspaceId: string) => 'recent_cookie_jars::' + workspaceId;
|
||||
@@ -11,10 +12,10 @@ const namespace = 'global';
|
||||
const fallback: string[] = [];
|
||||
|
||||
export function useRecentCookieJars() {
|
||||
const cookieJars = useCookieJars();
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const cookieJars = useAtomValue(cookieJarsAtom);
|
||||
const activeWorkspaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const kv = useKeyValue<string[]>({
|
||||
key: kvKey(activeWorkspace?.id ?? 'n/a'),
|
||||
key: kvKey(activeWorkspaceId ?? 'n/a'),
|
||||
namespace,
|
||||
fallback,
|
||||
});
|
||||
@@ -37,7 +38,7 @@ export function useSubscribeRecentCookieJars() {
|
||||
|
||||
const key = kvKey(activeWorkspaceId);
|
||||
|
||||
const recentIds = await getKeyValue<string[]>({ namespace, key, fallback });
|
||||
const recentIds = getKeyValue<string[]>({ namespace, key, fallback });
|
||||
if (recentIds[0] === activeCookieJarId) return; // Short-circuit
|
||||
|
||||
const withoutActiveId = recentIds.filter((id) => id !== activeCookieJarId);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
|
||||
import { activeEnvironmentIdAtom } from './useActiveEnvironment';
|
||||
import { activeWorkspaceIdAtom, useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useEnvironments } from './useEnvironments';
|
||||
import { activeWorkspaceAtom, activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useEnvironmentsBreakdown } from './useEnvironmentsBreakdown';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
|
||||
const kvKey = (workspaceId: string) => 'recent_environments::' + workspaceId;
|
||||
@@ -11,8 +12,8 @@ const namespace = 'global';
|
||||
const fallback: string[] = [];
|
||||
|
||||
export function useRecentEnvironments() {
|
||||
const { subEnvironments } = useEnvironments();
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const { subEnvironments } = useEnvironmentsBreakdown();
|
||||
const activeWorkspace = useAtomValue(activeWorkspaceAtom);
|
||||
const kv = useKeyValue<string[]>({
|
||||
key: kvKey(activeWorkspace?.id ?? 'n/a'),
|
||||
namespace,
|
||||
@@ -37,7 +38,7 @@ export function useSubscribeRecentEnvironments() {
|
||||
|
||||
const key = kvKey(activeWorkspaceId);
|
||||
|
||||
const recentIds = await getKeyValue<string[]>({ namespace, key, fallback });
|
||||
const recentIds = getKeyValue<string[]>({ namespace, key, fallback });
|
||||
if (recentIds[0] === activeEnvironmentId) return; // Short-circuit
|
||||
|
||||
const withoutActiveId = recentIds.filter((id) => id !== activeEnvironmentId);
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
|
||||
import { activeRequestIdAtom } from './useActiveRequestId';
|
||||
import { activeWorkspaceIdAtom, useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
import { useRequests } from './useRequests';
|
||||
import { useAllRequests } from './useAllRequests';
|
||||
|
||||
const kvKey = (workspaceId: string) => 'recent_requests::' + workspaceId;
|
||||
const namespace = 'global';
|
||||
const fallback: string[] = [];
|
||||
|
||||
export function useRecentRequests() {
|
||||
const requests = useRequests();
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const requests = useAllRequests();
|
||||
const activeWorkspaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
|
||||
const { set: setRecentRequests, value: recentRequests } = useKeyValue<string[]>({
|
||||
key: kvKey(activeWorkspace?.id ?? 'n/a'),
|
||||
key: kvKey(activeWorkspaceId ?? 'n/a'),
|
||||
namespace,
|
||||
fallback,
|
||||
});
|
||||
@@ -38,7 +39,7 @@ export function useSubscribeRecentRequests() {
|
||||
|
||||
const key = kvKey(activeWorkspaceId);
|
||||
|
||||
const recentIds = await getKeyValue<string[]>({ namespace, key, fallback });
|
||||
const recentIds = getKeyValue<string[]>({ namespace, key, fallback });
|
||||
if (recentIds[0] === activeRequestId) return; // Short-circuit
|
||||
|
||||
const withoutActiveId = recentIds.filter((id) => id !== activeRequestId);
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { workspacesAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
import { useWorkspaces } from './useWorkspaces';
|
||||
|
||||
const kvKey = () => 'recent_workspaces';
|
||||
const namespace = 'global';
|
||||
const fallback: string[] = [];
|
||||
|
||||
export function useRecentWorkspaces() {
|
||||
const workspaces = useWorkspaces();
|
||||
const workspaces = useAtomValue(workspacesAtom);
|
||||
const { value, isLoading } = useKeyValue<string[]>({ key: kvKey(), namespace, fallback });
|
||||
|
||||
const onlyValidIds = useMemo(
|
||||
@@ -31,7 +32,7 @@ export function useSubscribeRecentWorkspaces() {
|
||||
|
||||
const key = kvKey();
|
||||
|
||||
const recentIds = await getKeyValue<string[]>({ namespace, key, fallback });
|
||||
const recentIds = getKeyValue<string[]>({ namespace, key, fallback });
|
||||
if (recentIds[0] === activeWorkspaceId) return; // Short-circuit
|
||||
|
||||
const withoutActiveId = recentIds.filter((id) => id !== activeWorkspaceId);
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import {showPrompt} from "../lib/prompt";
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useRequests } from './useRequests';
|
||||
import { useUpdateAnyGrpcRequest } from './useUpdateAnyGrpcRequest';
|
||||
import { useUpdateAnyHttpRequest } from './useUpdateAnyHttpRequest';
|
||||
|
||||
export function useRenameRequest(requestId: string | null) {
|
||||
const updateHttpRequest = useUpdateAnyHttpRequest();
|
||||
const updateGrpcRequest = useUpdateAnyGrpcRequest();
|
||||
const requests = useRequests();
|
||||
|
||||
return useFastMutation({
|
||||
mutationKey: ['rename_request'],
|
||||
mutationFn: async () => {
|
||||
const request = requests.find((r) => r.id === requestId);
|
||||
if (request == null) return;
|
||||
|
||||
const name = await showPrompt({
|
||||
id: 'rename-request',
|
||||
title: 'Rename Request',
|
||||
description:
|
||||
request.name === '' ? (
|
||||
'Enter a new name'
|
||||
) : (
|
||||
<>
|
||||
Enter a new name for <InlineCode>{request.name}</InlineCode>
|
||||
</>
|
||||
),
|
||||
label: 'Name',
|
||||
placeholder: 'New Name',
|
||||
defaultValue: request.name,
|
||||
confirmText: 'Save',
|
||||
});
|
||||
|
||||
if (name == null) return;
|
||||
|
||||
if (request.model === 'http_request') {
|
||||
updateHttpRequest.mutate({ id: request.id, update: (r: HttpRequest) => ({ ...r, name }) });
|
||||
} else {
|
||||
updateGrpcRequest.mutate({ id: request.id, update: (r: GrpcRequest) => ({ ...r, name }) });
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useActiveEnvironment } from './useActiveEnvironment';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
|
||||
export function useRenderTemplate(template: string) {
|
||||
const workspaceId = useActiveWorkspace()?.id ?? 'n/a';
|
||||
const workspaceId = useAtomValue(activeWorkspaceIdAtom) ?? 'n/a';
|
||||
const environmentId = useActiveEnvironment()?.id ?? null;
|
||||
return useQuery<string>({
|
||||
placeholderData: (prev) => prev, // Keep previous data on refetch
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import type { ModelPayload } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { generateId } from '../lib/generateId';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
|
||||
const keyAtom = atom<Record<string, string>>({});
|
||||
const requestUpdateKeyAtom = atom<Record<string, string>>({});
|
||||
|
||||
getCurrentWebviewWindow()
|
||||
.listen<ModelPayload>('upserted_model', ({ payload }) => {
|
||||
if (
|
||||
(payload.model.model === 'http_request' ||
|
||||
payload.model.model === 'grpc_request' ||
|
||||
payload.model.model === 'websocket_request') &&
|
||||
((payload.updateSource.type === 'window' &&
|
||||
payload.updateSource.label !== getCurrentWebviewWindow().label) ||
|
||||
payload.updateSource.type !== 'window')
|
||||
) {
|
||||
wasUpdatedExternally(payload.model.id);
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
export function wasUpdatedExternally(changedRequestId: string) {
|
||||
jotaiStore.set(requestUpdateKeyAtom, (m) => ({ ...m, [changedRequestId]: generateId() }));
|
||||
}
|
||||
|
||||
export function useRequestUpdateKey(requestId: string | null) {
|
||||
const keys = useAtomValue(keyAtom);
|
||||
const keys = useAtomValue(requestUpdateKeyAtom);
|
||||
const key = keys[requestId ?? 'n/a'];
|
||||
return {
|
||||
updateKey: `${requestId}::${key ?? 'default'}`,
|
||||
wasUpdatedExternally: (changedRequestId: string) => {
|
||||
jotaiStore.set(keyAtom, (m) => ({ ...m, [changedRequestId]: generateId() }));
|
||||
},
|
||||
};
|
||||
return `${requestId}::${key ?? 'default'}`;
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { grpcRequestsAtom } from './useGrpcRequests';
|
||||
import { httpRequestsAtom } from './useHttpRequests';
|
||||
import { websocketRequestsAtom } from './useWebsocketRequests';
|
||||
|
||||
export const requestsAtom = atom((get) => [
|
||||
...get(httpRequestsAtom),
|
||||
...get(grpcRequestsAtom),
|
||||
...get(websocketRequestsAtom),
|
||||
]);
|
||||
|
||||
export function useRequests() {
|
||||
return useAtomValue(requestsAtom);
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import { settingsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { resolveAppearance } from '../lib/theme/appearance';
|
||||
import { usePreferredAppearance } from './usePreferredAppearance';
|
||||
import { useSettings } from './useSettings';
|
||||
|
||||
export function useResolvedAppearance() {
|
||||
const preferredAppearance = usePreferredAppearance();
|
||||
const settings = useSettings();
|
||||
const settings = useAtomValue(settingsAtom);
|
||||
return resolveAppearance(preferredAppearance, settings.appearance);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { settingsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { getResolvedTheme } from '../lib/theme/themes';
|
||||
import { usePreferredAppearance } from './usePreferredAppearance';
|
||||
import { useSettings } from './useSettings';
|
||||
|
||||
export function useResolvedTheme() {
|
||||
const preferredAppearance = usePreferredAppearance();
|
||||
const settings = useSettings();
|
||||
const settings = useAtomValue(settingsAtom);
|
||||
return getResolvedTheme(
|
||||
preferredAppearance,
|
||||
settings.appearance,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { save } from '@tauri-apps/plugin-dialog';
|
||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||
import { getModel } from '@yaakapp-internal/models';
|
||||
import mime from 'mime';
|
||||
import slugify from 'slugify';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
@@ -7,13 +8,12 @@ import { getContentTypeFromHeaders } from '../lib/model_util';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { showToast } from '../lib/toast';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { getHttpRequest } from './useHttpRequests';
|
||||
|
||||
export function useSaveResponse(response: HttpResponse) {
|
||||
return useFastMutation({
|
||||
mutationKey: ['save_response', response.id],
|
||||
mutationFn: async () => {
|
||||
const request = getHttpRequest(response.requestId);
|
||||
const request = getModel('http_request', response.requestId);
|
||||
if (request == null) return null;
|
||||
|
||||
const contentType = getContentTypeFromHeaders(response.headers) ?? 'unknown';
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||
import { getModel } from '@yaakapp-internal/models';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getActiveCookieJar } from './useActiveCookieJar';
|
||||
import { getActiveEnvironment } from './useActiveEnvironment';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { getHttpRequest } from './useHttpRequests';
|
||||
|
||||
export function useSendAnyHttpRequest() {
|
||||
return useFastMutation<HttpResponse | null, string, string | null>({
|
||||
mutationKey: ['send_any_request'],
|
||||
mutationFn: async (id) => {
|
||||
const request = getHttpRequest(id ?? 'n/a');
|
||||
const request = getModel('http_request', id ?? 'n/a');
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { Settings } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { atom } from 'jotai/index';
|
||||
import {jotaiStore} from "../lib/jotai";
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
|
||||
const settings = await invokeCmd<Settings>('cmd_get_settings');
|
||||
export const settingsAtom = atom<Settings>(settings);
|
||||
|
||||
export function useSettings() {
|
||||
return useAtomValue(settingsAtom);
|
||||
}
|
||||
|
||||
export function getSettings() {
|
||||
return jotaiStore.get(settingsAtom);
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
|
||||
export function useSidebarHidden() {
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const activeWorkspaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const { set, value } = useKeyValue<boolean>({
|
||||
namespace: 'no_sync',
|
||||
key: ['sidebar_hidden', activeWorkspace?.id ?? 'n/a'],
|
||||
key: ['sidebar_hidden', activeWorkspaceId ?? 'n/a'],
|
||||
fallback: false,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { keyValuesAtom } from '@yaakapp-internal/models';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { setKeyValue } from '../lib/keyValueStore';
|
||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { getKeyValue, keyValuesAtom } from './useKeyValue';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { getKeyValue } from './useKeyValue';
|
||||
|
||||
function kvKey(workspaceId: string | null) {
|
||||
return ['sidebar_collapsed', workspaceId ?? 'n/a'];
|
||||
@@ -22,7 +23,7 @@ export function useSidebarItemCollapsed(itemId: string) {
|
||||
|
||||
const toggle = useCallback(() => {
|
||||
setKeyValue({
|
||||
key: kvKey(getActiveWorkspaceId()),
|
||||
key: kvKey(jotaiStore.get(activeWorkspaceIdAtom)),
|
||||
namespace: 'no_sync',
|
||||
value: { ...getSidebarCollapsedMap(), [itemId]: !isCollapsed },
|
||||
}).catch(console.error);
|
||||
@@ -32,11 +33,8 @@ export function useSidebarItemCollapsed(itemId: string) {
|
||||
}
|
||||
|
||||
export function getSidebarCollapsedMap() {
|
||||
const activeWorkspaceId = getActiveWorkspaceId();
|
||||
if (activeWorkspaceId == null) return {};
|
||||
|
||||
const value = getKeyValue<Record<string, boolean>>({
|
||||
key: kvKey(activeWorkspaceId),
|
||||
key: kvKey(jotaiStore.get(activeWorkspaceIdAtom)),
|
||||
fallback: {},
|
||||
namespace: 'no_sync',
|
||||
});
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useLocalStorage } from 'react-use';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
|
||||
export function useSidebarWidth() {
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const activeWorkspaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const [width, setWidth] = useLocalStorage<number>(
|
||||
`sidebar_width::${activeWorkspace?.id ?? 'n/a'}`,
|
||||
`sidebar_width::${activeWorkspaceId ?? 'n/a'}`,
|
||||
250,
|
||||
);
|
||||
const resetWidth = useCallback(() => setWidth(250), [setWidth]);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
|
||||
/**
|
||||
* Like useState, except it will update the value when the default value changes
|
||||
*/
|
||||
export function useStateWithDeps<T>(defaultValue: T, deps: DependencyList) {
|
||||
export function useStateWithDeps<T>(defaultValue: T | (() => T), deps: DependencyList) {
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
useEffect(() => {
|
||||
setValue(defaultValue);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import { settingsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useEffect } from 'react';
|
||||
import { useSettings } from './useSettings';
|
||||
|
||||
export function useSyncFontSizeSetting() {
|
||||
const settings = useSettings();
|
||||
const settings = useAtomValue(settingsAtom);
|
||||
useEffect(() => {
|
||||
if (settings == null) {
|
||||
return;
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
import deepEqual from '@gilbarbara/deep-equal';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import type { AnyModel, KeyValue, ModelPayload } from '@yaakapp-internal/models';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { buildKeyValueKey } from '../lib/keyValueStore';
|
||||
import { modelsEq } from '../lib/model_util';
|
||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { cookieJarsAtom } from './useCookieJars';
|
||||
import { environmentsAtom } from './useEnvironments';
|
||||
import { foldersAtom } from './useFolders';
|
||||
import { grpcConnectionsAtom } from './useGrpcConnections';
|
||||
import { grpcEventsQueryKey } from './useGrpcEvents';
|
||||
import { grpcRequestsAtom } from './useGrpcRequests';
|
||||
import { httpRequestsAtom } from './useHttpRequests';
|
||||
import { httpResponsesAtom } from './useHttpResponses';
|
||||
import { keyValueQueryKey, keyValuesAtom } from './useKeyValue';
|
||||
import { useListenToTauriEvent } from './useListenToTauriEvent';
|
||||
import { pluginsAtom } from './usePlugins';
|
||||
import { useRequestUpdateKey } from './useRequestUpdateKey';
|
||||
import { settingsAtom } from './useSettings';
|
||||
import { websocketConnectionsAtom } from './useWebsocketConnections';
|
||||
import { websocketEventsQueryKey } from './useWebsocketEvents';
|
||||
import { websocketRequestsAtom } from './useWebsocketRequests';
|
||||
import { workspaceMetaAtom } from './useWorkspaceMeta';
|
||||
import { workspacesAtom } from './useWorkspaces';
|
||||
|
||||
export function useSyncModelStores() {
|
||||
const queryClient = useQueryClient();
|
||||
const { wasUpdatedExternally } = useRequestUpdateKey(null);
|
||||
|
||||
useListenToTauriEvent<ModelPayload>('upserted_model', ({ payload }) => {
|
||||
const queryKey =
|
||||
payload.model.model === 'grpc_event'
|
||||
? grpcEventsQueryKey(payload.model)
|
||||
: payload.model.model === 'websocket_event'
|
||||
? websocketEventsQueryKey(payload.model)
|
||||
: payload.model.model === 'key_value'
|
||||
? keyValueQueryKey(payload.model)
|
||||
: null;
|
||||
|
||||
// TODO: Move this logic to useRequestEditor() hook
|
||||
if (
|
||||
(payload.model.model === 'http_request' ||
|
||||
payload.model.model === 'grpc_request' ||
|
||||
payload.model.model === 'websocket_request') &&
|
||||
((payload.updateSource.type === 'window' &&
|
||||
payload.updateSource.label !== getCurrentWebviewWindow().label) ||
|
||||
payload.updateSource.type !== 'window')
|
||||
) {
|
||||
wasUpdatedExternally(payload.model.id);
|
||||
}
|
||||
|
||||
if (shouldIgnoreModel(payload)) return;
|
||||
|
||||
if (payload.model.model === 'workspace') {
|
||||
jotaiStore.set(workspacesAtom, updateModelList(payload.model));
|
||||
} else if (payload.model.model === 'workspace_meta') {
|
||||
jotaiStore.set(workspaceMetaAtom, payload.model);
|
||||
} else if (payload.model.model === 'plugin') {
|
||||
jotaiStore.set(pluginsAtom, updateModelList(payload.model));
|
||||
} else if (payload.model.model === 'http_request') {
|
||||
jotaiStore.set(httpRequestsAtom, updateModelList(payload.model));
|
||||
} else if (payload.model.model === 'folder') {
|
||||
jotaiStore.set(foldersAtom, updateModelList(payload.model));
|
||||
} else if (payload.model.model === 'http_response') {
|
||||
jotaiStore.set(httpResponsesAtom, updateModelList(payload.model));
|
||||
} else if (payload.model.model === 'grpc_request') {
|
||||
jotaiStore.set(grpcRequestsAtom, updateModelList(payload.model));
|
||||
} else if (payload.model.model === 'websocket_request') {
|
||||
jotaiStore.set(websocketRequestsAtom, updateModelList(payload.model));
|
||||
} else if (payload.model.model === 'websocket_connection') {
|
||||
jotaiStore.set(websocketConnectionsAtom, updateModelList(payload.model));
|
||||
} else if (payload.model.model === 'grpc_connection') {
|
||||
jotaiStore.set(grpcConnectionsAtom, updateModelList(payload.model));
|
||||
} else if (payload.model.model === 'environment') {
|
||||
jotaiStore.set(environmentsAtom, updateModelList(payload.model));
|
||||
} else if (payload.model.model === 'cookie_jar') {
|
||||
jotaiStore.set(cookieJarsAtom, updateModelList(payload.model));
|
||||
} else if (payload.model.model === 'settings') {
|
||||
jotaiStore.set(settingsAtom, payload.model);
|
||||
} else if (payload.model.model === 'key_value') {
|
||||
jotaiStore.set(keyValuesAtom, updateModelList(payload.model));
|
||||
} else if (queryKey != null) {
|
||||
// TODO: Convert all models to use Jotai
|
||||
queryClient.setQueryData(queryKey, (current: unknown) => {
|
||||
if (Array.isArray(current)) {
|
||||
return updateModelList(payload.model)(current);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
useListenToTauriEvent<ModelPayload>('deleted_model', ({ payload }) => {
|
||||
if (shouldIgnoreModel(payload)) return;
|
||||
|
||||
console.log('Delete model', payload);
|
||||
|
||||
if (payload.model.model === 'workspace') {
|
||||
jotaiStore.set(workspacesAtom, removeModelById(payload.model));
|
||||
} else if (payload.model.model === 'plugin') {
|
||||
jotaiStore.set(pluginsAtom, removeModelById(payload.model));
|
||||
} else if (payload.model.model === 'http_request') {
|
||||
jotaiStore.set(httpRequestsAtom, removeModelById(payload.model));
|
||||
} else if (payload.model.model === 'http_response') {
|
||||
jotaiStore.set(httpResponsesAtom, removeModelById(payload.model));
|
||||
} else if (payload.model.model === 'folder') {
|
||||
jotaiStore.set(foldersAtom, removeModelById(payload.model));
|
||||
} else if (payload.model.model === 'environment') {
|
||||
jotaiStore.set(environmentsAtom, removeModelById(payload.model));
|
||||
} else if (payload.model.model === 'grpc_request') {
|
||||
jotaiStore.set(grpcRequestsAtom, removeModelById(payload.model));
|
||||
} else if (payload.model.model === 'websocket_request') {
|
||||
jotaiStore.set(websocketRequestsAtom, removeModelById(payload.model));
|
||||
} else if (payload.model.model === 'websocket_connection') {
|
||||
jotaiStore.set(websocketConnectionsAtom, removeModelById(payload.model));
|
||||
} else if (payload.model.model === 'websocket_event') {
|
||||
queryClient.setQueryData(
|
||||
websocketEventsQueryKey(payload.model),
|
||||
removeModelById(payload.model),
|
||||
);
|
||||
} else if (payload.model.model === 'grpc_connection') {
|
||||
jotaiStore.set(grpcConnectionsAtom, removeModelById(payload.model));
|
||||
} else if (payload.model.model === 'grpc_event') {
|
||||
queryClient.setQueryData(grpcEventsQueryKey(payload.model), removeModelById(payload.model));
|
||||
} else if (payload.model.model === 'key_value') {
|
||||
queryClient.setQueryData(keyValueQueryKey(payload.model), removeModelByKv(payload.model));
|
||||
} else if (payload.model.model === 'cookie_jar') {
|
||||
jotaiStore.set(cookieJarsAtom, removeModelById(payload.model));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateModelList<T extends AnyModel>(model: T) {
|
||||
// Mark these models as DESC instead of ASC
|
||||
const pushToFront =
|
||||
model.model === 'http_response' ||
|
||||
model.model === 'grpc_connection' ||
|
||||
model.model === 'websocket_connection';
|
||||
|
||||
return (current: T[] | undefined | null): T[] => {
|
||||
const index = current?.findIndex((v) => modelsEq(v, model)) ?? -1;
|
||||
const existingModel = current?.[index];
|
||||
if (existingModel && deepEqual(existingModel, model)) {
|
||||
// We already have the exact model, so do nothing
|
||||
return current;
|
||||
} else if (existingModel) {
|
||||
return [...(current ?? []).slice(0, index), model, ...(current ?? []).slice(index + 1)];
|
||||
} else {
|
||||
return pushToFront ? [model, ...(current ?? [])] : [...(current ?? []), model];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function removeModelById<T extends { id: string }>(model: T) {
|
||||
return (prevEntries: T[] | undefined) => {
|
||||
const entries = prevEntries?.filter((e) => e.id !== model.id) ?? [];
|
||||
|
||||
// Don't trigger an update if we didn't remove anything
|
||||
if (entries.length === (prevEntries ?? []).length) {
|
||||
return prevEntries ?? [];
|
||||
}
|
||||
|
||||
return entries;
|
||||
};
|
||||
}
|
||||
|
||||
function removeModelByKv(model: KeyValue) {
|
||||
return (prevEntries: KeyValue[] | undefined) =>
|
||||
prevEntries?.filter(
|
||||
(e) =>
|
||||
!(
|
||||
e.namespace === model.namespace &&
|
||||
buildKeyValueKey(e.key) === buildKeyValueKey(model.key) &&
|
||||
e.value == model.value
|
||||
),
|
||||
) ?? [];
|
||||
}
|
||||
|
||||
function shouldIgnoreModel({ model, updateSource }: ModelPayload) {
|
||||
// Never ignore updates from non-user sources
|
||||
if (updateSource.type !== 'window') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Never ignore same-window updates
|
||||
if (updateSource.label === getCurrentWebviewWindow().label) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const activeWorkspaceId = getActiveWorkspaceId();
|
||||
// Only sync models that belong to this workspace, if a workspace ID is present
|
||||
if ('workspaceId' in model && model.workspaceId !== activeWorkspaceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (model.model === 'key_value') {
|
||||
return model.namespace === 'no_sync';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,19 +1,7 @@
|
||||
import {listWebsocketConnections, listWebsocketRequests} from '@yaakapp-internal/ws';
|
||||
import { changeModelStoreWorkspace } from '@yaakapp-internal/models';
|
||||
import { useEffect } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { activeWorkspaceIdAtom, getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { cookieJarsAtom } from './useCookieJars';
|
||||
import { environmentsAtom } from './useEnvironments';
|
||||
import { foldersAtom } from './useFolders';
|
||||
import { grpcConnectionsAtom } from './useGrpcConnections';
|
||||
import { grpcRequestsAtom } from './useGrpcRequests';
|
||||
import { httpRequestsAtom } from './useHttpRequests';
|
||||
import { httpResponsesAtom } from './useHttpResponses';
|
||||
import { keyValuesAtom } from './useKeyValue';
|
||||
import {websocketConnectionsAtom} from "./useWebsocketConnections";
|
||||
import { websocketRequestsAtom } from './useWebsocketRequests';
|
||||
import { workspaceMetaAtom } from './useWorkspaceMeta';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
|
||||
export function useSyncWorkspaceChildModels() {
|
||||
useEffect(() => {
|
||||
@@ -24,27 +12,6 @@ export function useSyncWorkspaceChildModels() {
|
||||
}
|
||||
|
||||
async function sync() {
|
||||
// Doesn't need a workspace ID, so sync it right away
|
||||
jotaiStore.set(keyValuesAtom, await invokeCmd('cmd_list_key_values'));
|
||||
|
||||
const workspaceId = getActiveWorkspaceId();
|
||||
if (workspaceId == null) return;
|
||||
|
||||
const args = { workspaceId };
|
||||
|
||||
// Set the things we need first, first
|
||||
jotaiStore.set(httpRequestsAtom, await invokeCmd('cmd_list_http_requests', args));
|
||||
jotaiStore.set(grpcRequestsAtom, await invokeCmd('cmd_list_grpc_requests', args));
|
||||
jotaiStore.set(foldersAtom, await invokeCmd('cmd_list_folders', args));
|
||||
jotaiStore.set(websocketRequestsAtom, await listWebsocketRequests(args));
|
||||
|
||||
// Then, set the rest
|
||||
jotaiStore.set(cookieJarsAtom, await invokeCmd('cmd_list_cookie_jars', args));
|
||||
jotaiStore.set(httpResponsesAtom, await invokeCmd('cmd_list_http_responses', args));
|
||||
jotaiStore.set(grpcConnectionsAtom, await invokeCmd('cmd_list_grpc_connections', args));
|
||||
jotaiStore.set(websocketConnectionsAtom, await listWebsocketConnections(args));
|
||||
jotaiStore.set(environmentsAtom, await invokeCmd('cmd_list_environments', args));
|
||||
|
||||
// Single models
|
||||
jotaiStore.set(workspaceMetaAtom, await invokeCmd('cmd_get_workspace_meta', { workspaceId }));
|
||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom) ?? null;
|
||||
changeModelStoreWorkspace(workspaceId).catch(console.error);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { emit } from '@tauri-apps/api/event';
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useEffect } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { resolvedModelName } from '../lib/resolvedModelName';
|
||||
import { useActiveEnvironment } from './useActiveEnvironment';
|
||||
import { getActiveRequest } from './useActiveRequest';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { activeRequestAtom } from './useActiveRequest';
|
||||
import { activeWorkspaceAtom } from './useActiveWorkspace';
|
||||
import { useAppInfo } from './useAppInfo';
|
||||
import { useOsInfo } from './useOsInfo';
|
||||
|
||||
export function useSyncWorkspaceRequestTitle() {
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const activeWorkspace = useAtomValue(activeWorkspaceAtom);
|
||||
const activeEnvironment = useActiveEnvironment();
|
||||
const osInfo = useOsInfo();
|
||||
const appInfo = useAppInfo();
|
||||
@@ -23,7 +25,7 @@ export function useSyncWorkspaceRequestTitle() {
|
||||
if (activeEnvironment) {
|
||||
newTitle += ` [${activeEnvironment.name}]`;
|
||||
}
|
||||
const activeRequest = getActiveRequest();
|
||||
const activeRequest = jotaiStore.get(activeRequestAtom);
|
||||
if (activeRequest) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
newTitle += ` › ${resolvedModelName(activeRequest)}`;
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import type { Folder } from '@yaakapp-internal/models';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { getFolder } from './useFolders';
|
||||
|
||||
export function useUpdateAnyFolder() {
|
||||
return useFastMutation<Folder, unknown, { id: string; update: (r: Folder) => Folder }>({
|
||||
mutationKey: ['update_any_folder'],
|
||||
mutationFn: async ({ id, update }) => {
|
||||
const folder = getFolder(id);
|
||||
if (folder === null) {
|
||||
throw new Error("Can't update a null folder");
|
||||
}
|
||||
|
||||
return invokeCmd<Folder>('cmd_update_folder', { folder: update(folder) });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { getGrpcRequest } from './useGrpcRequests';
|
||||
|
||||
export function useUpdateAnyGrpcRequest() {
|
||||
return useFastMutation<
|
||||
GrpcRequest,
|
||||
unknown,
|
||||
{ id: string; update: Partial<GrpcRequest> | ((r: GrpcRequest) => GrpcRequest) }
|
||||
>({
|
||||
mutationKey: ['update_any_grpc_request'],
|
||||
mutationFn: async ({ id, update }) => {
|
||||
const request = getGrpcRequest(id);
|
||||
if (request === null) {
|
||||
throw new Error("Can't update a null request");
|
||||
}
|
||||
|
||||
const patchedRequest =
|
||||
typeof update === 'function' ? update(request) : { ...request, ...update };
|
||||
return invokeCmd<GrpcRequest>('cmd_update_grpc_request', { request: patchedRequest });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||
import { upsertAnyModel } from '@yaakapp-internal/models';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { getHttpRequest } from './useHttpRequests';
|
||||
|
||||
export function useUpdateAnyHttpRequest() {
|
||||
return useFastMutation<
|
||||
void,
|
||||
unknown,
|
||||
{ id: string; update: Partial<HttpRequest> | ((r: HttpRequest) => HttpRequest) }
|
||||
>({
|
||||
mutationKey: ['update_any_http_request'],
|
||||
mutationFn: async ({ id, update }) => {
|
||||
const request = getHttpRequest(id);
|
||||
if (request === null) {
|
||||
throw new Error("Can't update a null request");
|
||||
}
|
||||
|
||||
const patchedRequest =
|
||||
typeof update === 'function' ? update(request) : { ...request, ...update };
|
||||
await upsertAnyModel(patchedRequest);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import type { CookieJar } from '@yaakapp-internal/models';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getCookieJar } from './useCookieJars';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useUpdateCookieJar(id: string | null) {
|
||||
return useFastMutation<CookieJar, unknown, Partial<CookieJar> | ((j: CookieJar) => CookieJar)>({
|
||||
mutationKey: ['update_cookie_jar', id],
|
||||
mutationFn: async (v) => {
|
||||
const cookieJar = getCookieJar(id);
|
||||
if (cookieJar == null) {
|
||||
throw new Error("Can't update a null workspace");
|
||||
}
|
||||
|
||||
const newCookieJar = typeof v === 'function' ? v(cookieJar) : { ...cookieJar, ...v };
|
||||
return invokeCmd<CookieJar>('cmd_update_cookie_jar', { cookieJar: newCookieJar });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { Environment } from '@yaakapp-internal/models';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { getEnvironment } from './useEnvironments';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useUpdateEnvironment(id: string | null) {
|
||||
return useFastMutation<
|
||||
Environment,
|
||||
unknown,
|
||||
Partial<Environment> | ((r: Environment) => Environment)
|
||||
>({
|
||||
mutationKey: ['update_environment', id],
|
||||
mutationFn: async (v) => {
|
||||
const environment = getEnvironment(id);
|
||||
if (environment == null) {
|
||||
throw new Error("Can't update a null environment");
|
||||
}
|
||||
|
||||
const newEnvironment = typeof v === 'function' ? v(environment) : { ...environment, ...v };
|
||||
return invokeCmd<Environment>('cmd_update_environment', { environment: newEnvironment });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import type { Settings } from '@yaakapp-internal/models';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { getSettings, settingsAtom } from './useSettings';
|
||||
|
||||
export function useUpdateSettings() {
|
||||
const setSettings = useSetAtom(settingsAtom);
|
||||
return useFastMutation<Settings, unknown, Partial<Settings>>({
|
||||
mutationKey: ['update_settings'],
|
||||
mutationFn: async (patch) => {
|
||||
const settings = getSettings();
|
||||
const newSettings: Settings = { ...settings, ...patch };
|
||||
return invokeCmd<Settings>('cmd_update_settings', { settings: newSettings });
|
||||
},
|
||||
onSuccess: (settings) => {
|
||||
setSettings(settings);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import type { WebsocketConnection } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
|
||||
export const websocketConnectionsAtom = atom<WebsocketConnection[]>([]);
|
||||
|
||||
export function useWebsocketConnections() {
|
||||
return useAtomValue(websocketConnectionsAtom);
|
||||
}
|
||||
|
||||
export function useLatestWebsocketConnection(requestId: string | null): WebsocketConnection | null {
|
||||
return useWebsocketConnections().find((r) => r.requestId === requestId) ?? null;
|
||||
}
|
||||
|
||||
export function getWebsocketConnection(id: string) {
|
||||
return jotaiStore.get(websocketConnectionsAtom).find((r) => r.id === id) ?? null;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { WebsocketEvent } from '@yaakapp-internal/models';
|
||||
import { listWebsocketEvents } from '@yaakapp-internal/ws';
|
||||
|
||||
export function websocketEventsQueryKey({ connectionId }: { connectionId: string }) {
|
||||
return ['websocket_events', { connectionId }];
|
||||
}
|
||||
|
||||
export function useWebsocketEvents(connectionId: string | null) {
|
||||
return (
|
||||
useQuery<WebsocketEvent[]>({
|
||||
enabled: connectionId !== null,
|
||||
initialData: [],
|
||||
queryKey: websocketEventsQueryKey({ connectionId: connectionId ?? 'n/a' }),
|
||||
queryFn: () => {
|
||||
if (connectionId == null) return [] as WebsocketEvent[];
|
||||
return listWebsocketEvents({ connectionId });
|
||||
},
|
||||
}).data ?? []
|
||||
);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { WebsocketRequest } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
|
||||
export const websocketRequestsAtom = atom<WebsocketRequest[]>([]);
|
||||
|
||||
export function useWebsocketRequests() {
|
||||
return useAtomValue(websocketRequestsAtom);
|
||||
}
|
||||
|
||||
export function getWebsocketRequest(id: string) {
|
||||
return jotaiStore.get(websocketRequestsAtom).find((r) => r.id === id) ?? null;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { WorkspaceMeta } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
|
||||
export const workspaceMetaAtom = atom<WorkspaceMeta | null>(null);
|
||||
|
||||
export function useWorkspaceMeta() {
|
||||
return useAtomValue(workspaceMetaAtom);
|
||||
}
|
||||
|
||||
export function getWorkspaceMeta() {
|
||||
return jotaiStore.get(workspaceMetaAtom);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import type { Workspace } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
|
||||
export const workspacesAtom = atom<Workspace[]>(
|
||||
await invokeCmd<Workspace[]>('cmd_list_workspaces'),
|
||||
);
|
||||
|
||||
export const sortedWorkspacesAtom = atom((get) =>
|
||||
get(workspacesAtom).sort((a, b) => a.name.localeCompare(b.name)),
|
||||
);
|
||||
|
||||
export function useWorkspaces() {
|
||||
return useAtomValue(sortedWorkspacesAtom);
|
||||
}
|
||||
|
||||
export function getWorkspace(id: string | null) {
|
||||
return jotaiStore.get(workspacesAtom).find((v) => v.id === id) ?? null;
|
||||
}
|
||||
@@ -1,28 +1,27 @@
|
||||
import { patchModel, settingsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { useSettings } from './useSettings';
|
||||
import { useUpdateSettings } from './useUpdateSettings';
|
||||
|
||||
export function useZoom() {
|
||||
const settings = useSettings();
|
||||
const updateSettings = useUpdateSettings();
|
||||
const settings = useAtomValue(settingsAtom);
|
||||
|
||||
const zoomIn = useCallback(() => {
|
||||
const zoomIn = useCallback(async () => {
|
||||
if (!settings) return;
|
||||
updateSettings.mutate({
|
||||
await patchModel(settings, {
|
||||
interfaceScale: Math.min(1.8, settings.interfaceScale * 1.1),
|
||||
});
|
||||
}, [settings, updateSettings]);
|
||||
}, [settings]);
|
||||
|
||||
const zoomOut = useCallback(() => {
|
||||
const zoomOut = useCallback(async () => {
|
||||
if (!settings) return;
|
||||
updateSettings.mutate({
|
||||
await patchModel(settings, {
|
||||
interfaceScale: Math.max(0.4, settings.interfaceScale * 0.9),
|
||||
});
|
||||
}, [settings, updateSettings]);
|
||||
}, [settings]);
|
||||
|
||||
const zoomReset = useCallback(() => {
|
||||
updateSettings.mutate({ ...settings, interfaceScale: 1 });
|
||||
}, [settings, updateSettings]);
|
||||
const zoomReset = useCallback(async () => {
|
||||
await patchModel(settings, { interfaceScale: 1 });
|
||||
}, [settings]);
|
||||
|
||||
return { zoomIn, zoomOut, zoomReset };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user