Start on plugin ctx API (#64)

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

View File

@@ -4,9 +4,8 @@ import type { KeyboardEvent, ReactNode } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useActiveCookieJar } from '../hooks/useActiveCookieJar';
import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
import { useActiveEnvironmentId } from '../hooks/useActiveEnvironmentId';
import { useActiveRequest } from '../hooks/useActiveRequest';
import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId';
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
import { useAppRoutes } from '../hooks/useAppRoutes';
import { useCreateEnvironment } from '../hooks/useCreateEnvironment';
import { useCreateGrpcRequest } from '../hooks/useCreateGrpcRequest';
@@ -56,8 +55,8 @@ const MAX_PER_GROUP = 8;
export function CommandPalette({ onClose }: { onClose: () => void }) {
const [command, setCommand] = useDebouncedState<string>('', 150);
const [selectedItemKey, setSelectedItemKey] = useState<string | null>(null);
const [activeEnvironment, setActiveEnvironmentId] = useActiveEnvironment();
const routes = useAppRoutes();
const activeEnvironmentId = useActiveEnvironmentId();
const workspaces = useWorkspaces();
const environments = useEnvironments();
const recentEnvironments = useRecentEnvironments();
@@ -68,12 +67,11 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
const openWorkspace = useOpenWorkspace();
const createWorkspace = useCreateWorkspace();
const createHttpRequest = useCreateHttpRequest();
const { activeCookieJar } = useActiveCookieJar();
const [activeCookieJar] = useActiveCookieJar();
const createGrpcRequest = useCreateGrpcRequest();
const createEnvironment = useCreateEnvironment();
const dialog = useDialog();
const workspaceId = useActiveWorkspaceId();
const activeEnvironment = useActiveEnvironment();
const workspace = useActiveWorkspace();
const sendRequest = useSendAnyHttpRequest();
const renameRequest = useRenameRequest(activeRequest?.id ?? null);
const deleteRequest = useDeleteRequest(activeRequest?.id ?? null);
@@ -86,9 +84,9 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
label: 'Open Settings',
action: 'settings.show',
onSelect: async () => {
if (workspaceId == null) return;
if (workspace == null) return;
await invokeCmd('cmd_new_nested_window', {
url: routes.paths.workspaceSettings({ workspaceId }),
url: routes.paths.workspaceSettings({ workspaceId: workspace.id }),
label: 'settings',
title: 'Yaak Settings',
});
@@ -190,7 +188,7 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
routes.paths,
sendRequest,
setSidebarHidden,
workspaceId,
workspace,
]);
const sortedRequests = useMemo(() => {
@@ -271,7 +269,7 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
return routes.navigate('request', {
workspaceId: r.workspaceId,
requestId: r.id,
environmentId: activeEnvironmentId ?? undefined,
environmentId: activeEnvironment?.id,
});
},
});
@@ -290,7 +288,7 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
environmentGroup.items.push({
key: `switch-environment-${e.id}`,
label: e.name,
onSelect: () => routes.setEnvironment(e),
onSelect: () => setActiveEnvironmentId(e.id),
});
}
@@ -313,9 +311,9 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
workspaceCommands,
sortedRequests,
routes,
activeEnvironmentId,
activeEnvironment,
sortedEnvironments,
activeEnvironment?.id,
setActiveEnvironmentId,
sortedWorkspaces,
openWorkspace,
]);

View File

