Run oxfmt across repo, add format script and docs

Add .oxfmtignore to skip generated bindings and wasm-pack output.
Add npm format script, update DEVELOPMENT.md for Vite+ toolchain,
and format all non-generated files with oxfmt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Gregory Schier
2026-03-13 10:15:49 -07:00
parent 45262edfbd
commit b4a1c418bb
664 changed files with 13638 additions and 13492 deletions

View File

@@ -1,10 +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';
import { useEffect } from 'react';
import { jotaiStore } from '../lib/jotai';
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
import { useSearch } from "@tanstack/react-router";
import type { CookieJar } from "@yaakapp-internal/models";
import { cookieJarsAtom } from "@yaakapp-internal/models";
import { atom, useAtomValue } from "jotai";
import { useEffect } from "react";
import { jotaiStore } from "../lib/jotai";
import { setWorkspaceSearchParams } from "../lib/setWorkspaceSearchParams";
export const activeCookieJarAtom = atom<CookieJar | null>(null);
@@ -30,7 +30,7 @@ export function getActiveCookieJar() {
export function useEnsureActiveCookieJar() {
const cookieJars = useAtomValue(cookieJarsAtom);
const { cookie_jar_id: activeCookieJarId } = useSearch({ from: '/workspaces/$workspaceId/' });
const { cookie_jar_id: activeCookieJarId } = useSearch({ from: "/workspaces/$workspaceId/" });
// Set the active cookie jar to the first one, if none set
// NOTE: We only run this on cookieJars to prevent data races when switching workspaces since a lot of
@@ -52,7 +52,7 @@ export function useEnsureActiveCookieJar() {
}
// There's no active jar, so set it to the first one
console.log('Defaulting active cookie jar to first jar', firstJar);
console.log("Defaulting active cookie jar to first jar", firstJar);
setWorkspaceSearchParams({ cookie_jar_id: firstJar.id });
}, [cookieJars]);
}

View File

@@ -1,9 +1,9 @@
import { useSearch } from '@tanstack/react-router';
import type { Environment } from '@yaakapp-internal/models';
import { environmentsAtom } from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
import { useEffect } from 'react';
import { jotaiStore } from '../lib/jotai';
import { useSearch } from "@tanstack/react-router";
import type { Environment } from "@yaakapp-internal/models";
import { environmentsAtom } from "@yaakapp-internal/models";
import { atom, useAtomValue } from "jotai";
import { useEffect } from "react";
import { jotaiStore } from "../lib/jotai";
export const activeEnvironmentIdAtom = atom<string>();

View File