@@ -12,7 +12,7 @@ interface Props {
export const CookieDialog = function ({ cookieJarId }: Props) {
const updateCookieJar = useUpdateCookieJar(cookieJarId ?? null);
const cookieJars = useCookieJars();
const cookieJars = useCookieJars().data ?? [];
const cookieJar = cookieJars.find((c) => c.id === cookieJarId);
if (cookieJar == null) {

View File

@@ -12,8 +12,8 @@ import { InlineCode } from './core/InlineCode';
import { useDialog } from './DialogContext';
export function CookieDropdown() {
const cookieJars = useCookieJars();
const { activeCookieJar, setActiveCookieJarId } = useActiveCookieJar();
const cookieJars = useCookieJars().data ?? [];
const [activeCookieJar, setActiveCookieJarId] = useActiveCookieJar();
const updateCookieJar = useUpdateCookieJar(activeCookieJar?.id ?? null);
const deleteCookieJar = useDeleteCookieJar(activeCookieJar ?? null);
const createCookieJar = useCreateCookieJar();

View File

@@ -1,7 +1,6 @@
import classNames from 'classnames';
import { memo, useCallback, useMemo } from 'react';
import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
import { useAppRoutes } from '../hooks/useAppRoutes';
import { useEnvironments } from '../hooks/useEnvironments';
import type { ButtonProps } from './core/Button';
import { Button } from './core/Button';
@@ -20,9 +19,8 @@ export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdo
...buttonProps
}: Props) {
const environments = useEnvironments();
const activeEnvironment = useActiveEnvironment();
const [activeEnvironment, setActiveEnvironmentId] = useActiveEnvironment();
const dialog = useDialog();
const routes = useAppRoutes();
const showEnvironmentDialog = useCallback(() => {
dialog.toggle({
@@ -43,9 +41,9 @@ export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdo
leftSlot: e.id === activeEnvironment?.id ? <Icon icon="check" /> : <Icon icon="empty" />,
onSelect: async () => {
if (e.id !== activeEnvironment?.id) {
routes.setEnvironment(e);
setActiveEnvironmentId(e.id);
} else {
routes.setEnvironment(null);
setActiveEnvironmentId(null);
}
},
}),
@@ -62,7 +60,7 @@ export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdo
onSelect: showEnvironmentDialog,
},
],
[activeEnvironment?.id, environments, routes, showEnvironmentDialog],
[activeEnvironment?.id, environments, setActiveEnvironmentId, showEnvironmentDialog],
);
return (

View File

@@ -2,7 +2,8 @@ import { useQueryClient } from '@tanstack/react-query';
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
import type { Model } from '@yaakapp/api';
import { useEffect } from 'react';
import { useAtiveWorkspaceChangedToast } from '../hooks/useAtiveWorkspaceChangedToast';
import { useEnsureActiveCookieJar, useMigrateActiveCookieJarId } from '../hooks/useActiveCookieJar';
import { useActiveWorkspaceChangedToast } from '../hooks/useActiveWorkspaceChangedToast';
import { cookieJarsQueryKey } from '../hooks/useCookieJars';
import { useCopy } from '../hooks/useCopy';
import { environmentsQueryKey } from '../hooks/useEnvironments';
@@ -16,6 +17,7 @@ import { httpResponsesQueryKey } from '../hooks/useHttpResponses';
import { keyValueQueryKey } from '../hooks/useKeyValue';
import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent';
import { useNotificationToast } from '../hooks/useNotificationToast';
import { useRecentCookieJars } from '../hooks/useRecentCookieJars';
import { useRecentEnvironments } from '../hooks/useRecentEnvironments';
import { useRecentRequests } from '../hooks/useRecentRequests';
import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces';
@@ -25,6 +27,7 @@ import { useSyncThemeToDocument } from '../hooks/useSyncThemeToDocument';
import { useToggleCommandPalette } from '../hooks/useToggleCommandPalette';
import { workspacesQueryKey } from '../hooks/useWorkspaces';
import { useZoom } from '../hooks/useZoom';
import { extractKeyValue } from '../lib/keyValueStore';
import { modelsEq } from '../lib/models';
import { catppuccinMacchiato } from '../lib/theme/themes/catppuccin';
import { githubLight } from '../lib/theme/themes/github';
@@ -38,12 +41,17 @@ export function GlobalHooks() {
// Include here so they always update, even if no component references them
useRecentWorkspaces();
useRecentEnvironments();
useRecentCookieJars();
useRecentRequests();
// Other useful things
useSyncThemeToDocument();
useNotificationToast();
useAtiveWorkspaceChangedToast();
useActiveWorkspaceChangedToast();
useEnsureActiveCookieJar();
// TODO: Remove in future version
useMigrateActiveCookieJarId();
const toggleCommandPalette = useToggleCommandPalette();
useHotKey('command_palette.toggle', toggleCommandPalette);
@@ -96,21 +104,28 @@ export function GlobalHooks() {
model.model,
);
if (shouldIgnoreModel(model)) return;
if (shouldIgnoreModel(model, windowLabel)) return;
queryClient.setQueryData<Model[]>(queryKey, (values = []) => {
const index = values.findIndex((v) => modelsEq(v, model)) ?? -1;
if (index >= 0) {
return [...values.slice(0, index), model, ...values.slice(index + 1)];
} else {
return pushToFront ? [model, ...(values ?? [])] : [...(values ?? []), model];
queryClient.setQueryData(queryKey, (current: unknown) => {
if (model.model === 'key_value') {
// Special-case for KeyValue
return extractKeyValue(model);
}
if (Array.isArray(current)) {
const index = current.findIndex((v) => modelsEq(v, model)) ?? -1;
if (index >= 0) {
return [...current.slice(0, index), model, ...current.slice(index + 1)];
} else {
return pushToFront ? [model, ...(current ?? [])] : [...(current ?? []), model];
}
}
});
});
useListenToTauriEvent<ModelPayload>('deleted_model', ({ payload }) => {
const { model } = payload;
if (shouldIgnoreModel(model)) return;
const { model, windowLabel } = payload;
if (shouldIgnoreModel(model, windowLabel)) return;
if (model.model === 'workspace') {
queryClient.setQueryData(workspacesQueryKey(), removeById(model));
@@ -181,7 +196,11 @@ function removeById<T extends { id: string }>(model: T) {
return (entries: T[] | undefined) => entries?.filter((e) => e.id !== model.id);
}
const shouldIgnoreModel = (payload: Model) => {
const shouldIgnoreModel = (payload: Model, windowLabel: string) => {
if (windowLabel === getCurrentWebviewWindow().label) {
// Never ignore same-window updates
return false;
}
if (payload.model === 'key_value') {
return payload.namespace === 'no_sync';
}

View File

@@ -52,12 +52,12 @@ export function MoveToWorkspaceDialog({ onDone, request, activeWorkspaceId }: Pr
if (request.model === 'http_request') {
await updateHttpRequest.mutateAsync(args);
queryClient.invalidateQueries({
await queryClient.invalidateQueries({
queryKey: httpRequestsQueryKey({ workspaceId: activeWorkspaceId }),
});
} else if (request.model === 'grpc_request') {
await updateGrpcRequest.mutateAsync(args);
queryClient.invalidateQueries({
await queryClient.invalidateQueries({
queryKey: grpcRequestsQueryKey({ workspaceId: activeWorkspaceId }),
});
}

View File

@@ -3,7 +3,7 @@ import { useMemo, useRef } from 'react';
import { useKeyPressEvent } from 'react-use';
import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
import { useActiveRequest } from '../hooks/useActiveRequest';
import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId';
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
import { useAppRoutes } from '../hooks/useAppRoutes';
import { useHotKey } from '../hooks/useHotKey';
import { useRecentRequests } from '../hooks/useRecentRequests';
@@ -18,8 +18,8 @@ import { HttpMethodTag } from './core/HttpMethodTag';
export function RecentRequestsDropdown({ className }: Pick<ButtonProps, 'className'>) {
const dropdownRef = useRef<DropdownRef>(null);
const activeRequest = useActiveRequest();
const activeWorkspaceId = useActiveWorkspaceId();
const activeEnvironment = useActiveEnvironment();
const activeWorkspace = useActiveWorkspace();
const [activeEnvironment] = useActiveEnvironment();
const routes = useAppRoutes();
const allRecentRequestIds = useRecentRequests();
const recentRequestIds = useMemo(() => allRecentRequestIds.slice(1), [allRecentRequestIds]);
@@ -42,7 +42,7 @@ export function RecentRequestsDropdown({ className }: Pick<ButtonProps, 'classNa
});
const items = useMemo<DropdownItem[]>(() => {
if (activeWorkspaceId === null) return [];
if (activeWorkspace === null) return [];
const recentRequestItems: DropdownItem[] = [];
for (const id of recentRequestIds) {
@@ -58,7 +58,7 @@ export function RecentRequestsDropdown({ className }: Pick<ButtonProps, 'classNa
routes.navigate('request', {
requestId: request.id,
environmentId: activeEnvironment?.id,
workspaceId: activeWorkspaceId,
workspaceId: activeWorkspace.id,
});
},
});
@@ -76,7 +76,7 @@ export function RecentRequestsDropdown({ className }: Pick<ButtonProps, 'classNa
}
return recentRequestItems.slice(0, 20);
}, [activeWorkspaceId, activeEnvironment?.id, recentRequestIds, requests, routes]);
}, [activeWorkspace, activeEnvironment?.id, recentRequestIds, requests, routes]);
return (
<Dropdown ref={dropdownRef} items={items}>

View File

@@ -1,6 +1,6 @@
import { open } from '@tauri-apps/plugin-shell';
import { useRef } from 'react';
import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId';
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
import { useAppInfo } from '../hooks/useAppInfo';
import { useAppRoutes } from '../hooks/useAppRoutes';
import { useCheckForUpdates } from '../hooks/useCheckForUpdates';
@@ -23,12 +23,12 @@ export function SettingsDropdown() {
const dialog = useDialog();
const checkForUpdates = useCheckForUpdates();
const routes = useAppRoutes();
const workspaceId = useActiveWorkspaceId();
const workspace = useActiveWorkspace();
const showSettings = async () => {
if (!workspaceId) return;
if (!workspace) return;
await invokeCmd('cmd_new_nested_window', {
url: routes.paths.workspaceSettings({ workspaceId }),
url: routes.paths.workspaceSettings({ workspaceId: workspace.id }),
label: 'settings',
title: 'Yaak Settings',
});

View File

@@ -1,11 +1,12 @@
import type { Folder, GrpcRequest, HttpRequest, Workspace } from '@yaakapp/api';
import classNames from 'classnames';
import type { ReactNode } from 'react';
import React, { Fragment, useCallback, useMemo, useRef, useState } from 'react';
import type { XYCoord } from 'react-dnd';
import { useDrag, useDrop } from 'react-dnd';
import { useKey, useKeyPressEvent } from 'react-use';
import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
import { useActiveEnvironmentId } from '../hooks/useActiveEnvironmentId';
import { useActiveRequest } from '../hooks/useActiveRequest';
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
import { useAppRoutes } from '../hooks/useAppRoutes';
@@ -32,7 +33,6 @@ import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
import { useWorkspaces } from '../hooks/useWorkspaces';
import { fallbackRequestName } from '../lib/fallbackRequestName';
import type { Folder, GrpcRequest, HttpRequest, Workspace } from '@yaakapp/api';
import { isResponseLoading } from '../lib/models';
import type { DropdownItem } from './core/Dropdown';
import { ContextMenu } from './core/Dropdown';
@@ -61,7 +61,7 @@ export function Sidebar({ className }: Props) {
const [hidden, setHidden] = useSidebarHidden();
const sidebarRef = useRef<HTMLLIElement>(null);
const activeRequest = useActiveRequest();
const activeEnvironmentId = useActiveEnvironmentId();
const [activeEnvironment] = useActiveEnvironment();
const folders = useFolders();
const requests = useRequests();
const activeWorkspace = useActiveWorkspace();
@@ -207,14 +207,14 @@ export function Sidebar({ className }: Props) {
routes.navigate('request', {
requestId: id,
workspaceId: item.workspaceId,
environmentId: activeEnvironmentId ?? undefined,
environmentId: activeEnvironment?.id,
});
setSelectedId(id);
setSelectedTree(tree);
if (!opts.noFocus) focusActiveRequest({ forced: { id, tree } });
}
},
[treeParentMap, collapsed, routes, activeEnvironmentId, focusActiveRequest],
[treeParentMap, collapsed, routes, activeEnvironment, focusActiveRequest],
);
const handleClearSelected = useCallback(() => {
@@ -260,7 +260,7 @@ export function Sidebar({ className }: Props) {
routes.navigate('request', {
requestId: selected.id,
workspaceId: activeWorkspace?.id,
environmentId: activeEnvironmentId ?? undefined,
environmentId: activeEnvironment?.id,
});
});

View File

@@ -5,7 +5,6 @@ import { useCallback, useMemo, useRef, useState } from 'react';
import { useWindowSize } from 'react-use';
import { useActiveRequest } from '../hooks/useActiveRequest';
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId';
import { useFloatingSidebarHidden } from '../hooks/useFloatingSidebarHidden';
import { useImportData } from '../hooks/useImportData';
import { useShouldFloatSidebar } from '../hooks/useShouldFloatSidebar';
@@ -16,7 +15,6 @@ import { useWorkspaces } from '../hooks/useWorkspaces';
import { Banner } from './core/Banner';
import { Button } from './core/Button';
import { HotKeyList } from './core/HotKeyList';
import { InlineCode } from './core/InlineCode';
import { FeedbackLink } from './core/Link';
import { HStack } from './core/Stacks';
import { CreateDropdown } from './CreateDropdown';
@@ -38,7 +36,6 @@ export default function Workspace() {
useSyncWorkspaceRequestTitle();
const workspaces = useWorkspaces();
const activeWorkspace = useActiveWorkspace();
const activeWorkspaceId = useActiveWorkspaceId();
const { setWidth, width, resetWidth } = useSidebarWidth();
const [sidebarHidden, setSidebarHidden] = useSidebarHidden();
const [floatingSidebarHidden, setFloatingSidebarHidden] = useFloatingSidebarHidden();
@@ -176,9 +173,8 @@ export default function Workspace() {
{activeWorkspace == null ? (
<div className="m-auto">
<Banner color="warning" className="max-w-[30rem]">
The active workspace{' '}
<InlineCode className="text-warning">{activeWorkspaceId}</InlineCode> was not found.
Select a workspace from the header menu or report this bug to <FeedbackLink />
The active workspace was not found. Select a workspace from the header menu or report
this bug to <FeedbackLink />
</Banner>
</div>
) : activeRequest == null ? (

View File

@@ -87,7 +87,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
ref,
) {
const s = useSettings();
const e = useActiveEnvironment();
const [e] = useActiveEnvironment();
const w = useActiveWorkspace();
const environment = autocompleteVariables ? e : null;
const workspace = autocompleteVariables ? w : null;

View File

@@ -9,7 +9,6 @@ class PlaceholderWidget extends WidgetType {
readonly exists: boolean,
readonly type: 'function' | 'variable' = 'variable',
) {
console.log('PLACEHOLDER', { name, value });
super();
}

View File

@@ -3,7 +3,7 @@ import classNames from 'classnames';
import type { CSSProperties, MouseEvent as ReactMouseEvent, ReactNode } from 'react';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { useLocalStorage } from 'react-use';
import { useActiveWorkspaceId } from '../../hooks/useActiveWorkspaceId';
import { useActiveWorkspace } from '../../hooks/useActiveWorkspace';
import { clamp } from '../../lib/clamp';
import { ResizeHandle } from '../ResizeHandle';
@@ -42,10 +42,13 @@ export function SplitLayout({
minWidthPx = 10,
}: Props) {
const containerRef = useRef<HTMLDivElement>(null);
const activeWorkspace = useActiveWorkspace();
const [verticalBasedOnSize, setVerticalBasedOnSize] = useState<boolean>(false);
const [widthRaw, setWidth] = useLocalStorage<number>(`${name}_width::${useActiveWorkspaceId()}`);
const [widthRaw, setWidth] = useLocalStorage<number>(
`${name}_width::${activeWorkspace?.id ?? 'n/a'}`,
);
const [heightRaw, setHeight] = useLocalStorage<number>(
`${name}_height::${useActiveWorkspaceId()}`,
`${name}_height::${activeWorkspace?.id ?? 'n/a'}`,
);
const width = widthRaw ?? defaultRatio;
let height = heightRaw ?? defaultRatio;