@@ -1,6 +1,6 @@
import { useAtomValue } from 'jotai';
import { activeEnvironmentAtom } from './useActiveEnvironment';
import { useEnvironmentVariables } from './useEnvironmentVariables';
import { useAtomValue } from "jotai";
import { activeEnvironmentAtom } from "./useActiveEnvironment";
import { useEnvironmentVariables } from "./useEnvironmentVariables";
export function useActiveEnvironmentVariables() {
const activeEnvironment = useAtomValue(activeEnvironmentAtom);

View File

@@ -1,6 +1,6 @@
import { foldersAtom } from '@yaakapp-internal/models';
import { atom } from 'jotai';
import { activeFolderIdAtom } from './useActiveFolderId';
import { foldersAtom } from "@yaakapp-internal/models";
import { atom } from "jotai";
import { activeFolderIdAtom } from "./useActiveFolderId";
export const activeFolderAtom = atom((get) => {
const activeFolderId = get(activeFolderIdAtom);

View File

@@ -1,7 +1,7 @@
import { useSearch } from '@tanstack/react-router';
import { atom } from 'jotai';
import { useEffect } from 'react';
import { jotaiStore } from '../lib/jotai';
import { useSearch } from "@tanstack/react-router";
import { atom } from "jotai";
import { useEffect } from "react";
import { jotaiStore } from "../lib/jotai";
export const activeFolderIdAtom = atom<string | null>(null);

View File

@@ -1,7 +1,7 @@
import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
import { activeRequestIdAtom } from './useActiveRequestId';
import { allRequestsAtom } from './useAllRequests';
import type { GrpcRequest, HttpRequest, WebsocketRequest } from "@yaakapp-internal/models";
import { atom, useAtomValue } from "jotai";
import { activeRequestIdAtom } from "./useActiveRequestId";
import { allRequestsAtom } from "./useAllRequests";
export const activeRequestAtom = atom((get) => {
const activeRequestId = get(activeRequestIdAtom);
@@ -15,9 +15,7 @@ interface TypeMap {
websocket_request: WebsocketRequest;
}
export function useActiveRequest<T extends keyof TypeMap>(
model?: T,
): TypeMap[T] | null {
export function useActiveRequest<T extends keyof TypeMap>(model?: T): TypeMap[T] | null {
const activeRequest = useAtomValue(activeRequestAtom);
if (model == null) return activeRequest as TypeMap[T];
if (activeRequest?.model === model) return activeRequest as TypeMap[T];

View File

@@ -1,7 +1,7 @@
import { useSearch } from '@tanstack/react-router';
import { atom } from 'jotai';
import { useEffect } from 'react';
import { jotaiStore } from '../lib/jotai';
import { useSearch } from "@tanstack/react-router";
import { atom } from "jotai";
import { useEffect } from "react";
import { jotaiStore } from "../lib/jotai";
export const activeRequestIdAtom = atom<string | null>(null);

View File

@@ -1,8 +1,8 @@
import { useParams } from '@tanstack/react-router';
import { workspaceMetasAtom, workspacesAtom } from '@yaakapp-internal/models';
import { atom } from 'jotai';
import { useEffect } from 'react';
import { jotaiStore } from '../lib/jotai';
import { useParams } from "@tanstack/react-router";
import { workspaceMetasAtom, workspacesAtom } from "@yaakapp-internal/models";
import { atom } from "jotai";
import { useEffect } from "react";
import { jotaiStore } from "../lib/jotai";
export const activeWorkspaceIdAtom = atom<string | null>(null);

View File

@@ -1,8 +1,8 @@
import { useAtomValue } from 'jotai';
import { useEffect, useState } from 'react';
import { InlineCode } from '../components/core/InlineCode';
import { showToast } from '../lib/toast';
import { activeWorkspaceAtom } from './useActiveWorkspace';
import { useAtomValue } from "jotai";
import { useEffect, useState } from "react";
import { InlineCode } from "../components/core/InlineCode";
import { showToast } from "../lib/toast";
import { activeWorkspaceAtom } from "./useActiveWorkspace";
export function useActiveWorkspaceChangedToast() {
const activeWorkspace = useAtomValue(activeWorkspaceAtom);
@@ -22,7 +22,7 @@ export function useActiveWorkspaceChangedToast() {
timeout: 3000,
message: (
<>
Activated workspace{' '}
Activated workspace{" "}
<InlineCode className="whitespace-nowrap">{activeWorkspace.name}</InlineCode>
</>
),

View File

@@ -2,8 +2,8 @@ import {
grpcRequestsAtom,
httpRequestsAtom,
websocketRequestsAtom,
} from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
} from "@yaakapp-internal/models";
import { atom, useAtomValue } from "jotai";
export const allRequestsAtom = atom((get) => [
...get(httpRequestsAtom),

View File

@@ -1,20 +1,20 @@
import type { Folder } from '@yaakapp-internal/models';
import { modelTypeLabel, patchModel } from '@yaakapp-internal/models';
import { useMemo } from 'react';
import { openFolderSettings } from '../commands/openFolderSettings';
import { openWorkspaceSettings } from '../commands/openWorkspaceSettings';
import { Icon } from '../components/core/Icon';
import { IconTooltip } from '../components/core/IconTooltip';
import { InlineCode } from '../components/core/InlineCode';
import { HStack } from '../components/core/Stacks';
import type { TabItem } from '../components/core/Tabs/Tabs';
import { capitalize } from '../lib/capitalize';
import { showConfirm } from '../lib/confirm';
import { resolvedModelName } from '../lib/resolvedModelName';
import { useHttpAuthenticationSummaries } from './useHttpAuthentication';
import type { AuthenticatedModel } from './useInheritedAuthentication';
import { useInheritedAuthentication } from './useInheritedAuthentication';
import { useModelAncestors } from './useModelAncestors';
import type { Folder } from "@yaakapp-internal/models";
import { modelTypeLabel, patchModel } from "@yaakapp-internal/models";
import { useMemo } from "react";
import { openFolderSettings } from "../commands/openFolderSettings";
import { openWorkspaceSettings } from "../commands/openWorkspaceSettings";
import { Icon } from "../components/core/Icon";
import { IconTooltip } from "../components/core/IconTooltip";
import { InlineCode } from "../components/core/InlineCode";
import { HStack } from "../components/core/Stacks";
import type { TabItem } from "../components/core/Tabs/Tabs";
import { capitalize } from "../lib/capitalize";
import { showConfirm } from "../lib/confirm";
import { resolvedModelName } from "../lib/resolvedModelName";
import { useHttpAuthenticationSummaries } from "./useHttpAuthentication";
import type { AuthenticatedModel } from "./useInheritedAuthentication";
import { useInheritedAuthentication } from "./useInheritedAuthentication";
import { useModelAncestors } from "./useModelAncestors";
export function useAuthTab<T extends string>(tabValue: T, model: AuthenticatedModel | null) {
const authentication = useHttpAuthenticationSummaries();
@@ -27,23 +27,23 @@ export function useAuthTab<T extends string>(tabValue: T, model: AuthenticatedMo
const tab: TabItem = {
value: tabValue,
label: 'Auth',
label: "Auth",
options: {
value: model.authenticationType,
items: [
...authentication.map((a) => ({
label: a.label || 'UNKNOWN',
label: a.label || "UNKNOWN",
shortLabel: a.shortLabel,
value: a.name,
})),
{ type: 'separator' },
{ type: "separator" },
{
label: 'Inherit from Parent',
label: "Inherit from Parent",
shortLabel:
inheritedAuth != null && inheritedAuth.authenticationType !== 'none' ? (
inheritedAuth != null && inheritedAuth.authenticationType !== "none" ? (
<HStack space={1.5}>
{authentication.find((a) => a.name === inheritedAuth.authenticationType)
?.shortLabel ?? 'UNKNOWN'}
?.shortLabel ?? "UNKNOWN"}
<IconTooltip
icon="magic_wand"
iconSize="xs"
@@ -51,15 +51,15 @@ export function useAuthTab<T extends string>(tabValue: T, model: AuthenticatedMo
/>
</HStack>
) : (
'Auth'
"Auth"
),
value: null,
},
{ label: 'No Auth', shortLabel: 'No Auth', value: 'none' },
{ label: "No Auth", shortLabel: "No Auth", value: "none" },
],
itemsAfter: (() => {
const actions: (
| { type: 'separator'; label: string }
| { type: "separator"; label: string }
| { label: string; leftSlot: React.ReactNode; onSelect: () => Promise<void> }
)[] = [];
@@ -67,26 +67,26 @@ export function useAuthTab<T extends string>(tabValue: T, model: AuthenticatedMo
if (
parentModel &&
model.authenticationType &&
model.authenticationType !== 'none' &&
(parentModel.authenticationType == null || parentModel.authenticationType === 'none')
model.authenticationType !== "none" &&
(parentModel.authenticationType == null || parentModel.authenticationType === "none")
) {
actions.push(
{ type: 'separator', label: 'Actions' },
{ type: "separator", label: "Actions" },
{
label: `Promote to ${capitalize(parentModel.model)}`,
leftSlot: (
<Icon
icon={parentModel.model === 'workspace' ? 'corner_right_up' : 'folder_up'}
icon={parentModel.model === "workspace" ? "corner_right_up" : "folder_up"}
/>
),
onSelect: async () => {
const confirmed = await showConfirm({
id: 'promote-auth-confirm',
title: 'Promote Authentication',
confirmText: 'Promote',
id: "promote-auth-confirm",
title: "Promote Authentication",
confirmText: "Promote",
description: (
<>
Move authentication config to{' '}
Move authentication config to{" "}
<InlineCode>{resolvedModelName(parentModel)}</InlineCode>?
</>
),
@@ -98,10 +98,10 @@ export function useAuthTab<T extends string>(tabValue: T, model: AuthenticatedMo
authenticationType: model.authenticationType,
});
if (parentModel.model === 'folder') {
openFolderSettings(parentModel.id, 'auth');
if (parentModel.model === "folder") {
openFolderSettings(parentModel.id, "auth");
} else {
openWorkspaceSettings('auth');
openWorkspaceSettings("auth");
}
}
},
@@ -111,33 +111,33 @@ export function useAuthTab<T extends string>(tabValue: T, model: AuthenticatedMo
// Copy from ancestor: copy auth config down to current model
const ancestorWithAuth = ancestors.find(
(a) => a.authenticationType != null && a.authenticationType !== 'none',
(a) => a.authenticationType != null && a.authenticationType !== "none",
);
if (ancestorWithAuth) {
if (actions.length === 0) {
actions.push({ type: 'separator', label: 'Actions' });
actions.push({ type: "separator", label: "Actions" });
}
actions.push({
label: `Copy from ${modelTypeLabel(ancestorWithAuth)}`,
leftSlot: (
<Icon
icon={
ancestorWithAuth.model === 'workspace' ? 'corner_right_down' : 'folder_down'
ancestorWithAuth.model === "workspace" ? "corner_right_down" : "folder_down"
}
/>
),
onSelect: async () => {
const confirmed = await showConfirm({
id: 'copy-auth-confirm',
title: 'Copy Authentication',
confirmText: 'Copy',
id: "copy-auth-confirm",
title: "Copy Authentication",
confirmText: "Copy",
description: (
<>
Copy{' '}
Copy{" "}
{authentication.find((a) => a.name === ancestorWithAuth.authenticationType)
?.label ?? 'authentication'}{' '}
?.label ?? "authentication"}{" "}
config from <InlineCode>{resolvedModelName(ancestorWithAuth)}</InlineCode>?
This will override the current authentication but will not affect the{' '}
This will override the current authentication but will not affect the{" "}
{modelTypeLabel(ancestorWithAuth).toLowerCase()}.
</>
),
@@ -155,7 +155,7 @@ export function useAuthTab<T extends string>(tabValue: T, model: AuthenticatedMo
return actions.length > 0 ? actions : undefined;
})(),
onChange: async (authenticationType) => {
let authentication: Folder['authentication'] = model.authentication;
let authentication: Folder["authentication"] = model.authentication;
if (model.authenticationType !== authenticationType) {
authentication = {
// Reset auth if changing types

View File

@@ -1,9 +1,9 @@
import { event } from '@tauri-apps/api';
import { useFastMutation } from './useFastMutation';
import { event } from "@tauri-apps/api";
import { useFastMutation } from "./useFastMutation";
export function useCancelHttpResponse(id: string | null) {
return useFastMutation<void>({
mutationKey: ['cancel_http_response', id],
mutationKey: ["cancel_http_response", id],
mutationFn: () => event.emit(`cancel_http_response_${id}`),
});
}

View File

@@ -1,19 +1,19 @@
import { useMutation } from '@tanstack/react-query';
import { InlineCode } from '../components/core/InlineCode';
import { showAlert } from '../lib/alert';
import { appInfo } from '../lib/appInfo';
import { minPromiseMillis } from '../lib/minPromiseMillis';
import { invokeCmd } from '../lib/tauri';
import { useMutation } from "@tanstack/react-query";
import { InlineCode } from "../components/core/InlineCode";
import { showAlert } from "../lib/alert";
import { appInfo } from "../lib/appInfo";
import { minPromiseMillis } from "../lib/minPromiseMillis";
import { invokeCmd } from "../lib/tauri";
export function useCheckForUpdates() {
return useMutation({
mutationKey: ['check_for_updates'],
mutationKey: ["check_for_updates"],
mutationFn: async () => {
const hasUpdate: boolean = await minPromiseMillis(invokeCmd('cmd_check_for_updates'), 500);
const hasUpdate: boolean = await minPromiseMillis(invokeCmd("cmd_check_for_updates"), 500);
if (!hasUpdate) {
showAlert({
id: 'no-updates',
title: 'No Update Available',
id: "no-updates",
title: "No Update Available",
body: (
<>
You are currently on the latest version <InlineCode>{appInfo.version}</InlineCode>

View File

@@ -1,5 +1,5 @@
import type { RefObject } from 'react';
import { useEffect, useRef } from 'react';
import type { RefObject } from "react";
import { useEffect, useRef } from "react";
/**
* Get notified when a mouse click happens outside the target ref
@@ -31,11 +31,11 @@ export function useClickOutside(
};
// NOTE: We're using mousedown instead of click to handle some edge cases like when a context
// menu is open with the ctrl key.
document.addEventListener('mousedown', handler, { capture: true });
document.addEventListener('contextmenu', handler, { capture: true });
document.addEventListener("mousedown", handler, { capture: true });
document.addEventListener("contextmenu", handler, { capture: true });
return () => {
document.removeEventListener('mousedown', handler);
document.removeEventListener('contextmenu', handler);
document.removeEventListener("mousedown", handler);
document.removeEventListener("contextmenu", handler);
};
}, [ignored, ref]);
}

View File

@@ -1,5 +1,5 @@
import type { RefObject } from 'react';
import { useLayoutEffect, useState } from 'react';
import type { RefObject } from "react";
import { useLayoutEffect, useState } from "react";
export function useContainerSize(ref: RefObject<HTMLElement | null>) {
const [size, setSize] = useState<{ width: number; height: number }>({ width: 0, height: 0 });

View File

@@ -1,11 +1,11 @@
import type { HttpResponse } from '@yaakapp-internal/models';
import { copyToClipboard } from '../lib/copy';
import { getResponseBodyText } from '../lib/responseBody';
import { useFastMutation } from './useFastMutation';
import type { HttpResponse } from "@yaakapp-internal/models";
import { copyToClipboard } from "../lib/copy";
import { getResponseBodyText } from "../lib/responseBody";
import { useFastMutation } from "./useFastMutation";
export function useCopyHttpResponse(response: HttpResponse) {
return useFastMutation({
mutationKey: ['copy_http_response', response.id],
mutationKey: ["copy_http_response", response.id],
async mutationFn() {
const body = await getResponseBodyText({ response, filter: null });
copyToClipboard(body);

View File

@@ -1,13 +1,13 @@
import { createWorkspaceModel } from '@yaakapp-internal/models';
import { jotaiStore } from '../lib/jotai';
import { showPrompt } from '../lib/prompt';
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
import { useFastMutation } from './useFastMutation';
import { createWorkspaceModel } from "@yaakapp-internal/models";
import { jotaiStore } from "../lib/jotai";
import { showPrompt } from "../lib/prompt";
import { setWorkspaceSearchParams } from "../lib/setWorkspaceSearchParams";
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
import { useFastMutation } from "./useFastMutation";
export function useCreateCookieJar() {
return useFastMutation({
mutationKey: ['create_cookie_jar'],
mutationKey: ["create_cookie_jar"],
mutationFn: async () => {
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
if (workspaceId == null) {
@@ -15,16 +15,16 @@ export function useCreateCookieJar() {
}
const name = await showPrompt({
id: 'new-cookie-jar',
title: 'New CookieJar',
placeholder: 'My Jar',
confirmText: 'Create',
label: 'Name',
defaultValue: 'My Jar',
id: "new-cookie-jar",
title: "New CookieJar",
placeholder: "My Jar",
confirmText: "Create",
label: "Name",
defaultValue: "My Jar",
});
if (name == null) return null;
return createWorkspaceModel({ model: 'cookie_jar', workspaceId, name });
return createWorkspaceModel({ model: "cookie_jar", workspaceId, name });
},
onSuccess: async (cookieJarId) => {
setWorkspaceSearchParams({ cookie_jar_id: cookieJarId });

View File

@@ -1,15 +1,15 @@
import type { HttpRequest, WebsocketRequest } from '@yaakapp-internal/models';
import type { GrpcRequest } from '@yaakapp-internal/sync';
import { useAtomValue } from 'jotai';
import { useMemo } from 'react';
import { createFolder } from '../commands/commands';
import type { DropdownItem } from '../components/core/Dropdown';
import { Icon } from '../components/core/Icon';
import { createRequestAndNavigate } from '../lib/createRequestAndNavigate';
import { generateId } from '../lib/generateId';
import { BODY_TYPE_GRAPHQL } from '../lib/model_util';
import { activeRequestAtom } from './useActiveRequest';
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
import type { HttpRequest, WebsocketRequest } from "@yaakapp-internal/models";
import type { GrpcRequest } from "@yaakapp-internal/sync";
import { useAtomValue } from "jotai";
import { useMemo } from "react";
import { createFolder } from "../commands/commands";
import type { DropdownItem } from "../components/core/Dropdown";
import { Icon } from "../components/core/Icon";
import { createRequestAndNavigate } from "../lib/createRequestAndNavigate";
import { generateId } from "../lib/generateId";
import { BODY_TYPE_GRAPHQL } from "../lib/model_util";
import { activeRequestAtom } from "./useActiveRequest";
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
export function useCreateDropdownItems({
hideFolder,
@@ -44,12 +44,12 @@ export function getCreateDropdownItems({
workspaceId: string | null;
activeRequest: HttpRequest | GrpcRequest | WebsocketRequest | null;
onCreate?: (
model: 'http_request' | 'grpc_request' | 'websocket_request' | 'folder',
model: "http_request" | "grpc_request" | "websocket_request" | "folder",
id: string,
) => void;
}): DropdownItem[] {
const folderId =
(folderIdOption === 'active-folder' ? activeRequest?.folderId : folderIdOption) ?? null;
(folderIdOption === "active-folder" ? activeRequest?.folderId : folderIdOption) ?? null;
if (workspaceId == null) {
return [];
@@ -57,59 +57,59 @@ export function getCreateDropdownItems({
return [
{
label: 'HTTP',
label: "HTTP",
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: async () => {
const id = await createRequestAndNavigate({ model: 'http_request', workspaceId, folderId });
onCreate?.('http_request', id);
const id = await createRequestAndNavigate({ model: "http_request", workspaceId, folderId });
onCreate?.("http_request", id);
},
},
{
label: 'GraphQL',
label: "GraphQL",
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: async () => {
const id = await createRequestAndNavigate({
model: 'http_request',
model: "http_request",
workspaceId,
folderId,
bodyType: BODY_TYPE_GRAPHQL,
method: 'POST',
headers: [{ name: 'Content-Type', value: 'application/json', id: generateId() }],
method: "POST",
headers: [{ name: "Content-Type", value: "application/json", id: generateId() }],
});
onCreate?.('http_request', id);
onCreate?.("http_request", id);
},
},
{
label: 'gRPC',
label: "gRPC",
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: async () => {
const id = await createRequestAndNavigate({ model: 'grpc_request', workspaceId, folderId });
onCreate?.('grpc_request', id);
const id = await createRequestAndNavigate({ model: "grpc_request", workspaceId, folderId });
onCreate?.("grpc_request", id);
},
},
{
label: 'WebSocket',
label: "WebSocket",
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: async () => {
const id = await createRequestAndNavigate({
model: 'websocket_request',
model: "websocket_request",
workspaceId,
folderId,
});
onCreate?.('websocket_request', id);
onCreate?.("websocket_request", id);
},
},
...((hideFolder
? []
: [
{ type: 'separator' },
{ type: "separator" },
{
label: 'Folder',
label: "Folder",
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: async () => {
const id = await createFolder.mutateAsync({ folderId });
if (id != null) {
onCreate?.('folder', id);
onCreate?.("folder", id);
}
},
},

View File

@@ -1,13 +1,13 @@
import { useCallback } from 'react';
import { CreateWorkspaceDialog } from '../components/CreateWorkspaceDialog';
import { showDialog } from '../lib/dialog';
import { useCallback } from "react";
import { CreateWorkspaceDialog } from "../components/CreateWorkspaceDialog";
import { showDialog } from "../lib/dialog";
export function useCreateWorkspace() {
return useCallback(() => {
showDialog({
id: 'create-workspace',
title: 'Create Workspace',
size: 'sm',
id: "create-workspace",
title: "Create Workspace",
size: "sm",
render: ({ hide }) => <CreateWorkspaceDialog hide={hide} />,
});
}, []);

View File

@@ -1,6 +1,6 @@
import { debounce } from '@yaakapp-internal/lib';
import type { Dispatch, SetStateAction } from 'react';
import { useMemo, useState } from 'react';
import { debounce } from "@yaakapp-internal/lib";
import type { Dispatch, SetStateAction } from "react";
import { useMemo, useState } from "react";
export function useDebouncedState<T>(
defaultValue: T,

View File

@@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { useDebouncedState } from './useDebouncedState';
import { useEffect } from "react";
import { useDebouncedState } from "./useDebouncedState";
export function useDebouncedValue<T>(value: T, delay = 500) {
const [state, setState] = useDebouncedState<T>(value, delay);

View File

@@ -1,12 +1,12 @@
import { invokeCmd } from '../lib/tauri';
import { useFastMutation } from './useFastMutation';
import { invokeCmd } from "../lib/tauri";
import { useFastMutation } from "./useFastMutation";
export function useDeleteGrpcConnections(requestId?: string) {
return useFastMutation({
mutationKey: ['delete_grpc_connections', requestId],
mutationKey: ["delete_grpc_connections", requestId],
mutationFn: async () => {
if (requestId === undefined) return;
await invokeCmd('cmd_delete_all_grpc_connections', { requestId });
await invokeCmd("cmd_delete_all_grpc_connections", { requestId });
},
});
}

View File

@@ -1,12 +1,12 @@
import { invokeCmd } from '../lib/tauri';
import { useFastMutation } from './useFastMutation';
import { invokeCmd } from "../lib/tauri";
import { useFastMutation } from "./useFastMutation";
export function useDeleteHttpResponses(requestId?: string) {
return useFastMutation({
mutationKey: ['delete_http_responses', requestId],
mutationKey: ["delete_http_responses", requestId],
mutationFn: async () => {
if (requestId === undefined) return;
await invokeCmd('cmd_delete_all_http_responses', { requestId });
await invokeCmd("cmd_delete_all_http_responses", { requestId });
},
});
}

View File

@@ -2,15 +2,15 @@ import {
grpcConnectionsAtom,
httpResponsesAtom,
websocketConnectionsAtom,
} from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
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 { activeWorkspaceIdAtom } from './useActiveWorkspace';
import { useFastMutation } from './useFastMutation';
} from "@yaakapp-internal/models";
import { useAtomValue } from "jotai";
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 { activeWorkspaceIdAtom } from "./useActiveWorkspace";
import { useFastMutation } from "./useFastMutation";
export function useDeleteSendHistory() {
const httpResponses = useAtomValue(httpResponsesAtom);
@@ -18,34 +18,34 @@ export function useDeleteSendHistory() {
const websocketConnections = useAtomValue(websocketConnectionsAtom);
const labels = [
httpResponses.length > 0 ? pluralizeCount('Http Response', httpResponses.length) : null,
grpcConnections.length > 0 ? pluralizeCount('Grpc Connection', grpcConnections.length) : null,
httpResponses.length > 0 ? pluralizeCount("Http Response", httpResponses.length) : null,
grpcConnections.length > 0 ? pluralizeCount("Grpc Connection", grpcConnections.length) : null,
websocketConnections.length > 0
? pluralizeCount('WebSocket Connection', websocketConnections.length)
? pluralizeCount("WebSocket Connection", websocketConnections.length)
: null,
].filter((l) => l != null);
return useFastMutation({
mutationKey: ['delete_send_history', labels],
mutationKey: ["delete_send_history", labels],
mutationFn: async () => {
if (labels.length === 0) {
showAlert({
id: 'no-responses',
title: 'Nothing to Delete',
body: 'There is no Http, Grpc, or Websocket history',
id: "no-responses",
title: "Nothing to Delete",
body: "There is no Http, Grpc, or Websocket history",
});
return;
}
const confirmed = await showConfirmDelete({
id: 'delete-send-history',
title: 'Clear Send History',
description: <>Delete {labels.join(' and ')}?</>,
id: "delete-send-history",
title: "Clear Send History",
description: <>Delete {labels.join(" and ")}?</>,
});
if (!confirmed) return false;
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
await invokeCmd('cmd_delete_send_history', { workspaceId });
await invokeCmd("cmd_delete_send_history", { workspaceId });
return true;
},
});

View File

@@ -1,10 +1,10 @@
import type { Environment } from '@yaakapp-internal/models';
import { useKeyValue } from './useKeyValue';
import type { Environment } from "@yaakapp-internal/models";
import { useKeyValue } from "./useKeyValue";
export function useEnvironmentValueVisibility(environment: Environment) {
return useKeyValue<boolean>({
namespace: 'global',
key: ['environmentValueVisibility', environment.workspaceId],
namespace: "global",
key: ["environmentValueVisibility", environment.workspaceId],
fallback: false,
});
}

View File

@@ -1,13 +1,13 @@
import type { Environment, EnvironmentVariable } from '@yaakapp-internal/models';
import { foldersAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { useMemo } from 'react';
import { jotaiStore } from '../lib/jotai';
import { isBaseEnvironment, isFolderEnvironment } from '../lib/model_util';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useActiveRequest } from './useActiveRequest';
import { useEnvironmentsBreakdown } from './useEnvironmentsBreakdown';
import { useParentFolders } from './useParentFolders';
import type { Environment, EnvironmentVariable } from "@yaakapp-internal/models";
import { foldersAtom } from "@yaakapp-internal/models";
import { useAtomValue } from "jotai";
import { useMemo } from "react";
import { jotaiStore } from "../lib/jotai";
import { isBaseEnvironment, isFolderEnvironment } from "../lib/model_util";
import { useActiveEnvironment } from "./useActiveEnvironment";
import { useActiveRequest } from "./useActiveRequest";
import { useEnvironmentsBreakdown } from "./useEnvironmentsBreakdown";
import { useParentFolders } from "./useParentFolders";
export function useEnvironmentVariables(targetEnvironmentId: string | null) {
const { baseEnvironment, folderEnvironments, allEnvironments } = useEnvironmentsBreakdown();
@@ -59,7 +59,7 @@ function wrapVariables(e: Environment | null): WrappedEnvironmentVariable[] {
if (e == null) return [];
const folders = jotaiStore.get(foldersAtom);
return e.variables.map((v) => {
const folder = e.parentModel === 'folder' ? folders.find((f) => f.id === e.parentId) : null;
const folder = e.parentModel === "folder" ? folders.find((f) => f.id === e.parentId) : null;
const source = folder?.name ?? e.name;
return { variable: v, environment: e, source };
});

View File

@@ -1,13 +1,13 @@
import { environmentsAtom } from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
import { environmentsAtom } from "@yaakapp-internal/models";
import { atom, useAtomValue } from "jotai";
export const environmentsBreakdownAtom = atom((get) => {
const allEnvironments = get(environmentsAtom);
const baseEnvironments = allEnvironments.filter((e) => e.parentModel === 'workspace') ?? [];
const baseEnvironments = allEnvironments.filter((e) => e.parentModel === "workspace") ?? [];
const subEnvironments =
allEnvironments
.filter((e) => e.parentModel === 'environment')
.filter((e) => e.parentModel === "environment")
?.sort((a, b) => {
if (a.sortPriority === b.sortPriority) {
return a.updatedAt > b.updatedAt ? 1 : -1;
@@ -16,7 +16,7 @@ export const environmentsBreakdownAtom = atom((get) => {
}) ?? [];
const folderEnvironments =
allEnvironments.filter((e) => e.parentModel === 'folder' && e.parentId != null) ?? [];
allEnvironments.filter((e) => e.parentModel === "folder" && e.parentId != null) ?? [];
const baseEnvironment = baseEnvironments[0] ?? null;
const otherBaseEnvironments = baseEnvironments.filter((e) => e.id !== baseEnvironment?.id) ?? [];

View File

@@ -1,6 +1,6 @@
import type { Virtualizer } from '@tanstack/react-virtual';
import { useCallback } from 'react';
import { useKey } from 'react-use';
import type { Virtualizer } from "@tanstack/react-virtual";
import { useCallback } from "react";
import { useKey } from "react-use";
interface UseEventViewerKeyboardProps {
totalCount: number;
@@ -28,7 +28,7 @@ export function useEventViewerKeyboard({
const newIndex = activeIndex == null ? 0 : Math.max(0, activeIndex - 1);
setActiveIndex(newIndex);
virtualizer?.scrollToIndex(newIndex, { align: 'auto' });
virtualizer?.scrollToIndex(newIndex, { align: "auto" });
}, [activeIndex, setActiveIndex, totalCount, virtualizer]);
const selectNext = useCallback(() => {
@@ -36,11 +36,11 @@ export function useEventViewerKeyboard({
const newIndex = activeIndex == null ? 0 : Math.min(totalCount - 1, activeIndex + 1);
setActiveIndex(newIndex);
virtualizer?.scrollToIndex(newIndex, { align: 'auto' });
virtualizer?.scrollToIndex(newIndex, { align: "auto" });
}, [activeIndex, setActiveIndex, totalCount, virtualizer]);
useKey(
(e) => e.key === 'ArrowUp' || e.key === 'k',
(e) => e.key === "ArrowUp" || e.key === "k",
(e) => {
if (!enabled || !isContainerFocused()) return;
e.preventDefault();
@@ -51,7 +51,7 @@ export function useEventViewerKeyboard({
);
useKey(
(e) => e.key === 'ArrowDown' || e.key === 'j',
(e) => e.key === "ArrowDown" || e.key === "j",
(e) => {
if (!enabled || !isContainerFocused()) return;
e.preventDefault();
@@ -62,7 +62,7 @@ export function useEventViewerKeyboard({
);
useKey(
(e) => e.key === 'Escape',
(e) => e.key === "Escape",
(e) => {
if (!enabled || !isContainerFocused()) return;
e.preventDefault();
@@ -73,7 +73,7 @@ export function useEventViewerKeyboard({
);
useKey(
(e) => e.key === 'Enter' || e.key === ' ',
(e) => e.key === "Enter" || e.key === " ",
(e) => {
if (!enabled || !isContainerFocused() || activeIndex == null) return;
e.preventDefault();

View File

@@ -1,17 +1,17 @@
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 { activeWorkspaceAtom } from './useActiveWorkspace';
import { useFastMutation } from './useFastMutation';
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 { activeWorkspaceAtom } from "./useActiveWorkspace";
import { useFastMutation } from "./useFastMutation";
export function useExportData() {
return useFastMutation({
mutationKey: ['export_data'],
mutationKey: ["export_data"],
onError: (err: string) => {
showAlert({ id: 'export-failed', title: 'Export Failed', body: err });
showAlert({ id: "export-failed", title: "Export Failed", body: err });
},
mutationFn: async () => {
const activeWorkspace = jotaiStore.get(activeWorkspaceAtom);
@@ -20,17 +20,17 @@ export function useExportData() {
if (activeWorkspace == null || workspaces.length === 0) return;
showDialog({
id: 'export-data',
title: 'Export Data',
size: 'md',
id: "export-data",
title: "Export Data",
size: "md",
noPadding: true,
render: ({ hide }) => (
<ExportDataDialog
onHide={hide}
onSuccess={() => {
showToast({
color: 'success',
message: 'Data export successful',
color: "success",
message: "Data export successful",
});
}}
/>

View File

@@ -1,6 +1,6 @@
import type { MutationKey } from '@tanstack/react-query';
import { useMemo } from 'react';
import { showToast } from '../lib/toast';
import type { MutationKey } from "@tanstack/react-query";
import { useMemo } from "react";
import { showToast } from "../lib/toast";
interface MutationOptions<TData, TError, TVariables> {
mutationKey: MutationKey;
@@ -13,7 +13,7 @@ interface MutationOptions<TData, TError, TVariables> {
type CallbackMutationOptions<TData, TError, TVariables> = Omit<
MutationOptions<TData, TError, TVariables>,
'mutationKey' | 'mutationFn'
"mutationKey" | "mutationFn"
>;
export function createFastMutation<TData = unknown, TError = unknown, TVariables = void>(
@@ -36,14 +36,14 @@ export function createFastMutation<TData = unknown, TError = unknown, TVariables
args?.onSettled?.();
return data;
} catch (err: unknown) {
const stringKey = mutationKey.join('.');
const stringKey = mutationKey.join(".");
const e = err as TError;
console.log('mutation error', stringKey, e);
console.log("mutation error", stringKey, e);
if (!disableToastError) {
showToast({
id: stringKey,
message: err instanceof Error ? err.message : String(err),
color: 'danger',
color: "danger",
timeout: 5000,
});
}

View File

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

View File

@@ -1,15 +1,15 @@
import { useQuery } from '@tanstack/react-query';
import type { Folder } from '@yaakapp-internal/models';
import { useQuery } from "@tanstack/react-query";
import type { Folder } from "@yaakapp-internal/models";
import type {
CallFolderActionRequest,
FolderAction,
GetFolderActionsResponse,
} from '@yaakapp-internal/plugins';
import { useMemo } from 'react';
import { invokeCmd } from '../lib/tauri';
import { usePluginsKey } from './usePlugins';
} from "@yaakapp-internal/plugins";
import { useMemo } from "react";
import { invokeCmd } from "../lib/tauri";
import { usePluginsKey } from "./usePlugins";
export type CallableFolderAction = Pick<FolderAction, 'label' | 'icon'> & {
export type CallableFolderAction = Pick<FolderAction, "label" | "icon"> & {
call: (folder: Folder) => Promise<void>;
};
@@ -17,7 +17,7 @@ export function useFolderActions() {
const pluginsKey = usePluginsKey();
const actionsResult = useQuery<CallableFolderAction[]>({
queryKey: ['folder_actions', pluginsKey],
queryKey: ["folder_actions", pluginsKey],
queryFn: () => getFolderActions(),
});
@@ -30,7 +30,7 @@ export function useFolderActions() {
}
export async function getFolderActions() {
const responses = await invokeCmd<GetFolderActionsResponse[]>('cmd_folder_actions');
const responses = await invokeCmd<GetFolderActionsResponse[]>("cmd_folder_actions");
const actions = responses.flatMap((r) =>
r.actions.map((a, i) => ({
label: a.label,
@@ -41,7 +41,7 @@ export async function getFolderActions() {
pluginRefId: r.pluginRefId,
args: { folder },
};
await invokeCmd('cmd_call_folder_action', { req: payload });
await invokeCmd("cmd_call_folder_action", { req: payload });
},
})),
);

View File

@@ -1,6 +1,6 @@
import { useQuery } from '@tanstack/react-query';
import type { EditorProps } from '../components/core/Editor/Editor';
import { tryFormatJson, tryFormatXml } from '../lib/formatters';
import { useQuery } from "@tanstack/react-query";
import type { EditorProps } from "../components/core/Editor/Editor";
import { tryFormatJson, tryFormatXml } from "../lib/formatters";
export function useFormatText({
text,
@@ -8,20 +8,20 @@ export function useFormatText({
pretty,
}: {
text: string;
language: EditorProps['language'];
language: EditorProps["language"];
pretty: boolean;
}) {
return useQuery({
placeholderData: (prev) => prev, // Keep previous data on refetch
queryKey: [text, language, pretty],
queryFn: async () => {
if (text === '' || !pretty) {
if (text === "" || !pretty) {
return text;
}
if (language === 'json') {
if (language === "json") {
return tryFormatJson(text);
}
if (language === 'xml' || language === 'html') {
if (language === "xml" || language === "html") {
return tryFormatXml(text);
}
return text;

View File

@@ -1,11 +1,11 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import { emit } from '@tauri-apps/api/event';
import type { GrpcConnection, GrpcRequest } from '@yaakapp-internal/models';
import { jotaiStore } from '../lib/jotai';
import { minPromiseMillis } from '../lib/minPromiseMillis';
import { invokeCmd } from '../lib/tauri';
import { activeEnvironmentIdAtom, useActiveEnvironment } from './useActiveEnvironment';
import { useDebouncedValue } from './useDebouncedValue';
import { useMutation, useQuery } from "@tanstack/react-query";
import { emit } from "@tauri-apps/api/event";
import type { GrpcConnection, GrpcRequest } from "@yaakapp-internal/models";
import { jotaiStore } from "../lib/jotai";
import { minPromiseMillis } from "../lib/minPromiseMillis";
import { invokeCmd } from "../lib/tauri";
import { activeEnvironmentIdAtom, useActiveEnvironment } from "./useActiveEnvironment";
import { useDebouncedValue } from "./useDebouncedValue";
export interface ReflectResponseService {
name: string;
@@ -17,36 +17,36 @@ export function useGrpc(
conn: GrpcConnection | null,
protoFiles: string[],
) {
const requestId = req?.id ?? 'n/a';
const requestId = req?.id ?? "n/a";
const environment = useActiveEnvironment();
const go = useMutation<void, string>({
mutationKey: ['grpc_go', conn?.id],
mutationKey: ["grpc_go", conn?.id],
mutationFn: () =>
invokeCmd<void>('cmd_grpc_go', { requestId, environmentId: environment?.id, protoFiles }),
invokeCmd<void>("cmd_grpc_go", { requestId, environmentId: environment?.id, protoFiles }),
});
const send = useMutation({
mutationKey: ['grpc_send', conn?.id],
mutationKey: ["grpc_send", conn?.id],
mutationFn: ({ message }: { message: string }) =>
emit(`grpc_client_msg_${conn?.id ?? 'none'}`, { Message: message }),
emit(`grpc_client_msg_${conn?.id ?? "none"}`, { Message: message }),
});
const cancel = useMutation({
mutationKey: ['grpc_cancel', conn?.id ?? 'n/a'],
mutationFn: () => emit(`grpc_client_msg_${conn?.id ?? 'none'}`, 'Cancel'),
mutationKey: ["grpc_cancel", conn?.id ?? "n/a"],
mutationFn: () => emit(`grpc_client_msg_${conn?.id ?? "none"}`, "Cancel"),
});
const commit = useMutation({
mutationKey: ['grpc_commit', conn?.id ?? 'n/a'],
mutationFn: () => emit(`grpc_client_msg_${conn?.id ?? 'none'}`, 'Commit'),
mutationKey: ["grpc_commit", conn?.id ?? "n/a"],
mutationFn: () => emit(`grpc_client_msg_${conn?.id ?? "none"}`, "Commit"),
});
const debouncedUrl = useDebouncedValue<string>(req?.url ?? '', 1000);
const debouncedUrl = useDebouncedValue<string>(req?.url ?? "", 1000);
const reflect = useQuery<ReflectResponseService[], string>({
enabled: req != null,
queryKey: ['grpc_reflect', req?.id ?? 'n/a', debouncedUrl, protoFiles],
queryKey: ["grpc_reflect", req?.id ?? "n/a", debouncedUrl, protoFiles],
staleTime: Infinity,
refetchOnMount: false,
refetchOnWindowFocus: false,
@@ -54,7 +54,7 @@ export function useGrpc(
queryFn: () => {
const environmentId = jotaiStore.get(activeEnvironmentIdAtom);
return minPromiseMillis<ReflectResponseService[]>(
invokeCmd('cmd_grpc_reflect', { requestId, protoFiles, environmentId }),
invokeCmd("cmd_grpc_reflect", { requestId, protoFiles, environmentId }),
300,
);
},
@@ -65,7 +65,7 @@ export function useGrpc(
reflect,
cancel,
commit,
isStreaming: conn != null && conn.state !== 'closed',
isStreaming: conn != null && conn.state !== "closed",
send,
};
}

View File

@@ -1,10 +1,10 @@
import { getKeyValue } from '../lib/keyValueStore';
import { useKeyValue } from './useKeyValue';
import { getKeyValue } from "../lib/keyValueStore";
import { useKeyValue } from "./useKeyValue";
export function protoFilesArgs(requestId: string | null) {
return {
namespace: 'global' as const,
key: ['proto_files', requestId ?? 'n/a'],
namespace: "global" as const,
key: ["proto_files", requestId ?? "n/a"],
};
}

View File

@@ -1,16 +1,16 @@
import { useQuery } from '@tanstack/react-query';
import type { GrpcRequest } from '@yaakapp-internal/models';
import { useQuery } from "@tanstack/react-query";
import type { GrpcRequest } from "@yaakapp-internal/models";
import type {
CallGrpcRequestActionRequest,
GetGrpcRequestActionsResponse,
GrpcRequestAction,
} from '@yaakapp-internal/plugins';
import { useMemo } from 'react';
import { invokeCmd } from '../lib/tauri';
import { getGrpcProtoFiles } from './useGrpcProtoFiles';
import { usePluginsKey } from './usePlugins';
} from "@yaakapp-internal/plugins";
import { useMemo } from "react";
import { invokeCmd } from "../lib/tauri";
import { getGrpcProtoFiles } from "./useGrpcProtoFiles";
import { usePluginsKey } from "./usePlugins";
export type CallableGrpcRequestAction = Pick<GrpcRequestAction, 'label' | 'icon'> & {
export type CallableGrpcRequestAction = Pick<GrpcRequestAction, "label" | "icon"> & {
call: (grpcRequest: GrpcRequest) => Promise<void>;
};
@@ -18,7 +18,7 @@ export function useGrpcRequestActions() {
const pluginsKey = usePluginsKey();
const actionsResult = useQuery<CallableGrpcRequestAction[]>({
queryKey: ['grpc_request_actions', pluginsKey],
queryKey: ["grpc_request_actions", pluginsKey],
queryFn: async () => {
return getGrpcRequestActions();
},
@@ -33,7 +33,7 @@ export function useGrpcRequestActions() {
}
export async function getGrpcRequestActions() {
const responses = await invokeCmd<GetGrpcRequestActionsResponse[]>('cmd_grpc_request_actions');
const responses = await invokeCmd<GetGrpcRequestActionsResponse[]>("cmd_grpc_request_actions");
return responses.flatMap((r) =>
r.actions.map((a, i) => ({
@@ -46,7 +46,7 @@ export async function getGrpcRequestActions() {
pluginRefId: r.pluginRefId,
args: { grpcRequest, protoFiles },
};
await invokeCmd('cmd_call_grpc_request_action', { req: payload });
await invokeCmd("cmd_call_grpc_request_action", { req: payload });
},
})),
);

View File

@@ -1,8 +1,8 @@
import { useMemo } from 'react';
import { CountBadge } from '../components/core/CountBadge';
import type { TabItem } from '../components/core/Tabs/Tabs';
import type { HeaderModel } from './useInheritedHeaders';
import { useInheritedHeaders } from './useInheritedHeaders';
import { useMemo } from "react";
import { CountBadge } from "../components/core/CountBadge";
import type { TabItem } from "../components/core/Tabs/Tabs";
import type { HeaderModel } from "./useInheritedHeaders";
import { useInheritedHeaders } from "./useInheritedHeaders";
export function useHeadersTab<T extends string>(
tabValue: T,
@@ -16,13 +16,13 @@ export function useHeadersTab<T extends string>(
const allHeaders = [
...inheritedHeaders,
...(model.model === 'grpc_request' ? model.metadata : model.headers),
...(model.model === "grpc_request" ? model.metadata : model.headers),
];
const numHeaders = allHeaders.filter((h) => h.name).length;
const tab: TabItem = {
value: tabValue,
label: label ?? 'Headers',
label: label ?? "Headers",
rightSlot: <CountBadge count={numHeaders} />,
};

View File

@@ -1,105 +1,105 @@
import { type } from '@tauri-apps/plugin-os';
import { debounce } from '@yaakapp-internal/lib';
import { settingsAtom } from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
import { useEffect } from 'react';
import { capitalize } from '../lib/capitalize';
import { jotaiStore } from '../lib/jotai';
import { type } from "@tauri-apps/plugin-os";
import { debounce } from "@yaakapp-internal/lib";
import { settingsAtom } from "@yaakapp-internal/models";
import { atom, useAtomValue } from "jotai";
import { useEffect } from "react";
import { capitalize } from "../lib/capitalize";
import { jotaiStore } from "../lib/jotai";
const HOLD_KEYS = ['Shift', 'Control', 'Command', 'Alt', 'Meta'];
const SINGLE_WHITELIST = ['Delete', 'Enter', 'Backspace'];
const HOLD_KEYS = ["Shift", "Control", "Command", "Alt", "Meta"];
const SINGLE_WHITELIST = ["Delete", "Enter", "Backspace"];
export type HotkeyAction =
| 'app.zoom_in'
| 'app.zoom_out'
| 'app.zoom_reset'
| 'command_palette.toggle'
| 'editor.autocomplete'
| 'environment_editor.toggle'
| 'hotkeys.showHelp'
| 'model.create'
| 'model.duplicate'
| 'request.send'
| 'request.rename'
| 'switcher.next'
| 'switcher.prev'
| 'switcher.toggle'
| 'settings.show'
| 'sidebar.filter'
| 'sidebar.selected.delete'
| 'sidebar.selected.duplicate'
| 'sidebar.selected.move'
| 'sidebar.selected.rename'
| 'sidebar.expand_all'
| 'sidebar.collapse_all'
| 'sidebar.focus'
| 'sidebar.context_menu'
| 'url_bar.focus'
| 'workspace_settings.show';
| "app.zoom_in"
| "app.zoom_out"
| "app.zoom_reset"
| "command_palette.toggle"
| "editor.autocomplete"
| "environment_editor.toggle"
| "hotkeys.showHelp"
| "model.create"
| "model.duplicate"
| "request.send"
| "request.rename"
| "switcher.next"
| "switcher.prev"
| "switcher.toggle"
| "settings.show"
| "sidebar.filter"
| "sidebar.selected.delete"
| "sidebar.selected.duplicate"
| "sidebar.selected.move"
| "sidebar.selected.rename"
| "sidebar.expand_all"
| "sidebar.collapse_all"
| "sidebar.focus"
| "sidebar.context_menu"
| "url_bar.focus"
| "workspace_settings.show";
/** Default hotkeys for macOS (uses Meta for Cmd) */
const defaultHotkeysMac: Record<HotkeyAction, string[]> = {
'app.zoom_in': ['Meta+Equal'],
'app.zoom_out': ['Meta+Minus'],
'app.zoom_reset': ['Meta+0'],
'command_palette.toggle': ['Meta+k'],
'editor.autocomplete': ['Control+Space'],
'environment_editor.toggle': ['Meta+Shift+e'],
'request.rename': ['Control+Shift+r'],
'request.send': ['Meta+Enter', 'Meta+r'],
'hotkeys.showHelp': ['Meta+Shift+/'],
'model.create': ['Meta+n'],
'model.duplicate': ['Meta+d'],
'switcher.next': ['Control+Shift+Tab'],
'switcher.prev': ['Control+Tab'],
'switcher.toggle': ['Meta+p'],
'settings.show': ['Meta+,'],
'sidebar.filter': ['Meta+f'],
'sidebar.expand_all': ['Meta+Shift+Equal'],
'sidebar.collapse_all': ['Meta+Shift+Minus'],
'sidebar.selected.delete': ['Delete', 'Meta+Backspace'],
'sidebar.selected.duplicate': ['Meta+d'],
'sidebar.selected.move': [],
'sidebar.selected.rename': ['Enter'],
'sidebar.focus': ['Meta+b'],
'sidebar.context_menu': ['Control+Enter'],
'url_bar.focus': ['Meta+l'],
'workspace_settings.show': ['Meta+;'],
"app.zoom_in": ["Meta+Equal"],
"app.zoom_out": ["Meta+Minus"],
"app.zoom_reset": ["Meta+0"],
"command_palette.toggle": ["Meta+k"],
"editor.autocomplete": ["Control+Space"],
"environment_editor.toggle": ["Meta+Shift+e"],
"request.rename": ["Control+Shift+r"],
"request.send": ["Meta+Enter", "Meta+r"],
"hotkeys.showHelp": ["Meta+Shift+/"],
"model.create": ["Meta+n"],
"model.duplicate": ["Meta+d"],
"switcher.next": ["Control+Shift+Tab"],
"switcher.prev": ["Control+Tab"],
"switcher.toggle": ["Meta+p"],
"settings.show": ["Meta+,"],
"sidebar.filter": ["Meta+f"],
"sidebar.expand_all": ["Meta+Shift+Equal"],
"sidebar.collapse_all": ["Meta+Shift+Minus"],
"sidebar.selected.delete": ["Delete", "Meta+Backspace"],
"sidebar.selected.duplicate": ["Meta+d"],
"sidebar.selected.move": [],
"sidebar.selected.rename": ["Enter"],
"sidebar.focus": ["Meta+b"],
"sidebar.context_menu": ["Control+Enter"],
"url_bar.focus": ["Meta+l"],
"workspace_settings.show": ["Meta+;"],
};
/** Default hotkeys for Windows/Linux (uses Control for Ctrl) */
const defaultHotkeysOther: Record<HotkeyAction, string[]> = {
'app.zoom_in': ['Control+Equal'],
'app.zoom_out': ['Control+Minus'],
'app.zoom_reset': ['Control+0'],
'command_palette.toggle': ['Control+k'],
'editor.autocomplete': ['Control+Space'],
'environment_editor.toggle': ['Control+Shift+e'],
'request.rename': ['F2'],
'request.send': ['Control+Enter', 'Control+r'],
'hotkeys.showHelp': ['Control+Shift+/'],
'model.create': ['Control+n'],
'model.duplicate': ['Control+d'],
'switcher.next': ['Control+Shift+Tab'],
'switcher.prev': ['Control+Tab'],
'switcher.toggle': ['Control+p'],
'settings.show': ['Control+,'],
'sidebar.filter': ['Control+f'],
'sidebar.expand_all': ['Control+Shift+Equal'],
'sidebar.collapse_all': ['Control+Shift+Minus'],
'sidebar.selected.delete': ['Delete', 'Control+Backspace'],
'sidebar.selected.duplicate': ['Control+d'],
'sidebar.selected.move': [],
'sidebar.selected.rename': ['Enter'],
'sidebar.focus': ['Control+b'],
'sidebar.context_menu': ['Alt+Insert'],
'url_bar.focus': ['Control+l'],
'workspace_settings.show': ['Control+;'],
"app.zoom_in": ["Control+Equal"],
"app.zoom_out": ["Control+Minus"],
"app.zoom_reset": ["Control+0"],
"command_palette.toggle": ["Control+k"],
"editor.autocomplete": ["Control+Space"],
"environment_editor.toggle": ["Control+Shift+e"],
"request.rename": ["F2"],
"request.send": ["Control+Enter", "Control+r"],
"hotkeys.showHelp": ["Control+Shift+/"],
"model.create": ["Control+n"],
"model.duplicate": ["Control+d"],
"switcher.next": ["Control+Shift+Tab"],
"switcher.prev": ["Control+Tab"],
"switcher.toggle": ["Control+p"],
"settings.show": ["Control+,"],
"sidebar.filter": ["Control+f"],
"sidebar.expand_all": ["Control+Shift+Equal"],
"sidebar.collapse_all": ["Control+Shift+Minus"],
"sidebar.selected.delete": ["Delete", "Control+Backspace"],
"sidebar.selected.duplicate": ["Control+d"],
"sidebar.selected.move": [],
"sidebar.selected.rename": ["Enter"],
"sidebar.focus": ["Control+b"],
"sidebar.context_menu": ["Alt+Insert"],
"url_bar.focus": ["Control+l"],
"workspace_settings.show": ["Control+;"],
};
/** Get the default hotkeys for the current platform */
export const defaultHotkeys: Record<HotkeyAction, string[]> =
type() === 'macos' ? defaultHotkeysMac : defaultHotkeysOther;
type() === "macos" ? defaultHotkeysMac : defaultHotkeysOther;
/** Atom that provides the effective hotkeys by merging defaults with user settings */
export const hotkeysAtom = atom((get) => {
@@ -124,48 +124,48 @@ function getHotkeys(): Record<HotkeyAction, string[]> {
}
const hotkeyLabels: Record<HotkeyAction, string> = {
'app.zoom_in': 'Zoom In',
'app.zoom_out': 'Zoom Out',
'app.zoom_reset': 'Zoom to Actual Size',
'command_palette.toggle': 'Toggle Command Palette',
'editor.autocomplete': 'Trigger Autocomplete',
'environment_editor.toggle': 'Edit Environments',
'hotkeys.showHelp': 'Show Keyboard Shortcuts',
'model.create': 'New Request',
'model.duplicate': 'Duplicate Request',
'request.rename': 'Rename Active Request',
'request.send': 'Send Active Request',
'switcher.next': 'Go To Previous Request',
'switcher.prev': 'Go To Next Request',
'switcher.toggle': 'Toggle Request Switcher',
'settings.show': 'Open Settings',
'sidebar.filter': 'Filter Sidebar',
'sidebar.expand_all': 'Expand All Folders',
'sidebar.collapse_all': 'Collapse All Folders',
'sidebar.selected.delete': 'Delete Selected Sidebar Item',
'sidebar.selected.duplicate': 'Duplicate Selected Sidebar Item',
'sidebar.selected.move': 'Move Selected to Workspace',
'sidebar.selected.rename': 'Rename Selected Sidebar Item',
'sidebar.focus': 'Focus or Toggle Sidebar',
'sidebar.context_menu': 'Show Context Menu',
'url_bar.focus': 'Focus URL',
'workspace_settings.show': 'Open Workspace Settings',
"app.zoom_in": "Zoom In",
"app.zoom_out": "Zoom Out",
"app.zoom_reset": "Zoom to Actual Size",
"command_palette.toggle": "Toggle Command Palette",
"editor.autocomplete": "Trigger Autocomplete",
"environment_editor.toggle": "Edit Environments",
"hotkeys.showHelp": "Show Keyboard Shortcuts",
"model.create": "New Request",
"model.duplicate": "Duplicate Request",
"request.rename": "Rename Active Request",
"request.send": "Send Active Request",
"switcher.next": "Go To Previous Request",
"switcher.prev": "Go To Next Request",
"switcher.toggle": "Toggle Request Switcher",
"settings.show": "Open Settings",
"sidebar.filter": "Filter Sidebar",
"sidebar.expand_all": "Expand All Folders",
"sidebar.collapse_all": "Collapse All Folders",
"sidebar.selected.delete": "Delete Selected Sidebar Item",
"sidebar.selected.duplicate": "Duplicate Selected Sidebar Item",
"sidebar.selected.move": "Move Selected to Workspace",
"sidebar.selected.rename": "Rename Selected Sidebar Item",
"sidebar.focus": "Focus or Toggle Sidebar",
"sidebar.context_menu": "Show Context Menu",
"url_bar.focus": "Focus URL",
"workspace_settings.show": "Open Workspace Settings",
};
const layoutInsensitiveKeys = [
'Equal',
'Minus',
'BracketLeft',
'BracketRight',
'Backquote',
'Space',
"Equal",
"Minus",
"BracketLeft",
"BracketRight",
"Backquote",
"Space",
];
export const hotkeyActions: HotkeyAction[] = (
Object.keys(defaultHotkeys) as (keyof typeof defaultHotkeys)[]
).sort((a, b) => {
const scopeA = a.split('.')[0] || '';
const scopeB = b.split('.')[0] || '';
const scopeA = a.split(".")[0] || "";
const scopeB = b.split(".")[0] || "";
if (scopeA !== scopeB) {
return scopeA.localeCompare(scopeB);
}
@@ -217,11 +217,11 @@ export function useHotKey(
export function useSubscribeHotKeys() {
useEffect(() => {
document.addEventListener('keyup', handleKeyUp, { capture: true });
document.addEventListener('keydown', handleKeyDown, { capture: true });
document.addEventListener("keyup", handleKeyUp, { capture: true });
document.addEventListener("keydown", handleKeyDown, { capture: true });
return () => {
document.removeEventListener('keydown', handleKeyDown, { capture: true });
document.removeEventListener('keyup', handleKeyUp, { capture: true });
document.removeEventListener("keydown", handleKeyDown, { capture: true });
document.removeEventListener("keyup", handleKeyUp, { capture: true });
};
}, []);
}
@@ -260,13 +260,13 @@ function handleKeyDown(e: KeyboardEvent) {
currentKeys.add(keyToAdd);
const currentKeysWithModifiers = new Set(currentKeys);
if (e.altKey) currentKeysWithModifiers.add('Alt');
if (e.ctrlKey) currentKeysWithModifiers.add('Control');
if (e.metaKey) currentKeysWithModifiers.add('Meta');
if (e.shiftKey) currentKeysWithModifiers.add('Shift');
if (e.altKey) currentKeysWithModifiers.add("Alt");
if (e.ctrlKey) currentKeysWithModifiers.add("Control");
if (e.metaKey) currentKeysWithModifiers.add("Meta");
if (e.shiftKey) currentKeysWithModifiers.add("Shift");
// Don't trigger if the user is focused within an element that explicitly disableds hotkeys
if (document.activeElement?.closest('[data-disable-hotkey]')) {
if (document.activeElement?.closest("[data-disable-hotkey]")) {
return;
}
@@ -274,14 +274,14 @@ function handleKeyDown(e: KeyboardEvent) {
if (
(e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) &&
currentKeysWithModifiers.size === 1 &&
(currentKeysWithModifiers.has('Backspace') || currentKeysWithModifiers.has('Delete'))
(currentKeysWithModifiers.has("Backspace") || currentKeysWithModifiers.has("Delete"))
) {
return;
}
const executed: string[] = [];
for (const { action, callback, options } of jotaiStore.get(sortedCallbacksAtom)) {
const enable = typeof options.enable === 'function' ? options.enable() : options.enable;
const enable = typeof options.enable === "function" ? options.enable() : options.enable;
if (enable === false) {
continue;
}
@@ -298,7 +298,7 @@ function handleKeyDown(e: KeyboardEvent) {
}
if (executed.length > 0) {
console.log('Executed hotkey', executed.join(', '));
console.log("Executed hotkey", executed.join(", "));
jotaiStore.set(currentKeysAtom, new Set([]));
}
clearCurrentKeysDebounced();
@@ -309,59 +309,59 @@ export function useHotkeyLabel(action: HotkeyAction): string {
}
export function getHotkeyScope(action: HotkeyAction): string {
const scope = action.split('.')[0];
return scope || '';
const scope = action.split(".")[0];
return scope || "";
}
export function formatHotkeyString(trigger: string): string[] {
const os = type();
const parts = trigger.split('+');
const parts = trigger.split("+");
const labelParts: string[] = [];
for (const p of parts) {
if (os === 'macos') {
if (p === 'Meta') {
labelParts.push('⌘');
} else if (p === 'Shift') {
labelParts.push('⇧');
} else if (p === 'Control') {
labelParts.push('⌃');
} else if (p === 'Alt') {
labelParts.push('⌥');
} else if (p === 'Enter') {
labelParts.push('↩');
} else if (p === 'Tab') {
labelParts.push('⇥');
} else if (p === 'Backspace') {
labelParts.push('⌫');
} else if (p === 'Delete') {
labelParts.push('⌦');
} else if (p === 'Minus') {
labelParts.push('-');
} else if (p === 'Plus') {
labelParts.push('+');
} else if (p === 'Equal') {
labelParts.push('=');
} else if (p === 'Space') {
labelParts.push('Space');
if (os === "macos") {
if (p === "Meta") {
labelParts.push("⌘");
} else if (p === "Shift") {
labelParts.push("⇧");
} else if (p === "Control") {
labelParts.push("⌃");
} else if (p === "Alt") {
labelParts.push("⌥");
} else if (p === "Enter") {
labelParts.push("↩");
} else if (p === "Tab") {
labelParts.push("⇥");
} else if (p === "Backspace") {
labelParts.push("⌫");
} else if (p === "Delete") {
labelParts.push("⌦");
} else if (p === "Minus") {
labelParts.push("-");
} else if (p === "Plus") {
labelParts.push("+");
} else if (p === "Equal") {
labelParts.push("=");
} else if (p === "Space") {
labelParts.push("Space");
} else {
labelParts.push(capitalize(p));
}
} else {
if (p === 'Control') {
labelParts.push('Ctrl');
} else if (p === 'Space') {
labelParts.push('Space');
if (p === "Control") {
labelParts.push("Ctrl");
} else if (p === "Space") {
labelParts.push("Space");
} else {
labelParts.push(capitalize(p));
}
}
}
if (os === 'macos') {
if (os === "macos") {
return labelParts;
}
return [labelParts.join('+')];
return [labelParts.join("+")];
}
export function useFormattedHotkey(action: HotkeyAction | null): string[] | null {
@@ -379,21 +379,21 @@ function compareKeys(keysA: string[], keysB: string[]) {
const sortedA = keysA
.map((k) => k.toLowerCase())
.sort()
.join('::');
.join("::");
const sortedB = keysB
.map((k) => k.toLowerCase())
.sort()
.join('::');
.join("::");
return sortedA === sortedB;
}
/** Build the full key combination from a KeyboardEvent including modifiers */
function getKeysFromEvent(e: KeyboardEvent): string[] {
const keys: string[] = [];
if (e.altKey) keys.push('Alt');
if (e.ctrlKey) keys.push('Control');
if (e.metaKey) keys.push('Meta');
if (e.shiftKey) keys.push('Shift');
if (e.altKey) keys.push("Alt");
if (e.ctrlKey) keys.push("Control");
if (e.metaKey) keys.push("Meta");
if (e.shiftKey) keys.push("Shift");
// Add the actual key (use code for layout-insensitive keys)
const keyToAdd = layoutInsensitiveKeys.includes(e.code) ? e.code : e.key;
@@ -409,7 +409,7 @@ function keysMatchAction(keys: string[], action: HotkeyAction): boolean {
if (!hkKeys || hkKeys.length === 0) return false;
for (const hkKey of hkKeys) {
const hotkeyParts = hkKey.split('+');
const hotkeyParts = hkKey.split("+");
if (compareKeys(hotkeyParts, keys)) {
return true;
}

View File

@@ -1,11 +1,11 @@
import { useQuery } from '@tanstack/react-query';
import type { GetHttpAuthenticationSummaryResponse } from '@yaakapp-internal/plugins';
import { atom, useAtomValue } from 'jotai';
import { useState } from 'react';
import { jotaiStore } from '../lib/jotai';
import { invokeCmd } from '../lib/tauri';
import { showErrorToast } from '../lib/toast';
import { usePluginsKey } from './usePlugins';
import { useQuery } from "@tanstack/react-query";
import type { GetHttpAuthenticationSummaryResponse } from "@yaakapp-internal/plugins";
import { atom, useAtomValue } from "jotai";
import { useState } from "react";
import { jotaiStore } from "../lib/jotai";
import { invokeCmd } from "../lib/tauri";
import { showErrorToast } from "../lib/toast";
import { usePluginsKey } from "./usePlugins";
const httpAuthenticationSummariesAtom = atom<GetHttpAuthenticationSummaryResponse[]>([]);
const orderedHttpAuthenticationAtom = atom((get) =>
@@ -21,7 +21,7 @@ export function useSubscribeHttpAuthentication() {
const pluginsKey = usePluginsKey();
useQuery({
queryKey: ['http_authentication_summaries', pluginsKey],
queryKey: ["http_authentication_summaries", pluginsKey],
// Fetch periodically until functions are returned
// NOTE: visibilitychange (refetchOnWindowFocus) does not work on Windows, so we'll rely on this logic
// to refetch things until that's working again
@@ -32,15 +32,15 @@ export function useSubscribeHttpAuthentication() {
queryFn: async () => {
try {
const result = await invokeCmd<GetHttpAuthenticationSummaryResponse[]>(
'cmd_get_http_authentication_summaries',
"cmd_get_http_authentication_summaries",
);
setNumResults(result.length);
jotaiStore.set(httpAuthenticationSummariesAtom, result);
return result;
} catch (err) {
showErrorToast({
id: 'http-authentication-error',
title: 'HTTP Authentication Error',
id: "http-authentication-error",
title: "HTTP Authentication Error",
message: err,
});
}

View File

@@ -1,19 +1,19 @@
import { useQuery } from '@tanstack/react-query';
import { useQuery } from "@tanstack/react-query";
import type {
Folder,
GrpcRequest,
HttpRequest,
WebsocketRequest,
Workspace,
} 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 { activeEnvironmentIdAtom } from './useActiveEnvironment';
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
} 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 { activeEnvironmentIdAtom } from "./useActiveEnvironment";
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
export function useHttpAuthenticationConfig(
authName: string | null,
@@ -29,14 +29,14 @@ export function useHttpAuthenticationConfig(
// handle that, we'll force the auth to re-fetch after each new response closes
const responseKey = md5(
responses
.filter((r) => r.state === 'closed')
.filter((r) => r.state === "closed")
.map((r) => r.id)
.join(':'),
.join(":"),
);
return useQuery({
queryKey: [
'http_authentication_config',
"http_authentication_config",
model,
authName,
values,
@@ -47,9 +47,9 @@ export function useHttpAuthenticationConfig(
],
placeholderData: (prev) => prev, // Keep previous data on refetch
queryFn: async () => {
if (authName == null || authName === 'inherit') return null;
if (authName == null || authName === "inherit") return null;
const config = await invokeCmd<GetHttpAuthenticationConfigResponse>(
'cmd_get_http_authentication_config',
"cmd_get_http_authentication_config",
{
authName,
values,
@@ -65,7 +65,7 @@ export function useHttpAuthenticationConfig(
call: async (
model: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace,
) => {
await invokeCmd('cmd_call_http_authentication_action', {
await invokeCmd("cmd_call_http_authentication_action", {
pluginRefId: config.pluginRefId,
actionIndex: i,
authName,

View File

@@ -1,15 +1,15 @@
import { useQuery } from '@tanstack/react-query';
import type { HttpRequest } from '@yaakapp-internal/models';
import { useQuery } from "@tanstack/react-query";
import type { HttpRequest } from "@yaakapp-internal/models";
import type {
CallHttpRequestActionRequest,
GetHttpRequestActionsResponse,
HttpRequestAction,
} from '@yaakapp-internal/plugins';
import { useMemo } from 'react';
import { invokeCmd } from '../lib/tauri';
import { usePluginsKey } from './usePlugins';
} from "@yaakapp-internal/plugins";
import { useMemo } from "react";
import { invokeCmd } from "../lib/tauri";
import { usePluginsKey } from "./usePlugins";
export type CallableHttpRequestAction = Pick<HttpRequestAction, 'label' | 'icon'> & {
export type CallableHttpRequestAction = Pick<HttpRequestAction, "label" | "icon"> & {
call: (httpRequest: HttpRequest) => Promise<void>;
};
@@ -17,7 +17,7 @@ export function useHttpRequestActions() {
const pluginsKey = usePluginsKey();
const actionsResult = useQuery<CallableHttpRequestAction[]>({
queryKey: ['http_request_actions', pluginsKey],
queryKey: ["http_request_actions", pluginsKey],
queryFn: () => getHttpRequestActions(),
});
@@ -30,7 +30,7 @@ export function useHttpRequestActions() {
}
export async function getHttpRequestActions() {
const responses = await invokeCmd<GetHttpRequestActionsResponse[]>('cmd_http_request_actions');
const responses = await invokeCmd<GetHttpRequestActionsResponse[]>("cmd_http_request_actions");
const actions = responses.flatMap((r) =>
r.actions.map((a, i) => ({
label: a.label,
@@ -41,7 +41,7 @@ export async function getHttpRequestActions() {
pluginRefId: r.pluginRefId,
args: { httpRequest },
};
await invokeCmd('cmd_call_http_request_action', { req: payload });
await invokeCmd("cmd_call_http_request_action", { req: payload });
},
})),
);

View File

@@ -1,11 +1,11 @@
import { useQuery } from '@tanstack/react-query';
import type { HttpResponse } from '@yaakapp-internal/models';
import { invokeCmd } from '../lib/tauri';
import { useQuery } from "@tanstack/react-query";
import type { HttpResponse } from "@yaakapp-internal/models";
import { invokeCmd } from "../lib/tauri";
export function useHttpRequestBody(response: HttpResponse | null) {
return useQuery({
placeholderData: (prev) => prev, // Keep previous data on refetch
queryKey: ['request_body', response?.id, response?.state, response?.requestContentLength],
queryKey: ["request_body", response?.id, response?.state, response?.requestContentLength],
enabled: (response?.requestContentLength ?? 0) > 0,
queryFn: async () => {
return getRequestBodyText(response);
@@ -18,7 +18,7 @@ export async function getRequestBodyText(response: HttpResponse | null) {
return null;
}
const data = await invokeCmd<number[] | null>('cmd_http_request_body', {
const data = await invokeCmd<number[] | null>("cmd_http_request_body", {
responseId: response.id,
});
@@ -27,6 +27,6 @@ export async function getRequestBodyText(response: HttpResponse | null) {
}
const body = new Uint8Array(data);
const bodyText = new TextDecoder('utf-8', { fatal: false }).decode(body);
const bodyText = new TextDecoder("utf-8", { fatal: false }).decode(body);
return { body, bodyText };
}

View File

@@ -1,28 +1,30 @@
import { invoke } from '@tauri-apps/api/core';
import type { HttpResponse, HttpResponseEvent } from '@yaakapp-internal/models';
import { invoke } from "@tauri-apps/api/core";
import type { HttpResponse, HttpResponseEvent } from "@yaakapp-internal/models";
import {
httpResponseEventsAtom,
mergeModelsInStore,
replaceModelsInStore,
} from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { useEffect } from 'react';
import { fireAndForget } from '../lib/fireAndForget';
} from "@yaakapp-internal/models";
import { useAtomValue } from "jotai";
import { useEffect } from "react";
import { fireAndForget } from "../lib/fireAndForget";
export function useHttpResponseEvents(response: HttpResponse | null) {
const allEvents = useAtomValue(httpResponseEventsAtom);
useEffect(() => {
if (response?.id == null) {
replaceModelsInStore('http_response_event', []);
replaceModelsInStore("http_response_event", []);
return;
}
// Fetch events from database, filtering out events from other responses and merging atomically
fireAndForget(invoke<HttpResponseEvent[]>('cmd_get_http_response_events', { responseId: response.id }).then(
(events) =>
mergeModelsInStore('http_response_event', events, (e) => e.responseId === response.id),
));
fireAndForget(
invoke<HttpResponseEvent[]>("cmd_get_http_response_events", { responseId: response.id }).then(
(events) =>
mergeModelsInStore("http_response_event", events, (e) => e.responseId === response.id),
),
);
}, [response?.id]);
const events = allEvents.filter((e) => e.responseId === response?.id);

View File

@@ -1,16 +1,16 @@
import type { HttpRequest } from '@yaakapp-internal/models';
import { patchModelById } from '@yaakapp-internal/models';
import { createRequestAndNavigate } from '../lib/createRequestAndNavigate';
import { jotaiStore } from '../lib/jotai';
import { invokeCmd } from '../lib/tauri';
import { showToast } from '../lib/toast';
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
import { useFastMutation } from './useFastMutation';
import { wasUpdatedExternally } from './useRequestUpdateKey';
import type { HttpRequest } from "@yaakapp-internal/models";
import { patchModelById } from "@yaakapp-internal/models";
import { createRequestAndNavigate } from "../lib/createRequestAndNavigate";
import { jotaiStore } from "../lib/jotai";
import { invokeCmd } from "../lib/tauri";
import { showToast } from "../lib/toast";
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
import { useFastMutation } from "./useFastMutation";
import { wasUpdatedExternally } from "./useRequestUpdateKey";
export function useImportCurl() {
return useFastMutation({
mutationKey: ['import_curl'],
mutationKey: ["import_curl"],
mutationFn: async ({
overwriteRequestId,
command,
@@ -19,17 +19,17 @@ export function useImportCurl() {
command: string;
}) => {
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
const importedRequest: HttpRequest = await invokeCmd('cmd_curl_to_request', {
const importedRequest: HttpRequest = await invokeCmd("cmd_curl_to_request", {
command,
workspaceId,
});
let verb: string;
if (overwriteRequestId == null) {
verb = 'Created';
verb = "Created";
await createRequestAndNavigate(importedRequest);
} else {
verb = 'Updated';
verb = "Updated";
await patchModelById(importedRequest.model, overwriteRequestId, (r: HttpRequest) => ({
...importedRequest,
id: r.id,
@@ -44,7 +44,7 @@ export function useImportCurl() {
}
showToast({
color: 'success',
color: "success",
message: `${verb} request from Curl`,
});
},

View File

@@ -4,9 +4,9 @@ import type {
HttpRequest,
WebsocketRequest,
Workspace,
} from '@yaakapp-internal/models';
import { foldersAtom, workspacesAtom } from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
} from "@yaakapp-internal/models";
import { foldersAtom, workspacesAtom } from "@yaakapp-internal/models";
import { atom, useAtomValue } from "jotai";
const ancestorsAtom = atom((get) => [...get(foldersAtom), ...get(workspacesAtom)]);
@@ -19,7 +19,7 @@ export function useInheritedAuthentication(baseModel: AuthenticatedModel | null)
const next = (child: AuthenticatedModel) => {
// We hit the top
if (child.model === 'workspace') {
if (child.model === "workspace") {
return child.authenticationType == null ? null : child;
}

View File

@@ -5,10 +5,10 @@ import type {
HttpRequestHeader,
WebsocketRequest,
Workspace,
} from '@yaakapp-internal/models';
import { foldersAtom, workspacesAtom } from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
import { defaultHeaders } from '../lib/defaultHeaders';
} from "@yaakapp-internal/models";
import { foldersAtom, workspacesAtom } from "@yaakapp-internal/models";
import { atom, useAtomValue } from "jotai";
import { defaultHeaders } from "../lib/defaultHeaders";
const ancestorsAtom = atom((get) => [...get(foldersAtom), ...get(workspacesAtom)]);
@@ -18,11 +18,11 @@ export function useInheritedHeaders(baseModel: HeaderModel | null) {
const parents = useAtomValue(ancestorsAtom);
if (baseModel == null) return [];
if (baseModel.model === 'workspace') return defaultHeaders;
if (baseModel.model === "workspace") return defaultHeaders;
const next = (child: HeaderModel): HttpRequestHeader[] => {
// Short-circuit at workspace level - return global defaults + workspace headers
if (child.model === 'workspace') {
if (child.model === "workspace") {
return [...defaultHeaders, ...child.headers];
}

View File

@@ -1,9 +1,9 @@
import { installPluginFromDirectory } from '@yaakapp-internal/plugins';
import { useFastMutation } from './useFastMutation';
import { installPluginFromDirectory } from "@yaakapp-internal/plugins";
import { useFastMutation } from "./useFastMutation";
export function useInstallPlugin() {
return useFastMutation<void, unknown, string>({
mutationKey: ['install_plugin'],
mutationKey: ["install_plugin"],
mutationFn: async (directory: string) => {
await installPluginFromDirectory(directory);
},

View File

@@ -1,18 +1,18 @@
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { invoke } from '@tauri-apps/api/core';
import type { GraphQlIntrospection, HttpRequest } from '@yaakapp-internal/models';
import type { GraphQLSchema, IntrospectionQuery } from 'graphql';
import { buildClientSchema, getIntrospectionQuery } from 'graphql';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { minPromiseMillis } from '../lib/minPromiseMillis';
import { getResponseBodyText } from '../lib/responseBody';
import { sendEphemeralRequest } from '../lib/sendEphemeralRequest';
import { useActiveEnvironment } from './useActiveEnvironment';
import { useDebouncedValue } from './useDebouncedValue';
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { invoke } from "@tauri-apps/api/core";
import type { GraphQlIntrospection, HttpRequest } from "@yaakapp-internal/models";
import type { GraphQLSchema, IntrospectionQuery } from "graphql";
import { buildClientSchema, getIntrospectionQuery } from "graphql";
import { useCallback, useEffect, useMemo, useState } from "react";
import { minPromiseMillis } from "../lib/minPromiseMillis";
import { getResponseBodyText } from "../lib/responseBody";
import { sendEphemeralRequest } from "../lib/sendEphemeralRequest";
import { useActiveEnvironment } from "./useActiveEnvironment";
import { useDebouncedValue } from "./useDebouncedValue";
const introspectionRequestBody = JSON.stringify({
query: getIntrospectionQuery(),
operationName: 'IntrospectionQuery',
operationName: "IntrospectionQuery",
});
export function useIntrospectGraphQL(
@@ -32,17 +32,14 @@ export function useIntrospectGraphQL(
const upsertIntrospection = useCallback(
async (content: string | null) => {
const v = await invoke<GraphQlIntrospection>(
'models_upsert_graphql_introspection',
{
requestId: baseRequest.id,
workspaceId: baseRequest.workspaceId,
content: content ?? '',
},
);
const v = await invoke<GraphQlIntrospection>("models_upsert_graphql_introspection", {
requestId: baseRequest.id,
workspaceId: baseRequest.workspaceId,
content: content ?? "",
});
// Update local introspection
queryClient.setQueryData(['introspection', baseRequest.id], v);
queryClient.setQueryData(["introspection", baseRequest.id], v);
},
[baseRequest.id, baseRequest.workspaceId, queryClient],
);
@@ -54,7 +51,7 @@ export function useIntrospectGraphQL(
const args = {
...baseRequest,
bodyType: 'application/json',
bodyType: "application/json",
body: { text: introspectionRequestBody },
};
const response = await minPromiseMillis(
@@ -74,7 +71,7 @@ export function useIntrospectGraphQL(
}
if (bodyText === null) {
return setError('Empty body returned in response');
return setError("Empty body returned in response");
}
console.log(`Got introspection response for ${baseRequest.url}`, bodyText);
@@ -97,18 +94,18 @@ export function useIntrospectGraphQL(
}, [baseRequest.id, debouncedRequest.url, debouncedRequest.method, activeEnvironment?.id]);
const clear = useCallback(async () => {
setError('');
setError("");
setSchema(null);
await upsertIntrospection(null);
}, [upsertIntrospection]);
useEffect(() => {
if (introspection.data?.content == null || introspection.data.content === '') {
if (introspection.data?.content == null || introspection.data.content === "") {
return;
}
const parseResult = tryParseIntrospectionToSchema(introspection.data.content);
if ('error' in parseResult) {
if ("error" in parseResult) {
setError(parseResult.error);
} else {
setSchema(parseResult.schema);
@@ -120,9 +117,9 @@ export function useIntrospectGraphQL(
function useIntrospectionResult(request: HttpRequest) {
return useQuery({
queryKey: ['introspection', request.id],
queryKey: ["introspection", request.id],
queryFn: async () =>
invoke<GraphQlIntrospection | null>('models_get_graphql_introspection', {
invoke<GraphQlIntrospection | null>("models_get_graphql_introspection", {
requestId: request.id,
}),
});
@@ -132,9 +129,9 @@ export function useCurrentGraphQLSchema(request: HttpRequest) {
const result = useIntrospectionResult(request);
return useMemo(() => {
if (result.data == null) return null;
if (result.data.content == null || result.data.content === '') return null;
if (result.data.content == null || result.data.content === "") return null;
const r = tryParseIntrospectionToSchema(result.data.content);
return 'error' in r ? null : r.schema;
return "error" in r ? null : r.schema;
}, [result.data]);
}
@@ -146,13 +143,13 @@ function tryParseIntrospectionToSchema(
parsedResponse = JSON.parse(content).data;
// oxlint-disable-next-line no-explicit-any
} catch (e: any) {
return { error: String('message' in e ? e.message : e) };
return { error: String("message" in e ? e.message : e) };
}
try {
return { schema: buildClientSchema(parsedResponse, {}) };
// oxlint-disable-next-line no-explicit-any
} catch (e: any) {
return { error: String('message' in e ? e.message : e) };
return { error: String("message" in e ? e.message : e) };
}
}

View File

@@ -1,5 +1,5 @@
import { useAtomValue } from 'jotai';
import { activeWorkspaceMetaAtom } from './useActiveWorkspace';
import { useAtomValue } from "jotai";
import { activeWorkspaceMetaAtom } from "./useActiveWorkspace";
export function useIsEncryptionEnabled() {
const workspaceMeta = useAtomValue(activeWorkspaceMetaAtom);

View File

@@ -1,7 +1,7 @@
import { useQuery } from '@tanstack/react-query';
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
import { useWindowSize } from 'react-use';
import { useDebouncedValue } from './useDebouncedValue';
import { useQuery } from "@tanstack/react-query";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { useWindowSize } from "react-use";
import { useDebouncedValue } from "./useDebouncedValue";
export function useIsFullscreen() {
const windowSize = useWindowSize();
@@ -13,7 +13,7 @@ export function useIsFullscreen() {
return (
useQuery({
queryKey: ['is_fullscreen', debouncedWindowWidth],
queryKey: ["is_fullscreen", debouncedWindowWidth],
queryFn: async () => {
return getCurrentWebviewWindow().isFullscreen();
},

View File

@@ -1,20 +1,20 @@
import deepEqual from '@gilbarbara/deep-equal';
import { useMutation } from '@tanstack/react-query';
import { keyValuesAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { selectAtom } from 'jotai/utils';
import { useCallback, useMemo } from 'react';
import { jotaiStore } from '../lib/jotai';
import { buildKeyValueKey, extractKeyValueOrFallback, setKeyValue } from '../lib/keyValueStore';
import deepEqual from "@gilbarbara/deep-equal";
import { useMutation } from "@tanstack/react-query";
import { keyValuesAtom } from "@yaakapp-internal/models";
import { useAtomValue } from "jotai";
import { selectAtom } from "jotai/utils";
import { useCallback, useMemo } from "react";
import { jotaiStore } from "../lib/jotai";
import { buildKeyValueKey, extractKeyValueOrFallback, setKeyValue } from "../lib/keyValueStore";
const DEFAULT_NAMESPACE = 'global';
const DEFAULT_NAMESPACE = "global";
export function useKeyValue<T extends object | boolean | number | string | null>({
namespace = DEFAULT_NAMESPACE,
key,
fallback,
}: {
namespace?: 'global' | 'no_sync' | 'license';
namespace?: "global" | "no_sync" | "license";
key: string | string[];
fallback: T;
}) {
@@ -38,14 +38,14 @@ export function useKeyValue<T extends object | boolean | number | string | null>
);
const { mutateAsync } = useMutation<void, unknown, T>({
mutationKey: ['set_key_value', namespace, key],
mutationKey: ["set_key_value", namespace, key],
mutationFn: (value) => setKeyValue<T>({ namespace, key, value }),
});
// oxlint-disable-next-line react-hooks/exhaustive-deps
const set = useCallback(
async (valueOrUpdate: ((v: T) => T) | T) => {
if (typeof valueOrUpdate === 'function') {
if (typeof valueOrUpdate === "function") {
const newV = valueOrUpdate(value ?? fallback);
if (newV === value) return;
await mutateAsync(newV);
@@ -55,7 +55,7 @@ export function useKeyValue<T extends object | boolean | number | string | null>
await mutateAsync(valueOrUpdate);
}
},
[typeof key === 'string' ? key : key.join('::'), namespace, value],
[typeof key === "string" ? key : key.join("::"), namespace, value],
);
const reset = useCallback(async () => mutateAsync(fallback), [fallback, mutateAsync]);
@@ -76,7 +76,7 @@ export function getKeyValue<T extends object | boolean | number | string | null>
key,
fallback,
}: {
namespace?: 'global' | 'no_sync' | 'license';
namespace?: "global" | "no_sync" | "license";
key: string | string[];
fallback: T;
}) {

View File

@@ -1,8 +1,8 @@
import { useEffect } from 'react';
import { useEffect } from "react";
export function useKeyboardEvent(
event: 'keyup' | 'keydown',
key: KeyboardEvent['key'],
event: "keyup" | "keydown",
key: KeyboardEvent["key"],
cb: () => void,
) {
// oxlint-disable-next-line react-hooks/exhaustive-deps -- Don't have `cb` as a dep for caller convenience

View File

@@ -1,6 +1,6 @@
import type { GrpcConnection } from '@yaakapp-internal/models';
import { grpcConnectionsAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import type { GrpcConnection } from "@yaakapp-internal/models";
import { grpcConnectionsAtom } from "@yaakapp-internal/models";
import { useAtomValue } from "jotai";
export function useLatestGrpcConnection(requestId: string | null): GrpcConnection | null {
return useAtomValue(grpcConnectionsAtom).find((c) => c.requestId === requestId) ?? null;

View File

@@ -1,6 +1,6 @@
import type { HttpResponse } from '@yaakapp-internal/models';
import { httpResponsesAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import type { HttpResponse } from "@yaakapp-internal/models";
import { httpResponsesAtom } from "@yaakapp-internal/models";
import { useAtomValue } from "jotai";
export function useLatestHttpResponse(requestId: string | null): HttpResponse | null {
return useAtomValue(httpResponsesAtom).find((r) => r.requestId === requestId) ?? null;

View File

@@ -1,7 +1,7 @@
import type { EventCallback, EventName } from '@tauri-apps/api/event';
import { listen } from '@tauri-apps/api/event';
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
import { useEffect, useRef } from 'react';
import type { EventCallback, EventName } from "@tauri-apps/api/event";
import { listen } from "@tauri-apps/api/event";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { useEffect, useRef } from "react";
export function useListenToTauriEvent<T>(event: EventName, fn: EventCallback<T>) {
const handlerRef = useRef(fn);
@@ -19,7 +19,7 @@ export function listenToTauriEvent<T>(event: EventName, fn: EventCallback<T>) {
event,
fn,
// Listen to `emit_all()` events or events specific to the current window
{ target: { label: getCurrentWebviewWindow().label, kind: 'Window' } },
{ target: { label: getCurrentWebviewWindow().label, kind: "Window" } },
);
return () => {

View File

@@ -1,7 +1,7 @@
import type { AnyModel, Folder, Workspace } from '@yaakapp-internal/models';
import { foldersAtom, workspacesAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { useMemo } from 'react';
import type { AnyModel, Folder, Workspace } from "@yaakapp-internal/models";
import { foldersAtom, workspacesAtom } from "@yaakapp-internal/models";
import { useAtomValue } from "jotai";
import { useMemo } from "react";
type ModelAncestor = Folder | Workspace;
@@ -20,7 +20,7 @@ export function getModelAncestors(
if (currentModel == null) return [];
const parentFolder =
'folderId' in currentModel && currentModel.folderId
"folderId" in currentModel && currentModel.folderId
? folders.find((f) => f.id === currentModel.folderId)
: null;
@@ -29,7 +29,7 @@ export function getModelAncestors(
}
const parentWorkspace =
'workspaceId' in currentModel && currentModel.workspaceId
"workspaceId" in currentModel && currentModel.workspaceId
? workspaces.find((w) => w.id === currentModel.workspaceId)
: null;

View File

@@ -1,7 +1,7 @@
import type { Folder, GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models';
import { foldersAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { useMemo } from 'react';
import type { Folder, GrpcRequest, HttpRequest, WebsocketRequest } from "@yaakapp-internal/models";
import { foldersAtom } from "@yaakapp-internal/models";
import { useAtomValue } from "jotai";
import { useMemo } from "react";
export function useParentFolders(m: Folder | HttpRequest | GrpcRequest | WebsocketRequest | null) {
const folders = useAtomValue(foldersAtom);

View File

@@ -1,19 +1,19 @@
import { invoke } from '@tauri-apps/api/core';
import type { GrpcConnection, GrpcEvent } from '@yaakapp-internal/models';
import { invoke } from "@tauri-apps/api/core";
import type { GrpcConnection, GrpcEvent } from "@yaakapp-internal/models";
import {
grpcConnectionsAtom,
grpcEventsAtom,
mergeModelsInStore,
replaceModelsInStore,
} from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
import { useEffect, useMemo } from 'react';
import { fireAndForget } from '../lib/fireAndForget';
import { atomWithKVStorage } from '../lib/atoms/atomWithKVStorage';
import { activeRequestIdAtom } from './useActiveRequestId';
} from "@yaakapp-internal/models";
import { atom, useAtomValue } from "jotai";
import { useEffect, useMemo } from "react";
import { fireAndForget } from "../lib/fireAndForget";
import { atomWithKVStorage } from "../lib/atoms/atomWithKVStorage";
import { activeRequestIdAtom } from "./useActiveRequestId";
const pinnedGrpcConnectionIdsAtom = atomWithKVStorage<Record<string, string | null>>(
'pinned-grpc-connection-ids',
"pinned-grpc-connection-ids",
{},
);
@@ -42,16 +42,16 @@ export const pinnedGrpcConnectionIdAtom = atom(
);
function recordKey(activeRequestId: string | null, latestConnection: GrpcConnection | null) {
return `${activeRequestId}-${latestConnection?.id ?? 'none'}`;
return `${activeRequestId}-${latestConnection?.id ?? "none"}`;
}
export const activeGrpcConnections = atom<GrpcConnection[]>((get) => {
const activeRequestId = get(activeRequestIdAtom) ?? 'n/a';
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 activeRequestId = get(activeRequestIdAtom) ?? "n/a";
const activeConnections = get(activeGrpcConnections);
const latestConnection = activeConnections[0] ?? null;
const pinnedConnectionId = get(pinnedGrpcConnectionIdsAtom)[
@@ -65,14 +65,16 @@ export function useGrpcEvents(connectionId: string | null) {
useEffect(() => {
if (connectionId == null) {
replaceModelsInStore('grpc_event', []);
replaceModelsInStore("grpc_event", []);
return;
}
// Fetch events from database, filtering out events from other connections and merging atomically
fireAndForget(invoke<GrpcEvent[]>('models_grpc_events', { connectionId }).then((events) =>
mergeModelsInStore('grpc_event', events, (e) => e.connectionId === connectionId),
));
fireAndForget(
invoke<GrpcEvent[]>("models_grpc_events", { connectionId }).then((events) =>
mergeModelsInStore("grpc_event", events, (e) => e.connectionId === connectionId),
),
);
}, [connectionId]);
return useMemo(

View File

@@ -1,16 +1,16 @@
import type { HttpResponse } from '@yaakapp-internal/models';
import { httpResponsesAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { useKeyValue } from './useKeyValue';
import { useLatestHttpResponse } from './useLatestHttpResponse';
import type { HttpResponse } from "@yaakapp-internal/models";
import { httpResponsesAtom } from "@yaakapp-internal/models";
import { useAtomValue } from "jotai";
import { useKeyValue } from "./useKeyValue";
import { useLatestHttpResponse } from "./useLatestHttpResponse";
export function usePinnedHttpResponse(activeRequestId: string) {
const latestResponse = useLatestHttpResponse(activeRequestId);
const { set, value: pinnedResponseId } = useKeyValue<string | null>({
// Key on the latest response instead of activeRequest because responses change out of band of active request
key: ['pinned_http_response_id', latestResponse?.id ?? 'n/a'],
key: ["pinned_http_response_id", latestResponse?.id ?? "n/a"],
fallback: null,
namespace: 'global',
namespace: "global",
});
const allResponses = useAtomValue(httpResponsesAtom);
const responses = allResponses.filter((r) => r.requestId === activeRequestId);

View File

@@ -1,34 +1,34 @@
import { invoke } from '@tauri-apps/api/core';
import type { WebsocketConnection, WebsocketEvent } from '@yaakapp-internal/models';
import { invoke } from "@tauri-apps/api/core";
import type { WebsocketConnection, WebsocketEvent } from "@yaakapp-internal/models";
import {
mergeModelsInStore,
replaceModelsInStore,
websocketConnectionsAtom,
websocketEventsAtom,
} from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
import { useEffect, useMemo } from 'react';
import { fireAndForget } from '../lib/fireAndForget';
import { atomWithKVStorage } from '../lib/atoms/atomWithKVStorage';
import { jotaiStore } from '../lib/jotai';
import { activeRequestIdAtom } from './useActiveRequestId';
} from "@yaakapp-internal/models";
import { atom, useAtomValue } from "jotai";
import { useEffect, useMemo } from "react";
import { fireAndForget } from "../lib/fireAndForget";
import { atomWithKVStorage } from "../lib/atoms/atomWithKVStorage";
import { jotaiStore } from "../lib/jotai";
import { activeRequestIdAtom } from "./useActiveRequestId";
const pinnedWebsocketConnectionIdAtom = atomWithKVStorage<Record<string, string | null>>(
'pinned-websocket-connection-ids',
"pinned-websocket-connection-ids",
{},
);
function recordKey(activeRequestId: string | null, latestConnection: WebsocketConnection | null) {
return `${activeRequestId}-${latestConnection?.id ?? 'none'}`;
return `${activeRequestId}-${latestConnection?.id ?? "none"}`;
}
export const activeWebsocketConnectionsAtom = atom<WebsocketConnection[]>((get) => {
const activeRequestId = get(activeRequestIdAtom) ?? 'n/a';
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 activeRequestId = get(activeRequestIdAtom) ?? "n/a";
const activeConnections = get(activeWebsocketConnectionsAtom);
const latestConnection = activeConnections[0] ?? null;
const pinnedConnectionId = get(pinnedWebsocketConnectionIdAtom)[
@@ -52,14 +52,16 @@ export function useWebsocketEvents(connectionId: string | null) {
useEffect(() => {
if (connectionId == null) {
replaceModelsInStore('websocket_event', []);
replaceModelsInStore("websocket_event", []);
return;
}
// Fetch events from database, filtering out events from other connections and merging atomically
fireAndForget(invoke<WebsocketEvent[]>('models_websocket_events', { connectionId }).then((events) =>
mergeModelsInStore('websocket_event', events, (e) => e.connectionId === connectionId),
));
fireAndForget(
invoke<WebsocketEvent[]>("models_websocket_events", { connectionId }).then((events) =>
mergeModelsInStore("websocket_event", events, (e) => e.connectionId === connectionId),
),
);
}, [connectionId]);
return useMemo(

View File

@@ -1,13 +1,13 @@
import { useQuery } from '@tanstack/react-query';
import type { Plugin } from '@yaakapp-internal/models';
import { pluginsAtom } from '@yaakapp-internal/models';
import type { PluginMetadata } from '@yaakapp-internal/plugins';
import { useAtomValue } from 'jotai';
import { queryClient } from '../lib/queryClient';
import { invokeCmd } from '../lib/tauri';
import { useQuery } from "@tanstack/react-query";
import type { Plugin } from "@yaakapp-internal/models";
import { pluginsAtom } from "@yaakapp-internal/models";
import type { PluginMetadata } from "@yaakapp-internal/plugins";
import { useAtomValue } from "jotai";
import { queryClient } from "../lib/queryClient";
import { invokeCmd } from "../lib/tauri";
function pluginInfoKey(id: string | null, plugin: Plugin | null) {
return ['plugin_info', id ?? 'n/a', plugin?.updatedAt ?? 'n/a'];
return ["plugin_info", id ?? "n/a", plugin?.updatedAt ?? "n/a"];
}
export function usePluginInfo(id: string | null) {
@@ -19,11 +19,11 @@ export function usePluginInfo(id: string | null) {
placeholderData: (prev) => prev, // Keep previous data on refetch
queryFn: () => {
if (id == null) return null;
return invokeCmd<PluginMetadata>('cmd_plugin_info', { id });
return invokeCmd<PluginMetadata>("cmd_plugin_info", { id });
},
});
}
export function invalidateAllPluginInfo() {
queryClient.invalidateQueries({ queryKey: ['plugin_info'] }).catch(console.error);
queryClient.invalidateQueries({ queryKey: ["plugin_info"] }).catch(console.error);
}

View File

@@ -1,17 +1,17 @@
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';
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
import { useDebouncedValue } from './useDebouncedValue';
import { invalidateAllPluginInfo } from './usePluginInfo';
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";
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
import { useDebouncedValue } from "./useDebouncedValue";
import { invalidateAllPluginInfo } from "./usePluginInfo";
export function usePluginsKey() {
const pluginKey = useAtomValue(pluginsAtom)
.map((p) => p.id + p.updatedAt)
.join(',');
.join(",");
// Debounce plugins both for efficiency and to give plugins a chance to reload after the DB updates
return useDebouncedValue(pluginKey, 1000);
@@ -22,11 +22,11 @@ export function usePluginsKey() {
*/
export function useRefreshPlugins() {
return useMutation({
mutationKey: ['refresh_plugins'],
mutationKey: ["refresh_plugins"],
mutationFn: async () => {
await minPromiseMillis(
(async () => {
await invokeCmd('cmd_reload_plugins');
await invokeCmd("cmd_reload_plugins");
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
await changeModelStoreWorkspace(workspaceId); // Force refresh models
invalidateAllPluginInfo();

View File

@@ -1,6 +1,6 @@
import { useRef } from 'react';
import { useRef } from "react";
const PORTAL_CONTAINER_ID = 'react-portal';
const PORTAL_CONTAINER_ID = "react-portal";
export function usePortal(name: string) {
const ref = useRef(getOrCreatePortal(name));
@@ -11,8 +11,8 @@ function getOrCreatePortal(name: string) {
const portalContainer = document.getElementById(PORTAL_CONTAINER_ID) as HTMLDivElement;
let existing = portalContainer.querySelector(`:scope > [data-portal-name="${name}"]`);
if (!existing) {
const el: HTMLDivElement = document.createElement('div');
el.setAttribute('data-portal-name', name);
const el: HTMLDivElement = document.createElement("div");
el.setAttribute("data-portal-name", name);
portalContainer.appendChild(el);
existing = el;
}

View File

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

View File

@@ -1,5 +1,5 @@
import { useCallback, useState } from 'react';
import { generateId } from '../lib/generateId';
import { useCallback, useState } from "react";
import { generateId } from "../lib/generateId";
export function useRandomKey(initialValue?: string) {
const [value, setValue] = useState<string>(initialValue ?? generateId());

View File

@@ -1,19 +1,19 @@
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 { useKeyValue } from './useKeyValue';
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 { useKeyValue } from "./useKeyValue";
const kvKey = (workspaceId: string) => `recent_cookie_jars::${workspaceId}`;
const namespace = 'global';
const namespace = "global";
const fallback: string[] = [];
export function useRecentCookieJars() {
const cookieJars = useAtomValue(cookieJarsAtom);
const kv = useKeyValue<string[]>({
key: kvKey(cookieJars[0]?.workspaceId ?? 'n/a'),
key: kvKey(cookieJars[0]?.workspaceId ?? "n/a"),
namespace,
fallback,
});

View File

@@ -1,18 +1,18 @@
import { useEffect, useMemo } from 'react';
import { jotaiStore } from '../lib/jotai';
import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
import { activeEnvironmentAtom } from './useActiveEnvironment';
import { useEnvironmentsBreakdown } from './useEnvironmentsBreakdown';
import { useKeyValue } from './useKeyValue';
import { useEffect, useMemo } from "react";
import { jotaiStore } from "../lib/jotai";
import { getKeyValue, setKeyValue } from "../lib/keyValueStore";
import { activeEnvironmentAtom } from "./useActiveEnvironment";
import { useEnvironmentsBreakdown } from "./useEnvironmentsBreakdown";
import { useKeyValue } from "./useKeyValue";
const kvKey = (workspaceId: string) => `recent_environments::${workspaceId}`;
const namespace = 'global';
const namespace = "global";
const fallback: string[] = [];
export function useRecentEnvironments() {
const { subEnvironments, allEnvironments } = useEnvironmentsBreakdown();
const kv = useKeyValue<string[]>({
key: kvKey(allEnvironments[0]?.workspaceId ?? 'n/a'),
key: kvKey(allEnvironments[0]?.workspaceId ?? "n/a"),
namespace,
fallback,
});

View File

@@ -1,19 +1,19 @@
import { useEffect, useMemo } from 'react';
import { jotaiStore } from '../lib/jotai';
import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
import { activeRequestAtom } from './useActiveRequest';
import { useAllRequests } from './useAllRequests';
import { useKeyValue } from './useKeyValue';
import { useEffect, useMemo } from "react";
import { jotaiStore } from "../lib/jotai";
import { getKeyValue, setKeyValue } from "../lib/keyValueStore";
import { activeRequestAtom } from "./useActiveRequest";
import { useAllRequests } from "./useAllRequests";
import { useKeyValue } from "./useKeyValue";
const kvKey = (workspaceId: string) => `recent_requests::${workspaceId}`;
const namespace = 'global';
const namespace = "global";
const fallback: string[] = [];
export function useRecentRequests() {
const requests = useAllRequests();
const { set: setRecentRequests, value: recentRequests } = useKeyValue<string[]>({
key: kvKey(requests[0]?.workspaceId ?? 'n/a'),
key: kvKey(requests[0]?.workspaceId ?? "n/a"),
namespace,
fallback,
});

View File

@@ -1,13 +1,13 @@
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 { 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";
const kvKey = () => 'recent_workspaces';
const namespace = 'global';
const kvKey = () => "recent_workspaces";
const namespace = "global";
const fallback: string[] = [];
export function useRecentWorkspaces() {
@@ -43,6 +43,6 @@ async function updateRecentWorkspaces() {
const withoutActiveId = recentIds.filter((id) => id !== activeWorkspaceId);
const value = [activeWorkspaceId, ...withoutActiveId];
console.log('Recent workspaces update', activeWorkspaceId);
console.log("Recent workspaces update", activeWorkspaceId);
await setKeyValue({ namespace, key, value });
}

View File

@@ -1,10 +1,10 @@
import { useQuery } from '@tanstack/react-query';
import type { RenderPurpose } from '@yaakapp-internal/plugins';
import { useAtomValue } from 'jotai';
import { minPromiseMillis } from '../lib/minPromiseMillis';
import { invokeCmd } from '../lib/tauri';
import { useActiveEnvironment } from './useActiveEnvironment';
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
import { useQuery } from "@tanstack/react-query";
import type { RenderPurpose } from "@yaakapp-internal/plugins";
import { useAtomValue } from "jotai";
import { minPromiseMillis } from "../lib/minPromiseMillis";
import { invokeCmd } from "../lib/tauri";
import { useActiveEnvironment } from "./useActiveEnvironment";
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
export function useRenderTemplate({
template,
@@ -21,13 +21,13 @@ export function useRenderTemplate({
ignoreError?: boolean;
preservePreviousValue?: boolean;
}) {
const workspaceId = useAtomValue(activeWorkspaceIdAtom) ?? 'n/a';
const workspaceId = useAtomValue(activeWorkspaceIdAtom) ?? "n/a";
const environmentId = useActiveEnvironment()?.id ?? null;
return useQuery<string>({
refetchOnWindowFocus: false,
enabled,
placeholderData: preservePreviousValue ? (prev) => prev : undefined,
queryKey: ['render_template', workspaceId, environmentId, refreshKey, purpose, ignoreError],
queryKey: ["render_template", workspaceId, environmentId, refreshKey, purpose, ignoreError],
queryFn: () =>
minPromiseMillis(
renderTemplate({ template, workspaceId, environmentId, purpose, ignoreError }),
@@ -49,7 +49,7 @@ export async function renderTemplate({
purpose: RenderPurpose;
ignoreError?: boolean;
}): Promise<string> {
return invokeCmd('cmd_render_template', {
return invokeCmd("cmd_render_template", {
template,
workspaceId,
environmentId,
@@ -67,5 +67,5 @@ export async function decryptTemplate({
workspaceId: string;
environmentId: string | null;
}): Promise<string> {
return invokeCmd('cmd_decrypt_template', { template, workspaceId, environmentId });
return invokeCmd("cmd_decrypt_template", { template, workspaceId, environmentId });
}

View File

@@ -1,11 +1,11 @@
import EventEmitter from 'eventemitter3';
import { atom, useAtom } from 'jotai';
import type { DependencyList } from 'react';
import { useCallback, useEffect } from 'react';
import EventEmitter from "eventemitter3";
import { atom, useAtom } from "jotai";
import type { DependencyList } from "react";
import { useCallback, useEffect } from "react";
type EventDataMap = {
'request_params.focus_value': string;
'request_pane.focus_tab': undefined;
"request_params.focus_value": string;
"request_pane.focus_tab": undefined;
};
export function useRequestEditorEvent<
@@ -28,13 +28,13 @@ export function useRequestEditor() {
const [urlParametersKey, setUrlParametersKey] = useAtom(urlParamsKeyAtom);
const [urlKey, setUrlKey] = useAtom(urlKeyAtom);
const focusParamsTab = useCallback(() => {
emitter.emit('request_pane.focus_tab', undefined);
emitter.emit("request_pane.focus_tab", undefined);
}, []);
const focusParamValue = useCallback(
(name: string) => {
focusParamsTab();
requestAnimationFrame(() => emitter.emit('request_params.focus_value', name));
requestAnimationFrame(() => emitter.emit("request_params.focus_value", name));
},
[focusParamsTab],
);

View File

@@ -1,22 +1,22 @@
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';
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 requestUpdateKeyAtom = atom<Record<string, string>>({});
getCurrentWebviewWindow()
.listen<ModelPayload>('model_write', ({ payload }) => {
if (payload.change.type !== 'upsert') return;
.listen<ModelPayload>("model_write", ({ payload }) => {
if (payload.change.type !== "upsert") return;
if (
(payload.model.model === 'http_request' ||
payload.model.model === 'grpc_request' ||
payload.model.model === 'websocket_request') &&
((payload.updateSource.type === 'window' &&
(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')
payload.updateSource.type !== "window")
) {
wasUpdatedExternally(payload.model.id);
}
@@ -29,6 +29,6 @@ export function wasUpdatedExternally(changedRequestId: string) {
export function useRequestUpdateKey(requestId: string | null) {
const keys = useAtomValue(requestUpdateKeyAtom);
const key = keys[requestId ?? 'n/a'];
return `${requestId}::${key ?? 'default'}`;
const key = keys[requestId ?? "n/a"];
return `${requestId}::${key ?? "default"}`;
}

View File

@@ -1,7 +1,7 @@
import { settingsAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { resolveAppearance } from '../lib/theme/appearance';
import { usePreferredAppearance } from './usePreferredAppearance';
import { settingsAtom } from "@yaakapp-internal/models";
import { useAtomValue } from "jotai";
import { resolveAppearance } from "../lib/theme/appearance";
import { usePreferredAppearance } from "./usePreferredAppearance";
export function useResolvedAppearance() {
const preferredAppearance = usePreferredAppearance();

View File

@@ -1,9 +1,9 @@
import { useQuery } from '@tanstack/react-query';
import { settingsAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { getResolvedTheme, getThemes } from '../lib/theme/themes';
import { usePluginsKey } from './usePlugins';
import { usePreferredAppearance } from './usePreferredAppearance';
import { useQuery } from "@tanstack/react-query";
import { settingsAtom } from "@yaakapp-internal/models";
import { useAtomValue } from "jotai";
import { getResolvedTheme, getThemes } from "../lib/theme/themes";
import { usePluginsKey } from "./usePlugins";
import { usePreferredAppearance } from "./usePreferredAppearance";
export function useResolvedTheme() {
const preferredAppearance = usePreferredAppearance();
@@ -11,7 +11,7 @@ export function useResolvedTheme() {
const pluginKey = usePluginsKey();
return useQuery({
placeholderData: (prev) => prev,
queryKey: ['resolved_theme', preferredAppearance, settings.updatedAt, pluginKey],
queryKey: ["resolved_theme", preferredAppearance, settings.updatedAt, pluginKey],
queryFn: async () => {
const data = await getResolvedTheme(
preferredAppearance,

View File

@@ -1,12 +1,12 @@
import { useQuery } from '@tanstack/react-query';
import type { HttpResponse } from '@yaakapp-internal/models';
import type { ServerSentEvent } from '@yaakapp-internal/sse';
import { getResponseBodyEventSource } from '../lib/responseBody';
import { useQuery } from "@tanstack/react-query";
import type { HttpResponse } from "@yaakapp-internal/models";
import type { ServerSentEvent } from "@yaakapp-internal/sse";
import { getResponseBodyEventSource } from "../lib/responseBody";
export function useResponseBodyEventSource(response: HttpResponse) {
return useQuery<ServerSentEvent[]>({
placeholderData: (prev) => prev, // Keep previous data on refetch
queryKey: ['response-body-event-source', response.id, response.contentLength],
queryKey: ["response-body-event-source", response.id, response.contentLength],
queryFn: () => getResponseBodyEventSource(response),
});
}

View File

@@ -1,6 +1,6 @@
import { useQuery } from '@tanstack/react-query';
import type { HttpResponse } from '@yaakapp-internal/models';
import { getResponseBodyBytes, getResponseBodyText } from '../lib/responseBody';
import { useQuery } from "@tanstack/react-query";
import type { HttpResponse } from "@yaakapp-internal/models";
import { getResponseBodyBytes, getResponseBodyText } from "../lib/responseBody";
export function useResponseBodyText({
response,
@@ -12,11 +12,11 @@ export function useResponseBodyText({
return useQuery({
placeholderData: (prev) => prev, // Keep previous data on refetch
queryKey: [
'response_body_text',
"response_body_text",
response.id,
response.updatedAt,
response.contentLength,
filter ?? '',
filter ?? "",
],
queryFn: () => getResponseBodyText({ response, filter }),
});
@@ -25,7 +25,7 @@ export function useResponseBodyText({
export function useResponseBodyBytes({ response }: { response: HttpResponse }) {
return useQuery({
placeholderData: (prev) => prev, // Keep previous data on refetch
queryKey: ['response_body_bytes', response.id, response.updatedAt, response.contentLength],
queryKey: ["response_body_bytes", response.id, response.updatedAt, response.contentLength],
queryFn: () => getResponseBodyBytes(response),
});
}

View File

@@ -1,8 +1,8 @@
import { useLocalStorage } from 'react-use';
import { useLocalStorage } from "react-use";
const DEFAULT_VIEW_MODE = 'pretty';
const DEFAULT_VIEW_MODE = "pretty";
export function useResponseViewMode(requestId?: string): [string, (m: 'pretty' | 'raw') => void] {
const [value, setValue] = useLocalStorage<'pretty' | 'raw'>(`response_view_mode::${requestId}`);
export function useResponseViewMode(requestId?: string): [string, (m: "pretty" | "raw") => void] {
const [value, setValue] = useLocalStorage<"pretty" | "raw">(`response_view_mode::${requestId}`);
return [value ?? DEFAULT_VIEW_MODE, setValue];
}

View File

@@ -1,29 +1,29 @@
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';
import { getContentTypeFromHeaders } from '../lib/model_util';
import { invokeCmd } from '../lib/tauri';
import { showToast } from '../lib/toast';
import { useFastMutation } from './useFastMutation';
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";
import { getContentTypeFromHeaders } from "../lib/model_util";
import { invokeCmd } from "../lib/tauri";
import { showToast } from "../lib/toast";
import { useFastMutation } from "./useFastMutation";
export function useSaveResponse(response: HttpResponse) {
return useFastMutation({
mutationKey: ['save_response', response.id],
mutationKey: ["save_response", response.id],
mutationFn: async () => {
const request = getModel('http_request', response.requestId);
const request = getModel("http_request", response.requestId);
if (request == null) return null;
const contentType = getContentTypeFromHeaders(response.headers) ?? 'unknown';
const contentType = getContentTypeFromHeaders(response.headers) ?? "unknown";
const ext = mime.getExtension(contentType);
const slug = slugify(request.name || 'response', { lower: true });
const slug = slugify(request.name || "response", { lower: true });
const filepath = await save({
defaultPath: ext ? `${slug}.${ext}` : slug,
title: 'Save Response',
title: "Save Response",
});
await invokeCmd('cmd_save_response', { responseId: response.id, filepath });
await invokeCmd("cmd_save_response", { responseId: response.id, filepath });
showToast({
message: (
<>

View File

@@ -1,9 +1,9 @@
import { useEffect } from 'react';
import { useEffect } from "react";
export function useScrollIntoView<T extends HTMLElement>(node: T | null, enabled: boolean) {
useEffect(() => {
if (enabled) {
node?.scrollIntoView({ block: 'nearest' });
node?.scrollIntoView({ block: "nearest" });
}
}, [enabled, node]);
}

View File

@@ -1,20 +1,20 @@
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 { createFastMutation, useFastMutation } from './useFastMutation';
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 { createFastMutation, useFastMutation } from "./useFastMutation";
export function useSendAnyHttpRequest() {
return useFastMutation<HttpResponse | null, string, string | null>({
mutationKey: ['send_any_request'],
mutationKey: ["send_any_request"],
mutationFn: async (id) => {
const request = getModel('http_request', id ?? 'n/a');
const request = getModel("http_request", id ?? "n/a");
if (request == null) {
return null;
}
return invokeCmd('cmd_send_http_request', {
return invokeCmd("cmd_send_http_request", {
request,
environmentId: getActiveEnvironment()?.id,
cookieJarId: getActiveCookieJar()?.id,
@@ -24,14 +24,14 @@ export function useSendAnyHttpRequest() {
}
export const sendAnyHttpRequest = createFastMutation<HttpResponse | null, string, string | null>({
mutationKey: ['send_any_request'],
mutationKey: ["send_any_request"],
mutationFn: async (id) => {
const request = getModel('http_request', id ?? 'n/a');
const request = getModel("http_request", id ?? "n/a");
if (request == null) {
return null;
}
return invokeCmd('cmd_send_http_request', {
return invokeCmd("cmd_send_http_request", {
request,
environmentId: getActiveEnvironment()?.id,
cookieJarId: getActiveCookieJar()?.id,

View File

@@ -1,10 +1,10 @@
import { useFastMutation } from './useFastMutation';
import { useSendAnyHttpRequest } from './useSendAnyHttpRequest';
import { useFastMutation } from "./useFastMutation";
import { useSendAnyHttpRequest } from "./useSendAnyHttpRequest";
export function useSendManyRequests() {
const sendAnyRequest = useSendAnyHttpRequest();
return useFastMutation<void, string, string[]>({
mutationKey: ['send_many_requests'],
mutationKey: ["send_many_requests"],
mutationFn: async (requestIds: string[]) => {
for (const id of requestIds) {
sendAnyRequest.mutate(id);

View File

@@ -1,4 +1,4 @@
import { useWindowSize } from 'react-use';
import { useWindowSize } from "react-use";
const WINDOW_FLOATING_SIDEBAR_WIDTH = 600;

View File

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

View File

@@ -1,9 +1,9 @@
import { atom } from 'jotai';
import { atomWithKVStorage } from '../lib/atoms/atomWithKVStorage';
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
import { atom } from "jotai";
import { atomWithKVStorage } from "../lib/atoms/atomWithKVStorage";
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
function kvKey(workspaceId: string | null) {
return ['sidebar_collapsed', workspaceId ?? 'n/a'];
return ["sidebar_collapsed", workspaceId ?? "n/a"];
}
export const sidebarCollapsedAtom = atom((get) => {

View File

@@ -1,12 +1,12 @@
import { useAtomValue } from 'jotai';
import { useCallback } from 'react';
import { useLocalStorage } from 'react-use';
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
import { useAtomValue } from "jotai";
import { useCallback } from "react";
import { useLocalStorage } from "react-use";
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
export function useSidebarWidth() {
const activeWorkspaceId = useAtomValue(activeWorkspaceIdAtom);
const [width, setWidth] = useLocalStorage<number>(
`sidebar_width::${activeWorkspaceId ?? 'n/a'}`,
`sidebar_width::${activeWorkspaceId ?? "n/a"}`,
250,
);
const resetWidth = useCallback(() => setWidth(250), [setWidth]);

View File

@@ -1,5 +1,5 @@
import type { DependencyList } from 'react';
import { useEffect, useState } from 'react';
import type { DependencyList } from "react";
import { useEffect, useState } from "react";
/**
* Like useState, except it will update the value when the default value changes

View File

@@ -1,8 +1,8 @@
import { type } from '@tauri-apps/plugin-os';
import { useIsFullscreen } from './useIsFullscreen';
import { type } from "@tauri-apps/plugin-os";
import { useIsFullscreen } from "./useIsFullscreen";
export function useStoplightsVisible() {
const fullscreen = useIsFullscreen();
const stoplightsVisible = type() === 'macos' && !fullscreen;
const stoplightsVisible = type() === "macos" && !fullscreen;
return stoplightsVisible;
}

View File

@@ -1,7 +1,7 @@
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
import { settingsAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { useEffect } from 'react';
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { settingsAtom } from "@yaakapp-internal/models";
import { useAtomValue } from "jotai";
import { useEffect } from "react";
export function useSyncFontSizeSetting() {
const settings = useAtomValue(settingsAtom);
@@ -12,6 +12,6 @@ export function useSyncFontSizeSetting() {
const { interfaceScale, editorFontSize } = settings;
getCurrentWebviewWindow().setZoom(interfaceScale).catch(console.error);
document.documentElement.style.setProperty('--editor-font-size', `${editorFontSize}px`);
document.documentElement.style.setProperty("--editor-font-size", `${editorFontSize}px`);
}, [settings]);
}

View File

@@ -1,7 +1,7 @@
import { changeModelStoreWorkspace } from '@yaakapp-internal/models';
import { useEffect } from 'react';
import { jotaiStore } from '../lib/jotai';
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
import { changeModelStoreWorkspace } from "@yaakapp-internal/models";
import { useEffect } from "react";
import { jotaiStore } from "../lib/jotai";
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
export function useSyncWorkspaceChildModels() {
useEffect(() => {

View File

@@ -1,13 +1,13 @@
import { setWindowTitle } from '@yaakapp-internal/mac-window';
import { settingsAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { useEffect } from 'react';
import { appInfo } from '../lib/appInfo';
import { jotaiStore } from '../lib/jotai';
import { resolvedModelName } from '../lib/resolvedModelName';
import { useActiveEnvironment } from './useActiveEnvironment';
import { activeRequestAtom } from './useActiveRequest';
import { activeWorkspaceAtom } from './useActiveWorkspace';
import { setWindowTitle } from "@yaakapp-internal/mac-window";
import { settingsAtom } from "@yaakapp-internal/models";
import { useAtomValue } from "jotai";
import { useEffect } from "react";
import { appInfo } from "../lib/appInfo";
import { jotaiStore } from "../lib/jotai";
import { resolvedModelName } from "../lib/resolvedModelName";
import { useActiveEnvironment } from "./useActiveEnvironment";
import { activeRequestAtom } from "./useActiveRequest";
import { activeWorkspaceAtom } from "./useActiveWorkspace";
export function useSyncWorkspaceRequestTitle() {
const activeWorkspace = useAtomValue(activeWorkspaceAtom);
@@ -16,7 +16,7 @@ export function useSyncWorkspaceRequestTitle() {
useEffect(() => {
const settings = jotaiStore.get(settingsAtom);
let newTitle = activeWorkspace ? activeWorkspace.name : 'Yaak';
let newTitle = activeWorkspace ? activeWorkspace.name : "Yaak";
if (activeEnvironment) {
newTitle += ` (${activeEnvironment.name})`;
}

View File

@@ -1,16 +1,16 @@
import { useHotKey } from './useHotKey';
import { useListenToTauriEvent } from './useListenToTauriEvent';
import { useZoom } from './useZoom';
import { useHotKey } from "./useHotKey";
import { useListenToTauriEvent } from "./useListenToTauriEvent";
import { useZoom } from "./useZoom";
export function useSyncZoomSetting() {
// Handle Zoom.
// Note, Mac handles it in the app menu, so need to also handle keyboard
// shortcuts for Windows/Linux
const zoom = useZoom();
useHotKey('app.zoom_in', zoom.zoomIn);
useListenToTauriEvent('zoom_in', zoom.zoomIn);
useHotKey('app.zoom_out', zoom.zoomOut);
useListenToTauriEvent('zoom_out', zoom.zoomOut);
useHotKey('app.zoom_reset', zoom.zoomReset);
useListenToTauriEvent('zoom_reset', zoom.zoomReset);
useHotKey("app.zoom_in", zoom.zoomIn);
useListenToTauriEvent("zoom_in", zoom.zoomIn);
useHotKey("app.zoom_out", zoom.zoomOut);
useListenToTauriEvent("zoom_out", zoom.zoomOut);
useHotKey("app.zoom_reset", zoom.zoomReset);
useListenToTauriEvent("zoom_reset", zoom.zoomReset);
}

View File

@@ -1,4 +1,4 @@
import { useQuery } from '@tanstack/react-query';
import { useQuery } from "@tanstack/react-query";
import {
environmentsAtom,
type Folder,
@@ -8,13 +8,13 @@ import {
pluginsAtom,
type WebsocketRequest,
type Workspace,
} from '@yaakapp-internal/models';
import type { GetTemplateFunctionConfigResponse, JsonPrimitive } from '@yaakapp-internal/plugins';
import { useAtomValue } from 'jotai';
import { md5 } from 'js-md5';
import { invokeCmd } from '../lib/tauri';
import { activeEnvironmentIdAtom } from './useActiveEnvironment';
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
} from "@yaakapp-internal/models";
import type { GetTemplateFunctionConfigResponse, JsonPrimitive } from "@yaakapp-internal/plugins";
import { useAtomValue } from "jotai";
import { md5 } from "js-md5";
import { invokeCmd } from "../lib/tauri";
import { activeEnvironmentIdAtom } from "./useActiveEnvironment";
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
export function useTemplateFunctionConfig(
functionName: string | null,
@@ -26,20 +26,20 @@ export function useTemplateFunctionConfig(
const environmentId = useAtomValue(activeEnvironmentIdAtom);
const responses = useAtomValue(httpResponsesAtom);
const environments = useAtomValue(environmentsAtom);
const environmentsKey = environments.map((e) => e.id + e.updatedAt).join(':');
const environmentsKey = environments.map((e) => e.id + e.updatedAt).join(":");
// Some auth handlers like OAuth 2.0 show the current token after a successful request. To
// handle that, we'll force the auth to re-fetch after each new response closes
const responseKey = md5(
responses
.filter((r) => r.state === 'closed')
.filter((r) => r.state === "closed")
.map((r) => r.id)
.join(':'),
.join(":"),
);
return useQuery({
queryKey: [
'template_function_config',
"template_function_config",
model,
functionName,
values,
@@ -64,7 +64,7 @@ export async function getTemplateFunctionConfig(
environmentId: string | undefined,
) {
const config = await invokeCmd<GetTemplateFunctionConfigResponse>(
'cmd_template_function_config',
"cmd_template_function_config",
{
functionName,
values,

View File

@@ -1,13 +1,13 @@
import { useQuery } from '@tanstack/react-query';
import { useQuery } from "@tanstack/react-query";
import type {
GetTemplateFunctionSummaryResponse,
TemplateFunction,
} from '@yaakapp-internal/plugins';
import { atom, useAtomValue, useSetAtom } from 'jotai';
import { useMemo, useState } from 'react';
import type { TwigCompletionOption } from '../components/core/Editor/twig/completion';
import { invokeCmd } from '../lib/tauri';
import { usePluginsKey } from './usePlugins';
} from "@yaakapp-internal/plugins";
import { atom, useAtomValue, useSetAtom } from "jotai";
import { useMemo, useState } from "react";
import type { TwigCompletionOption } from "../components/core/Editor/twig/completion";
import { invokeCmd } from "../lib/tauri";
import { usePluginsKey } from "./usePlugins";
const templateFunctionsAtom = atom<TemplateFunction[]>([]);
@@ -21,9 +21,9 @@ export function useTemplateFunctionCompletionOptions(
return [];
}
return templateFunctions.map((fn) => {
const argsLabel = fn.args.length > 0 ? '…' : '';
const argsLabel = fn.args.length > 0 ? "…" : "";
const fn2: TwigCompletionOption = {
type: 'function',
type: "function",
onClick: (rawTag: string, startPos: number) => onClick(fn, rawTag, startPos),
label: `${fn.name}(${argsLabel})`,
invalid: false,
@@ -41,7 +41,7 @@ export function useSubscribeTemplateFunctions() {
const setAtom = useSetAtom(templateFunctionsAtom);
useQuery({
queryKey: ['template_functions', pluginsKey],
queryKey: ["template_functions", pluginsKey],
// Fetch periodically until functions are returned
// NOTE: visibilitychange (refetchOnWindowFocus) does not work on Windows, so we'll rely on this logic
// to refetch things until that's working again
@@ -50,7 +50,7 @@ export function useSubscribeTemplateFunctions() {
refetchOnMount: true,
queryFn: async () => {
const result = await invokeCmd<GetTemplateFunctionSummaryResponse[]>(
'cmd_template_function_summaries',
"cmd_template_function_summaries",
);
setNumFns(result.length);
const functions = result.flatMap((r) => r.functions) ?? [];

View File

@@ -1,15 +1,15 @@
import { useQuery } from '@tanstack/react-query';
import type { Tokens } from '@yaakapp-internal/templates';
import { invokeCmd } from '../lib/tauri';
import { useQuery } from "@tanstack/react-query";
import type { Tokens } from "@yaakapp-internal/templates";
import { invokeCmd } from "../lib/tauri";
export function useTemplateTokensToString(tokens: Tokens) {
return useQuery<string>({
refetchOnWindowFocus: false,
queryKey: ['template_tokens_to_string', tokens],
queryKey: ["template_tokens_to_string", tokens],
queryFn: () => templateTokensToString(tokens),
});
}
export async function templateTokensToString(tokens: Tokens): Promise<string> {
return invokeCmd('cmd_template_tokens_to_string', { tokens });
return invokeCmd("cmd_template_tokens_to_string", { tokens });
}

View File

@@ -1,5 +1,5 @@
import { useRef, useState } from 'react';
import { useUnmount } from 'react-use';
import { useRef, useState } from "react";
import { useUnmount } from "react-use";
/** Returns a boolean that is true for a given number of milliseconds. */
export function useTimedBoolean(millis = 1500): [boolean, () => void] {

View File

@@ -1,12 +1,12 @@
import type { TimelineViewMode } from '../components/HttpResponsePane';
import { useKeyValue } from './useKeyValue';
import type { TimelineViewMode } from "../components/HttpResponsePane";
import { useKeyValue } from "./useKeyValue";
const DEFAULT_VIEW_MODE: TimelineViewMode = 'timeline';
const DEFAULT_VIEW_MODE: TimelineViewMode = "timeline";
export function useTimelineViewMode() {
const { set, value } = useKeyValue<TimelineViewMode>({
namespace: 'no_sync',
key: 'timeline_view_mode',
namespace: "no_sync",
key: "timeline_view_mode",
fallback: DEFAULT_VIEW_MODE,
});

View File

@@ -1,4 +1,4 @@
import { useCallback, useState } from 'react';
import { useCallback, useState } from "react";
export function useToggle(initialValue = false) {
const [value, setValue] = useState<boolean>(initialValue);

View File

@@ -1,15 +1,15 @@
import { useCallback } from 'react';
import { CommandPaletteDialog } from '../components/CommandPaletteDialog';
import { toggleDialog } from '../lib/dialog';
import { useCallback } from "react";
import { CommandPaletteDialog } from "../components/CommandPaletteDialog";
import { toggleDialog } from "../lib/dialog";
export function useToggleCommandPalette() {
const togglePalette = useCallback(() => {
toggleDialog({
id: 'command_palette',
size: 'dynamic',
id: "command_palette",
size: "dynamic",
hideX: true,
className: 'mb-auto mt-[4rem] !max-h-[min(30rem,calc(100vh-4rem))]',
vAlign: 'top',
className: "mb-auto mt-[4rem] !max-h-[min(30rem,calc(100vh-4rem))]",
vAlign: "top",
noPadding: true,
noScroll: true,
render: ({ hide }) => <CommandPaletteDialog onClose={hide} />,

View File

@@ -1,15 +1,15 @@
import { useQuery } from '@tanstack/react-query';
import type { WebsocketRequest } from '@yaakapp-internal/models';
import { useQuery } from "@tanstack/react-query";
import type { WebsocketRequest } from "@yaakapp-internal/models";
import type {
CallWebsocketRequestActionRequest,
GetWebsocketRequestActionsResponse,
WebsocketRequestAction,
} from '@yaakapp-internal/plugins';
import { useMemo } from 'react';
import { invokeCmd } from '../lib/tauri';
import { usePluginsKey } from './usePlugins';
} from "@yaakapp-internal/plugins";
import { useMemo } from "react";
import { invokeCmd } from "../lib/tauri";
import { usePluginsKey } from "./usePlugins";
export type CallableWebSocketRequestAction = Pick<WebsocketRequestAction, 'label' | 'icon'> & {
export type CallableWebSocketRequestAction = Pick<WebsocketRequestAction, "label" | "icon"> & {
call: (request: WebsocketRequest) => Promise<void>;
};
@@ -17,7 +17,7 @@ export function useWebsocketRequestActions() {
const pluginsKey = usePluginsKey();
const actionsResult = useQuery<CallableWebSocketRequestAction[]>({
queryKey: ['websocket_request_actions', pluginsKey],
queryKey: ["websocket_request_actions", pluginsKey],
queryFn: () => getWebsocketRequestActions(),
});
@@ -31,7 +31,7 @@ export function useWebsocketRequestActions() {
export async function getWebsocketRequestActions() {
const responses = await invokeCmd<GetWebsocketRequestActionsResponse[]>(
'cmd_websocket_request_actions',
"cmd_websocket_request_actions",
);
const actions = responses.flatMap((r) =>
r.actions.map((a: WebsocketRequestAction, i: number) => ({
@@ -43,7 +43,7 @@ export async function getWebsocketRequestActions() {
pluginRefId: r.pluginRefId,
args: { websocketRequest },
};
await invokeCmd('cmd_call_websocket_request_action', { req: payload });
await invokeCmd("cmd_call_websocket_request_action", { req: payload });
},
})),
);

View File

@@ -1,6 +1,6 @@
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
import { useEffect, useState } from 'react';
import { fireAndForget } from '../lib/fireAndForget';
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { useEffect, useState } from "react";
import { fireAndForget } from "../lib/fireAndForget";
export function useWindowFocus() {
const [visible, setVisible] = useState(true);

Some files were not shown because too many files have changed in this diff Show More