mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-21 17:09:09 +01:00
Fixed the circular imports and things
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import classNames from 'classnames';
|
||||
import { fuzzyFilter } from 'fuzzbunny';
|
||||
import type { KeyboardEvent, ReactNode } from 'react';
|
||||
@@ -11,6 +12,7 @@ import { useCreateHttpRequest } from '../hooks/useCreateHttpRequest';
|
||||
import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
|
||||
import { useDebouncedState } from '../hooks/useDebouncedState';
|
||||
import { useDeleteRequest } from '../hooks/useDeleteRequest';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
import { useEnvironments } from '../hooks/useEnvironments';
|
||||
import type { HotkeyAction } from '../hooks/useHotKey';
|
||||
import { useHotKey } from '../hooks/useHotKey';
|
||||
@@ -27,8 +29,6 @@ import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest';
|
||||
import { useSidebarHidden } from '../hooks/useSidebarHidden';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||
import { router } from '../main';
|
||||
import { Route } from '../routes/workspaces/$workspaceId/requests/$requestId';
|
||||
import { CookieDialog } from './CookieDialog';
|
||||
import { Button } from './core/Button';
|
||||
import { Heading } from './core/Heading';
|
||||
@@ -37,7 +37,6 @@ import { HttpMethodTag } from './core/HttpMethodTag';
|
||||
import { Icon } from './core/Icon';
|
||||
import { PlainInput } from './core/PlainInput';
|
||||
import { HStack } from './core/Stacks';
|
||||
import { useDialog } from './DialogContext';
|
||||
import { EnvironmentEditDialog } from './EnvironmentEditDialog';
|
||||
|
||||
interface CommandPaletteGroup {
|
||||
@@ -78,6 +77,7 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
|
||||
const deleteRequest = useDeleteRequest(activeRequest?.id ?? null);
|
||||
const [, setSidebarHidden] = useSidebarHidden();
|
||||
const openSettings = useOpenSettings();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const workspaceCommands = useMemo<CommandPaletteItem[]>(() => {
|
||||
const commands: CommandPaletteItem[] = [
|
||||
@@ -267,9 +267,9 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
|
||||
<div className="truncate">{fallbackRequestName(r)}</div>
|
||||
</HStack>
|
||||
),
|
||||
onSelect: () => {
|
||||
router.navigate({
|
||||
to: Route.fullPath,
|
||||
onSelect: async () => {
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: {
|
||||
workspaceId: r.workspaceId,
|
||||
requestId: r.id,
|
||||
@@ -315,8 +315,9 @@ export function CommandPalette({ onClose }: { onClose: () => void }) {
|
||||
}, [
|
||||
workspaceCommands,
|
||||
sortedRequests,
|
||||
activeEnvironment?.id,
|
||||
navigate,
|
||||
sortedEnvironments,
|
||||
activeEnvironment?.id,
|
||||
setActiveEnvironmentId,
|
||||
sortedWorkspaces,
|
||||
openWorkspace,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Dropdown, type DropdownItem } from './core/Dropdown';
|
||||
import { Icon } from './core/Icon';
|
||||
import { IconButton } from './core/IconButton';
|
||||
import { InlineCode } from './core/InlineCode';
|
||||
import { useDialog } from './DialogContext';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
|
||||
export function CookieDropdown() {
|
||||
const cookieJars = useCookieJars() ?? [];
|
||||
|
||||
@@ -1,80 +1,5 @@
|
||||
import React, { createContext, useContext, useMemo, useState } from 'react';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import type { DialogProps } from './core/Dialog';
|
||||
import { Dialog } from './core/Dialog';
|
||||
|
||||
type DialogEntry = {
|
||||
id: string;
|
||||
render: ({ hide }: { hide: () => void }) => React.ReactNode;
|
||||
} & Omit<DialogProps, 'open' | 'children'>;
|
||||
|
||||
interface State {
|
||||
dialogs: DialogEntry[];
|
||||
actions: Actions;
|
||||
}
|
||||
|
||||
interface Actions {
|
||||
show: (d: DialogEntry) => void;
|
||||
toggle: (d: DialogEntry) => void;
|
||||
hide: (id: string) => void;
|
||||
}
|
||||
import { createContext } from 'react';
|
||||
import type { DialogState } from './Dialogs';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const DialogContext = createContext<State>({} as State);
|
||||
|
||||
export const DialogProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [dialogs, setDialogs] = useState<State['dialogs']>([]);
|
||||
const actions = useMemo<Actions>(
|
||||
() => ({
|
||||
show({ id, ...props }: DialogEntry) {
|
||||
trackEvent('dialog', 'show', { id });
|
||||
setDialogs((a) => [...a.filter((d) => d.id !== id), { id, ...props }]);
|
||||
},
|
||||
toggle({ id, ...props }: DialogEntry) {
|
||||
if (dialogs.some((d) => d.id === id)) this.hide(id);
|
||||
else this.show({ id, ...props });
|
||||
},
|
||||
hide: (id: string) => {
|
||||
setDialogs((a) => a.filter((d) => d.id !== id));
|
||||
},
|
||||
}),
|
||||
[dialogs],
|
||||
);
|
||||
|
||||
const state: State = {
|
||||
dialogs,
|
||||
actions,
|
||||
};
|
||||
|
||||
return <DialogContext.Provider value={state}>{children}</DialogContext.Provider>;
|
||||
};
|
||||
|
||||
function DialogInstance({ id, render, onClose, ...props }: DialogEntry) {
|
||||
const { actions } = useContext(DialogContext);
|
||||
const children = render({ hide: () => actions.hide(id) });
|
||||
return (
|
||||
<Dialog
|
||||
open
|
||||
onClose={() => {
|
||||
onClose?.();
|
||||
actions.hide(id);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export const useDialog = () => useContext(DialogContext).actions;
|
||||
|
||||
export function Dialogs() {
|
||||
const { dialogs } = useContext(DialogContext);
|
||||
return (
|
||||
<>
|
||||
{dialogs.map((props: DialogEntry) => (
|
||||
<DialogInstance key={props.id} {...props} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
export const DialogContext = createContext<DialogState>({} as DialogState);
|
||||
|
||||
75
src-web/components/Dialogs.tsx
Normal file
75
src-web/components/Dialogs.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import React, { useContext, useMemo, useState } from 'react';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { Dialog, type DialogProps } from './core/Dialog';
|
||||
import { DialogContext } from './DialogContext';
|
||||
|
||||
type DialogEntry = {
|
||||
id: string;
|
||||
render: ({ hide }: { hide: () => void }) => React.ReactNode;
|
||||
} & Omit<DialogProps, 'open' | 'children'>;
|
||||
|
||||
export interface DialogState {
|
||||
dialogs: DialogEntry[];
|
||||
actions: Actions;
|
||||
}
|
||||
|
||||
interface Actions {
|
||||
show: (d: DialogEntry) => void;
|
||||
toggle: (d: DialogEntry) => void;
|
||||
hide: (id: string) => void;
|
||||
}
|
||||
|
||||
export const DialogProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [dialogs, setDialogs] = useState<DialogState['dialogs']>([]);
|
||||
const actions = useMemo<Actions>(
|
||||
() => ({
|
||||
show({ id, ...props }: DialogEntry) {
|
||||
trackEvent('dialog', 'show', { id });
|
||||
setDialogs((a) => [...a.filter((d) => d.id !== id), { id, ...props }]);
|
||||
},
|
||||
toggle({ id, ...props }: DialogEntry) {
|
||||
if (dialogs.some((d) => d.id === id)) this.hide(id);
|
||||
else this.show({ id, ...props });
|
||||
},
|
||||
hide: (id: string) => {
|
||||
setDialogs((a) => a.filter((d) => d.id !== id));
|
||||
},
|
||||
}),
|
||||
[dialogs],
|
||||
);
|
||||
|
||||
const state: DialogState = {
|
||||
dialogs,
|
||||
actions,
|
||||
};
|
||||
|
||||
return <DialogContext.Provider value={state}>{children}</DialogContext.Provider>;
|
||||
};
|
||||
|
||||
function DialogInstance({ id, render, onClose, ...props }: DialogEntry) {
|
||||
const { actions } = useContext(DialogContext);
|
||||
const children = render({ hide: () => actions.hide(id) });
|
||||
return (
|
||||
<Dialog
|
||||
open
|
||||
onClose={() => {
|
||||
onClose?.();
|
||||
actions.hide(id);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export function Dialogs() {
|
||||
const { dialogs } = useContext(DialogContext);
|
||||
return (
|
||||
<>
|
||||
{dialogs.map((props: DialogEntry) => (
|
||||
<DialogInstance key={props.id} {...props} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { Button } from './core/Button';
|
||||
import type { DropdownItem } from './core/Dropdown';
|
||||
import { Dropdown } from './core/Dropdown';
|
||||
import { Icon } from './core/Icon';
|
||||
import { useDialog } from './DialogContext';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
import { EnvironmentEditDialog } from './EnvironmentEditDialog';
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||
import { updateSchema } from 'cm6-graphql';
|
||||
import type { EditorView } from 'codemirror';
|
||||
|
||||
import { formatSdl } from 'format-graphql';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useLocalStorage } from 'react-use';
|
||||
import { useIntrospectGraphQL } from '../hooks/useIntrospectGraphQL';
|
||||
import { tryFormatJson } from '../lib/formatters';
|
||||
import { Button } from './core/Button';
|
||||
import { Dropdown } from './core/Dropdown';
|
||||
import type { EditorProps } from './core/Editor';
|
||||
import { Editor, formatGraphQL } from './core/Editor';
|
||||
import type { EditorProps } from './core/Editor/Editor';
|
||||
import { Editor } from './core/Editor/Editor';
|
||||
import { FormattedError } from './core/FormattedError';
|
||||
import { Icon } from './core/Icon';
|
||||
import { Separator } from './core/Separator';
|
||||
import { useDialog } from './DialogContext';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
|
||||
type Props = Pick<EditorProps, 'heightMode' | 'className' | 'forceUpdateKey'> & {
|
||||
baseRequest: HttpRequest;
|
||||
@@ -168,7 +170,7 @@ export function GraphQLEditor({ body, onChange, baseRequest, ...extraEditorProps
|
||||
<Editor
|
||||
language="graphql"
|
||||
heightMode="auto"
|
||||
format={formatGraphQL}
|
||||
format={formatSdl}
|
||||
defaultValue={currentBody.query}
|
||||
onChange={handleChangeQuery}
|
||||
placeholder="..."
|
||||
|
||||
@@ -16,13 +16,13 @@ import { tryFormatJson } from '../lib/formatters';
|
||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||
import { count } from '../lib/pluralize';
|
||||
import { Button } from './core/Button';
|
||||
import type { EditorProps } from './core/Editor';
|
||||
import { Editor } from './core/Editor';
|
||||
import { FormattedError } from './core/FormattedError';
|
||||
import { InlineCode } from './core/InlineCode';
|
||||
import { VStack } from './core/Stacks';
|
||||
import { useDialog } from './DialogContext';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
import { GrpcProtoSelection } from './GrpcProtoSelection';
|
||||
import type { EditorProps} from './core/Editor/Editor';
|
||||
import {Editor} from './core/Editor/Editor';
|
||||
|
||||
type Props = Pick<EditorProps, 'heightMode' | 'onChange' | 'className'> & {
|
||||
services: ReflectResponseService[] | null;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import classNames from 'classnames';
|
||||
import type { HTMLAttributes, ReactNode } from 'react';
|
||||
import React from 'react';
|
||||
import { useSettings } from '../hooks/useSettings';
|
||||
import { useOsInfo } from '../hooks/useOsInfo';
|
||||
import { useSettings } from '../hooks/useSettings';
|
||||
import { useStoplightsVisible } from '../hooks/useStoplightsVisible';
|
||||
import { WINDOW_CONTROLS_WIDTH, WindowControls } from './WindowControls';
|
||||
import { HEADER_SIZE_LG, HEADER_SIZE_MD, WINDOW_CONTROLS_WIDTH } from '../lib/constants';
|
||||
import { WindowControls } from './WindowControls';
|
||||
|
||||
interface HeaderSizeProps extends HTMLAttributes<HTMLDivElement> {
|
||||
children?: ReactNode;
|
||||
@@ -13,9 +14,6 @@ interface HeaderSizeProps extends HTMLAttributes<HTMLDivElement> {
|
||||
onlyXWindowControl?: boolean;
|
||||
}
|
||||
|
||||
export const HEADER_SIZE_MD = '27px';
|
||||
export const HEADER_SIZE_LG = '38px';
|
||||
|
||||
export function HeaderSize({
|
||||
className,
|
||||
style,
|
||||
@@ -33,7 +31,8 @@ export function HeaderSize({
|
||||
style={{
|
||||
...style,
|
||||
// Add padding for macOS stoplights, but keep it the same width (account for the interface scale)
|
||||
paddingLeft: (stoplightsVisible && !ignoreControlsSpacing) ? 72 / settings.interfaceScale : undefined,
|
||||
paddingLeft:
|
||||
stoplightsVisible && !ignoreControlsSpacing ? 72 / settings.interfaceScale : undefined,
|
||||
...(size === 'md' ? { height: HEADER_SIZE_MD } : {}),
|
||||
...(size === 'lg' ? { height: HEADER_SIZE_LG } : {}),
|
||||
...(osInfo.osType === 'macos' || ignoreControlsSpacing
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useLicense } from '@yaakapp-internal/license';
|
||||
import { useOpenSettings } from '../hooks/useOpenSettings';
|
||||
import type { ButtonProps } from './core/Button';
|
||||
import { Button } from './core/Button';
|
||||
import { SettingsTab } from './Settings/Settings';
|
||||
import {SettingsTab} from "./Settings/SettingsTab";
|
||||
|
||||
const details: Record<
|
||||
LicenseCheckStatus['type'],
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useRef } from 'react';
|
||||
import Markdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { useKeyValue } from '../hooks/useKeyValue';
|
||||
import { Editor } from './core/Editor';
|
||||
import {Editor} from "./core/Editor/Editor";
|
||||
import { IconButton } from './core/IconButton';
|
||||
import { SplitLayout } from './core/SplitLayout';
|
||||
import { VStack } from './core/Stacks';
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
|
||||
import React, { useState } from 'react';
|
||||
import { useToast } from '../hooks/useToast';
|
||||
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
||||
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||
import { router } from '../main';
|
||||
import { Route } from '../routes/workspaces/$workspaceId/index';
|
||||
import { Button } from './core/Button';
|
||||
import { InlineCode } from './core/InlineCode';
|
||||
import { Select } from './core/Select';
|
||||
import { VStack } from './core/Stacks';
|
||||
import { useToast } from './ToastContext';
|
||||
|
||||
interface Props {
|
||||
activeWorkspaceId: string;
|
||||
@@ -23,6 +22,7 @@ export function MoveToWorkspaceDialog({ onDone, request, activeWorkspaceId }: Pr
|
||||
const updateHttpRequest = useUpdateAnyHttpRequest();
|
||||
const updateGrpcRequest = useUpdateAnyGrpcRequest();
|
||||
const toast = useToast();
|
||||
const navigate = useNavigate();
|
||||
const [selectedWorkspaceId, setSelectedWorkspaceId] = useState<string>(activeWorkspaceId);
|
||||
|
||||
return (
|
||||
@@ -69,10 +69,10 @@ export function MoveToWorkspaceDialog({ onDone, request, activeWorkspaceId }: Pr
|
||||
size="xs"
|
||||
color="secondary"
|
||||
className="mr-auto min-w-[5rem]"
|
||||
onClick={() => {
|
||||
onClick={async () => {
|
||||
toast.hide('workspace-moved');
|
||||
router.navigate({
|
||||
to: Route.fullPath,
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: selectedWorkspaceId },
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import classNames from 'classnames';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useKeyPressEvent } from 'react-use';
|
||||
@@ -7,8 +8,6 @@ import { useHotKey } from '../hooks/useHotKey';
|
||||
import { useRecentRequests } from '../hooks/useRecentRequests';
|
||||
import { useRequests } from '../hooks/useRequests';
|
||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||
import { router } from '../main';
|
||||
import { Route } from '../routes/workspaces/$workspaceId/requests/$requestId';
|
||||
import type { ButtonProps } from './core/Button';
|
||||
import { Button } from './core/Button';
|
||||
import type { DropdownItem, DropdownRef } from './core/Dropdown';
|
||||
@@ -22,6 +21,7 @@ export function RecentRequestsDropdown({ className }: Pick<ButtonProps, 'classNa
|
||||
const allRecentRequestIds = useRecentRequests();
|
||||
const recentRequestIds = useMemo(() => allRecentRequestIds.slice(1), [allRecentRequestIds]);
|
||||
const requests = useRequests();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Handle key-up
|
||||
useKeyPressEvent('Control', undefined, () => {
|
||||
@@ -52,9 +52,9 @@ export function RecentRequestsDropdown({ className }: Pick<ButtonProps, 'classNa
|
||||
label: fallbackRequestName(request),
|
||||
// leftSlot: <CountBadge className="!ml-0 px-0 w-5" count={recentRequestItems.length} />,
|
||||
leftSlot: <HttpMethodTag className="text-right" shortNames request={request} />,
|
||||
onSelect: () => {
|
||||
router.navigate({
|
||||
to: Route.fullPath,
|
||||
onSelect: async () => {
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: {
|
||||
requestId: request.id,
|
||||
workspaceId: activeWorkspace.id,
|
||||
@@ -77,7 +77,7 @@ export function RecentRequestsDropdown({ className }: Pick<ButtonProps, 'classNa
|
||||
}
|
||||
|
||||
return recentRequestItems.slice(0, 20);
|
||||
}, [activeWorkspace, recentRequestIds, requests]);
|
||||
}, [activeWorkspace, navigate, recentRequestIds, requests]);
|
||||
|
||||
return (
|
||||
<Dropdown ref={dropdownRef} items={items}>
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import { useEffect } from 'react';
|
||||
import { getRecentCookieJars } from '../hooks/useRecentCookieJars';
|
||||
import { getRecentEnvironments } from '../hooks/useRecentEnvironments';
|
||||
import { getRecentRequests } from '../hooks/useRecentRequests';
|
||||
import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { router } from '../main';
|
||||
import { Route as WorkspaceRoute } from '../routes/workspaces/$workspaceId';
|
||||
import { Route as RequestRoute } from '../routes/workspaces/$workspaceId/requests/$requestId';
|
||||
|
||||
export function RedirectToLatestWorkspace() {
|
||||
const workspaces = useWorkspaces();
|
||||
const recentWorkspaces = useRecentWorkspaces();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (workspaces.length === 0) {
|
||||
@@ -25,20 +24,20 @@ export function RedirectToLatestWorkspace() {
|
||||
const requestId = (await getRecentRequests(workspaceId))[0] ?? null;
|
||||
|
||||
if (workspaceId != null && requestId != null) {
|
||||
await router.navigate({
|
||||
to: RequestRoute.fullPath,
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: { workspaceId, requestId },
|
||||
search: { cookieJarId, environmentId },
|
||||
});
|
||||
} else {
|
||||
await router.navigate({
|
||||
to: WorkspaceRoute.fullPath,
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId },
|
||||
search: { cookieJarId, environmentId },
|
||||
});
|
||||
}
|
||||
})();
|
||||
}, [recentWorkspaces, workspaces, workspaces.length]);
|
||||
}, [navigate, recentWorkspaces, workspaces, workspaces.length]);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { getHttpRequest } from '../lib/store';
|
||||
import type { DropdownItem } from './core/Dropdown';
|
||||
import { ContextMenu } from './core/Dropdown';
|
||||
import { Icon } from './core/Icon';
|
||||
import { useDialog } from './DialogContext';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
import { FolderSettingsDialog } from './FolderSettingsDialog';
|
||||
import type { SidebarTreeNode } from './Sidebar';
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { useRequestEditor, useRequestEditorEvent } from '../hooks/useRequestEdit
|
||||
import { useRequests } from '../hooks/useRequests';
|
||||
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
|
||||
import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest';
|
||||
import { useToast } from '../hooks/useToast';
|
||||
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
||||
import { languageFromContentType } from '../lib/contentType';
|
||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||
@@ -34,7 +35,7 @@ import { BasicAuth } from './BasicAuth';
|
||||
import { BearerAuth } from './BearerAuth';
|
||||
import { BinaryFileEditor } from './BinaryFileEditor';
|
||||
import { CountBadge } from './core/CountBadge';
|
||||
import { Editor } from './core/Editor';
|
||||
import { Editor } from './core/Editor/Editor';
|
||||
import type {
|
||||
GenericCompletionConfig,
|
||||
GenericCompletionOption,
|
||||
@@ -50,7 +51,6 @@ import { FormUrlencodedEditor } from './FormUrlencodedEditor';
|
||||
import { GraphQLEditor } from './GraphQLEditor';
|
||||
import { HeadersEditor } from './HeadersEditor';
|
||||
import { MarkdownEditor } from './MarkdownEditor';
|
||||
import { useToast } from './ToastContext';
|
||||
import { UrlBar } from './UrlBar';
|
||||
import { UrlParametersEditor } from './UrlParameterEditor';
|
||||
|
||||
@@ -446,25 +446,28 @@ export const RequestPane = memo(function RequestPane({
|
||||
<EmptyStateText>Empty Body</EmptyStateText>
|
||||
)}
|
||||
</TabContent>
|
||||
<TabContent value={TAB_DESCRIPTION}><div className="grid grid-rows-[auto_minmax(0,1fr)] h-full">
|
||||
<PlainInput
|
||||
label="Request Name"
|
||||
hideLabel
|
||||
defaultValue={activeRequest.name}
|
||||
className="font-sans !text-xl !px-0"
|
||||
containerClassName="border-0"
|
||||
placeholder={fallbackRequestName(activeRequest)}
|
||||
onChange={(name) => updateRequest.mutate({ id: activeRequestId, update: { name } })}
|
||||
/>
|
||||
<MarkdownEditor
|
||||
name="request-description"
|
||||
placeholder="A Markdown description of this request."
|
||||
defaultValue={activeRequest.description}
|
||||
onChange={(description) =>
|
||||
updateRequest.mutate({ id: activeRequestId, update: { description } })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<TabContent value={TAB_DESCRIPTION}>
|
||||
<div className="grid grid-rows-[auto_minmax(0,1fr)] h-full">
|
||||
<PlainInput
|
||||
label="Request Name"
|
||||
hideLabel
|
||||
defaultValue={activeRequest.name}
|
||||
className="font-sans !text-xl !px-0"
|
||||
containerClassName="border-0"
|
||||
placeholder={fallbackRequestName(activeRequest)}
|
||||
onChange={(name) =>
|
||||
updateRequest.mutate({ id: activeRequestId, update: { name } })
|
||||
}
|
||||
/>
|
||||
<MarkdownEditor
|
||||
name="request-description"
|
||||
placeholder="A Markdown description of this request."
|
||||
defaultValue={activeRequest.description}
|
||||
onChange={(description) =>
|
||||
updateRequest.mutate({ id: activeRequestId, update: { description } })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</TabContent>
|
||||
</Tabs>
|
||||
</>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import { useRouteError } from 'react-router-dom';
|
||||
import { router } from '../main';
|
||||
import { Route } from '../routes/workspaces';
|
||||
import { Button } from './core/Button';
|
||||
import { FormattedError } from './core/FormattedError';
|
||||
import { Heading } from './core/Heading';
|
||||
import { VStack } from './core/Stacks';
|
||||
|
||||
export default function RouteError() {
|
||||
const navigate = useNavigate();
|
||||
const error = useRouteError();
|
||||
console.log('Error', error);
|
||||
const stringified = JSON.stringify(error);
|
||||
@@ -20,8 +20,8 @@ export default function RouteError() {
|
||||
<VStack space={2}>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
router.navigate({ to: Route.fullPath });
|
||||
onClick={async () => {
|
||||
await navigate({ to: '/workspaces' });
|
||||
}}
|
||||
>
|
||||
Go Home
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useSearch } from '@tanstack/react-router';
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import classNames from 'classnames';
|
||||
import React, { useState } from 'react';
|
||||
@@ -12,18 +13,10 @@ import { SettingsGeneral } from './SettingsGeneral';
|
||||
import { SettingsLicense } from './SettingsLicense';
|
||||
import { SettingsPlugins } from './SettingsPlugins';
|
||||
import { SettingsProxy } from './SettingsProxy';
|
||||
import { SettingsTab } from './SettingsTab';
|
||||
|
||||
interface Props {
|
||||
hide?: () => void;
|
||||
defaultTab?: SettingsTab;
|
||||
}
|
||||
|
||||
export enum SettingsTab {
|
||||
General = 'general',
|
||||
Proxy = 'proxy',
|
||||
Appearance = 'appearance',
|
||||
Plugins = 'plugins',
|
||||
License = 'license',
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
@@ -34,9 +27,10 @@ const tabs = [
|
||||
SettingsTab.License,
|
||||
];
|
||||
|
||||
export default function Settings({ hide, defaultTab }: Props) {
|
||||
export default function Settings({ hide }: Props) {
|
||||
const osInfo = useOsInfo();
|
||||
const [tab, setTab] = useState<string>(defaultTab ?? SettingsTab.General);
|
||||
const { tab: tabFromQuery } = useSearch({ from: '/workspaces/$workspaceId/settings' });
|
||||
const [tab, setTab] = useState<string>(tabFromQuery ?? SettingsTab.General);
|
||||
|
||||
// Close settings window on escape
|
||||
// TODO: Could this be put in a better place? Eg. in Rust key listener when creating the window
|
||||
|
||||
@@ -9,7 +9,7 @@ import { getThemes } from '../../lib/theme/themes';
|
||||
import { isThemeDark } from '../../lib/theme/window';
|
||||
import type { ButtonProps } from '../core/Button';
|
||||
import { Checkbox } from '../core/Checkbox';
|
||||
import { Editor } from '../core/Editor';
|
||||
import {Editor} from "../core/Editor/Editor";
|
||||
import type { IconProps } from '../core/Icon';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { yaakDark } from '../../lib/theme/themes/yaak';
|
||||
import { getThemeCSS } from '../../lib/theme/window';
|
||||
import { Banner } from '../core/Banner';
|
||||
import { Button } from '../core/Button';
|
||||
import { Editor } from '../core/Editor';
|
||||
import {Editor} from "../core/Editor/Editor";
|
||||
import type { IconProps } from '../core/Icon';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
|
||||
@@ -20,7 +20,7 @@ export function SettingsGeneral() {
|
||||
const settings = useSettings();
|
||||
const updateSettings = useUpdateSettings();
|
||||
const appInfo = useAppInfo();
|
||||
const checkForUpdates = useCheckForUpdates();
|
||||
const checkForUpdates = useCheckForUpdates();
|
||||
|
||||
if (settings == null || workspace == null) {
|
||||
return null;
|
||||
|
||||
8
src-web/components/Settings/SettingsTab.ts
Normal file
8
src-web/components/Settings/SettingsTab.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
export enum SettingsTab {
|
||||
General = 'general',
|
||||
Proxy = 'proxy',
|
||||
Appearance = 'appearance',
|
||||
Plugins = 'plugins',
|
||||
License = 'license',
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import type { DropdownRef } from './core/Dropdown';
|
||||
import { Dropdown } from './core/Dropdown';
|
||||
import { Icon } from './core/Icon';
|
||||
import { IconButton } from './core/IconButton';
|
||||
import { useDialog } from './DialogContext';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
import { KeyboardShortcutsDialog } from './KeyboardShortcutsDialog';
|
||||
|
||||
export function SettingsDropdown() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import type { Folder, GrpcRequest, HttpRequest, Workspace } from '@yaakapp-internal/models';
|
||||
import classNames from 'classnames';
|
||||
import { atom, useAtom } from 'jotai';
|
||||
import { useAtom } from 'jotai';
|
||||
import React, { memo, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useKey, useKeyPressEvent } from 'react-use';
|
||||
import { getActiveRequest } from '../hooks/useActiveRequest';
|
||||
@@ -16,9 +17,8 @@ import { useSidebarHidden } from '../hooks/useSidebarHidden';
|
||||
import { useUpdateAnyFolder } from '../hooks/useUpdateAnyFolder';
|
||||
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
||||
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
||||
import { router } from '../main';
|
||||
import { Route } from '../routes/workspaces/$workspaceId/requests/$requestId';
|
||||
import { ContextMenu } from './core/Dropdown';
|
||||
import { sidebarSelectedIdAtom } from './SidebarAtoms';
|
||||
import type { SidebarItemProps } from './SidebarItem';
|
||||
import { SidebarItems } from './SidebarItems';
|
||||
|
||||
@@ -32,9 +32,6 @@ export interface SidebarTreeNode {
|
||||
depth: number;
|
||||
}
|
||||
|
||||
// This is an atom so we can use it in the child items to avoid re-rendering the entire list
|
||||
export const sidebarSelectedIdAtom = atom<string | null>(null);
|
||||
|
||||
export const Sidebar = memo(function Sidebar({ className }: Props) {
|
||||
const [hidden, setHidden] = useSidebarHidden();
|
||||
const sidebarRef = useRef<HTMLLIElement>(null);
|
||||
@@ -52,6 +49,7 @@ export const Sidebar = memo(function Sidebar({ className }: Props) {
|
||||
const [draggingId, setDraggingId] = useState<string | null>(null);
|
||||
const [hoveredTree, setHoveredTree] = useState<SidebarTreeNode | null>(null);
|
||||
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
||||
const navigate = useNavigate();
|
||||
const { value: collapsed, set: setCollapsed } = useKeyValue<Record<string, boolean>>({
|
||||
key: ['sidebar_collapsed', activeWorkspace?.id ?? 'n/a'],
|
||||
fallback: {},
|
||||
@@ -165,8 +163,8 @@ export const Sidebar = memo(function Sidebar({ className }: Props) {
|
||||
if (item.model === 'folder') {
|
||||
await setCollapsed((c) => ({ ...c, [item.id]: !c[item.id] }));
|
||||
} else {
|
||||
router.navigate({
|
||||
to: Route.fullPath,
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: {
|
||||
requestId: id,
|
||||
workspaceId: item.workspaceId,
|
||||
@@ -179,7 +177,7 @@ export const Sidebar = memo(function Sidebar({ className }: Props) {
|
||||
setSelectedTree(tree);
|
||||
}
|
||||
},
|
||||
[treeParentMap, setCollapsed, setHasFocus, setSelectedId],
|
||||
[treeParentMap, setCollapsed, navigate, setSelectedId],
|
||||
);
|
||||
|
||||
const handleClearSelected = useCallback(() => {
|
||||
@@ -214,7 +212,7 @@ export const Sidebar = memo(function Sidebar({ className }: Props) {
|
||||
);
|
||||
});
|
||||
|
||||
useKeyPressEvent('Enter', (e) => {
|
||||
useKeyPressEvent('Enter', async (e) => {
|
||||
if (!hasFocus) return;
|
||||
const selected = selectableRequests.find((r) => r.id === selectedId);
|
||||
if (!selected || activeWorkspace == null) {
|
||||
@@ -222,8 +220,8 @@ export const Sidebar = memo(function Sidebar({ className }: Props) {
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
router.navigate({
|
||||
to: Route.fullPath,
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: {
|
||||
requestId: selected.id,
|
||||
workspaceId: activeWorkspace?.id ?? null,
|
||||
|
||||
5
src-web/components/SidebarAtoms.ts
Normal file
5
src-web/components/SidebarAtoms.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
// This is an atom so we can use it in the child items to avoid re-rendering the entire list
|
||||
import {atom} from "jotai/index";
|
||||
|
||||
export const sidebarSelectedIdAtom = atom<string | null>(null);
|
||||
@@ -8,14 +8,14 @@ import { activeRequestAtom } from '../hooks/useActiveRequest';
|
||||
import { useScrollIntoView } from '../hooks/useScrollIntoView';
|
||||
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
||||
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { isResponseLoading } from '../lib/model_util';
|
||||
import { jotaiStore } from '../routes/__root';
|
||||
import { HttpMethodTag } from './core/HttpMethodTag';
|
||||
import { Icon } from './core/Icon';
|
||||
import { StatusTag } from './core/StatusTag';
|
||||
import { RequestContextMenu } from './RequestContextMenu';
|
||||
import type { SidebarTreeNode } from './Sidebar';
|
||||
import { sidebarSelectedIdAtom } from './Sidebar';
|
||||
import {sidebarSelectedIdAtom} from "./SidebarAtoms";
|
||||
import type { SidebarItemsProps } from './SidebarItems';
|
||||
|
||||
enum ItemTypes {
|
||||
@@ -42,7 +42,7 @@ type DragItem = {
|
||||
itemName: string;
|
||||
};
|
||||
|
||||
function SidebarItem_({
|
||||
export const SidebarItem = memo(function SidebarItem({
|
||||
itemName,
|
||||
itemId,
|
||||
itemModel,
|
||||
@@ -106,7 +106,7 @@ function SidebarItem_({
|
||||
const [selected, setSelected] = useState<boolean>(
|
||||
jotaiStore.get(sidebarSelectedIdAtom) == itemId,
|
||||
);
|
||||
useEffect(() => {
|
||||
useEffect(() => {
|
||||
jotaiStore.sub(sidebarSelectedIdAtom, () => {
|
||||
const value = jotaiStore.get(sidebarSelectedIdAtom);
|
||||
setSelected(value === itemId);
|
||||
@@ -262,17 +262,4 @@ function SidebarItem_({
|
||||
{children}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
export const SidebarItem = memo<SidebarItemProps>(SidebarItem_, (a, b) => {
|
||||
const different = [];
|
||||
for (const key of Object.keys(a) as (keyof SidebarItemProps)[]) {
|
||||
if (a[key] !== b[key]) {
|
||||
different.push(key);
|
||||
}
|
||||
}
|
||||
if (different.length > 0) {
|
||||
console.log('ITEM DIFFERENT -------------------', different.join(', '));
|
||||
}
|
||||
return different.length === 0;
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface SidebarItemsProps {
|
||||
grpcConnections: GrpcConnection[];
|
||||
}
|
||||
|
||||
function SidebarItems_({
|
||||
export const SidebarItems = memo(function SidebarItems({
|
||||
tree,
|
||||
selectedTree,
|
||||
draggingId,
|
||||
@@ -102,17 +102,4 @@ function SidebarItems_({
|
||||
)}
|
||||
</VStack>
|
||||
);
|
||||
}
|
||||
|
||||
export const SidebarItems = memo<SidebarItemsProps>(SidebarItems_, (a, b) => {
|
||||
const different = [];
|
||||
for (const key of Object.keys(a) as (keyof SidebarItemsProps)[]) {
|
||||
if (a[key] !== b[key]) {
|
||||
different.push(key);
|
||||
}
|
||||
}
|
||||
if (different.length > 0) {
|
||||
console.log('ITEMS DIFFERENT -------------------', different.join(', '));
|
||||
}
|
||||
return different.length === 0;
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,105 +1,4 @@
|
||||
import type { ShowToastRequest } from '@yaakapp-internal/plugin';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { createContext, useContext, useMemo, useRef, useState } from 'react';
|
||||
import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent';
|
||||
import { generateId } from '../lib/generateId';
|
||||
import type { ToastProps } from './core/Toast';
|
||||
import { Toast } from './core/Toast';
|
||||
import { Portal } from './Portal';
|
||||
import { createContext } from 'react';
|
||||
import type { ToastState } from './Toasts';
|
||||
|
||||
type ToastEntry = {
|
||||
id?: string;
|
||||
message: ReactNode;
|
||||
timeout?: 3000 | 5000 | 8000 | null;
|
||||
onClose?: ToastProps['onClose'];
|
||||
} & Omit<ToastProps, 'onClose' | 'open' | 'children' | 'timeout'>;
|
||||
|
||||
type PrivateToastEntry = ToastEntry & {
|
||||
id: string;
|
||||
timeout: number | null;
|
||||
};
|
||||
|
||||
interface State {
|
||||
toasts: PrivateToastEntry[];
|
||||
actions: Actions;
|
||||
}
|
||||
|
||||
interface Actions {
|
||||
show: (d: ToastEntry) => void;
|
||||
hide: (id: string) => void;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const ToastContext = createContext<State>({} as State);
|
||||
|
||||
export const ToastProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [toasts, setToasts] = useState<State['toasts']>([]);
|
||||
const timeoutRef = useRef<NodeJS.Timeout>();
|
||||
const actions = useMemo<Actions>(
|
||||
() => ({
|
||||
show({ id, timeout = 5000, ...props }: ToastEntry) {
|
||||
id = id ?? generateId();
|
||||
if (timeout != null) {
|
||||
timeoutRef.current = setTimeout(() => this.hide(id), timeout);
|
||||
}
|
||||
setToasts((a) => {
|
||||
if (a.some((v) => v.id === id)) {
|
||||
// It's already visible with this id
|
||||
return a;
|
||||
}
|
||||
return [...a, { id, timeout, ...props }];
|
||||
});
|
||||
return id;
|
||||
},
|
||||
hide: (id: string) => {
|
||||
setToasts((all) => {
|
||||
const t = all.find((t) => t.id === id);
|
||||
t?.onClose?.();
|
||||
return all.filter((t) => t.id !== id);
|
||||
});
|
||||
},
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
useListenToTauriEvent<ShowToastRequest>('show_toast', (event) => {
|
||||
actions.show({ ...event.payload });
|
||||
});
|
||||
|
||||
const state: State = { toasts, actions };
|
||||
return <ToastContext.Provider value={state}>{children}</ToastContext.Provider>;
|
||||
};
|
||||
|
||||
function ToastInstance({ id, message, timeout, ...props }: PrivateToastEntry) {
|
||||
const { actions } = useContext(ToastContext);
|
||||
return (
|
||||
<Toast
|
||||
open
|
||||
timeout={timeout}
|
||||
{...props}
|
||||
// We call onClose inside actions.hide instead of passing to toast so that
|
||||
// it gets called from external close calls as well
|
||||
onClose={() => actions.hide(id)}
|
||||
>
|
||||
{message}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
export const useToast = () => useContext(ToastContext).actions;
|
||||
|
||||
export const Toasts = () => {
|
||||
const { toasts } = useContext(ToastContext);
|
||||
return (
|
||||
<Portal name="toasts">
|
||||
<div className="absolute right-0 bottom-0 z-20">
|
||||
<AnimatePresence>
|
||||
{toasts.map((props: PrivateToastEntry) => (
|
||||
<ToastInstance key={props.id} {...props} />
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
export const ToastContext = createContext<ToastState>({} as ToastState);
|
||||
|
||||
100
src-web/components/Toasts.tsx
Normal file
100
src-web/components/Toasts.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import type { ShowToastRequest } from '@yaakapp-internal/plugin';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import React, {type ReactNode, useContext, useMemo, useRef, useState} from 'react';
|
||||
import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent';
|
||||
import { generateId } from '../lib/generateId';
|
||||
import {Toast, type ToastProps} from './core/Toast';
|
||||
import { Portal } from './Portal';
|
||||
import { ToastContext } from './ToastContext';
|
||||
|
||||
type ToastEntry = {
|
||||
id?: string;
|
||||
message: ReactNode;
|
||||
timeout?: 3000 | 5000 | 8000 | null;
|
||||
onClose?: ToastProps['onClose'];
|
||||
} & Omit<ToastProps, 'onClose' | 'open' | 'children' | 'timeout'>;
|
||||
|
||||
type PrivateToastEntry = ToastEntry & {
|
||||
id: string;
|
||||
timeout: number | null;
|
||||
};
|
||||
|
||||
export interface ToastState {
|
||||
toasts: PrivateToastEntry[];
|
||||
actions: Actions;
|
||||
}
|
||||
|
||||
export interface Actions {
|
||||
show: (d: ToastEntry) => void;
|
||||
hide: (id: string) => void;
|
||||
}
|
||||
|
||||
|
||||
export const ToastProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [toasts, setToasts] = useState<ToastState['toasts']>([]);
|
||||
const timeoutRef = useRef<NodeJS.Timeout>();
|
||||
const actions = useMemo<Actions>(
|
||||
() => ({
|
||||
show({ id, timeout = 5000, ...props }: ToastEntry) {
|
||||
id = id ?? generateId();
|
||||
if (timeout != null) {
|
||||
timeoutRef.current = setTimeout(() => this.hide(id), timeout);
|
||||
}
|
||||
setToasts((a) => {
|
||||
if (a.some((v) => v.id === id)) {
|
||||
// It's already visible with this id
|
||||
return a;
|
||||
}
|
||||
return [...a, { id, timeout, ...props }];
|
||||
});
|
||||
return id;
|
||||
},
|
||||
hide: (id: string) => {
|
||||
setToasts((all) => {
|
||||
const t = all.find((t) => t.id === id);
|
||||
t?.onClose?.();
|
||||
return all.filter((t) => t.id !== id);
|
||||
});
|
||||
},
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
useListenToTauriEvent<ShowToastRequest>('show_toast', (event) => {
|
||||
actions.show({ ...event.payload });
|
||||
});
|
||||
|
||||
const state: ToastState = { toasts, actions };
|
||||
return <ToastContext.Provider value={state}>{children}</ToastContext.Provider>;
|
||||
};
|
||||
|
||||
function ToastInstance({ id, message, timeout, ...props }: PrivateToastEntry) {
|
||||
const { actions } = useContext(ToastContext);
|
||||
return (
|
||||
<Toast
|
||||
open
|
||||
timeout={timeout}
|
||||
{...props}
|
||||
// We call onClose inside actions.hide instead of passing to toast so that
|
||||
// it gets called from external close calls as well
|
||||
onClose={() => actions.hide(id)}
|
||||
>
|
||||
{message}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
export const Toasts = () => {
|
||||
const { toasts } = useContext(ToastContext);
|
||||
return (
|
||||
<Portal name="toasts">
|
||||
<div className="absolute right-0 bottom-0 z-20">
|
||||
<AnimatePresence>
|
||||
{toasts.map((props: PrivateToastEntry) => (
|
||||
<ToastInstance key={props.id} {...props} />
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import classNames from 'classnames';
|
||||
import React, { useState } from 'react';
|
||||
import { useOsInfo } from '../hooks/useOsInfo';
|
||||
import {WINDOW_CONTROLS_WIDTH} from "../lib/constants";
|
||||
import { Button } from './core/Button';
|
||||
import { HStack } from './core/Stacks';
|
||||
|
||||
@@ -11,8 +12,6 @@ interface Props {
|
||||
macos?: boolean;
|
||||
}
|
||||
|
||||
export const WINDOW_CONTROLS_WIDTH = '10.5rem';
|
||||
|
||||
export function WindowControls({ className, onlyX }: Props) {
|
||||
const [maximized, setMaximized] = useState<boolean>(false);
|
||||
const osInfo = useOsInfo();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import classNames from 'classnames';
|
||||
import { motion } from 'framer-motion';
|
||||
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
||||
import { useFloatingSidebarHidden } from '../hooks/useFloatingSidebarHidden';
|
||||
@@ -41,10 +41,6 @@ export function Workspace() {
|
||||
null,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('RENDER WORKSPACE');
|
||||
}, []);
|
||||
|
||||
const unsub = () => {
|
||||
if (moveState.current !== null) {
|
||||
document.documentElement.removeEventListener('mousemove', moveState.current.move);
|
||||
|
||||
@@ -14,7 +14,7 @@ import type { DropdownItem } from './core/Dropdown';
|
||||
import { Icon } from './core/Icon';
|
||||
import type { RadioDropdownItem } from './core/RadioDropdown';
|
||||
import { RadioDropdown } from './core/RadioDropdown';
|
||||
import { useDialog } from './DialogContext';
|
||||
import { useDialog } from '../hooks/useDialog';
|
||||
import { OpenWorkspaceDialog } from './OpenWorkspaceDialog';
|
||||
import { WorkspaceSettingsDialog } from './WorkpaceSettingsDialog';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { Editor } from './Editor';
|
||||
import {Editor} from "./Editor/Editor";
|
||||
import type { PairEditorProps } from './PairEditor';
|
||||
|
||||
type Props = PairEditorProps;
|
||||
|
||||
@@ -19,11 +19,11 @@ import {
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { useActiveEnvironmentVariables } from '../../../hooks/useActiveEnvironmentVariables';
|
||||
import {useDialog} from "../../../hooks/useDialog";
|
||||
import { parseTemplate } from '../../../hooks/useParseTemplate';
|
||||
import { useRequestEditor } from '../../../hooks/useRequestEditor';
|
||||
import { useSettings } from '../../../hooks/useSettings';
|
||||
import { useTemplateFunctions } from '../../../hooks/useTemplateFunctions';
|
||||
import { useDialog } from '../../DialogContext';
|
||||
import { TemplateFunctionDialog } from '../../TemplateFunctionDialog';
|
||||
import { TemplateVariableDialog } from '../../TemplateVariableDialog';
|
||||
import { IconButton } from '../IconButton';
|
||||
@@ -33,11 +33,6 @@ import { baseExtensions, getLanguageExtension, multiLineExtensions } from './ext
|
||||
import type { GenericCompletionConfig } from './genericCompletion';
|
||||
import { singleLineExt } from './singleLine';
|
||||
|
||||
// Export some things so all the code-split parts are in this file
|
||||
export { buildClientSchema, getIntrospectionQuery } from 'graphql/utilities';
|
||||
export { graphql } from 'cm6-graphql';
|
||||
export { formatSdl } from 'format-graphql';
|
||||
|
||||
export interface EditorProps {
|
||||
id?: string;
|
||||
readOnly?: boolean;
|
||||
|
||||
@@ -37,7 +37,7 @@ import type { EnvironmentVariable } from '@yaakapp-internal/models';
|
||||
import type { TemplateFunction } from '@yaakapp-internal/plugin';
|
||||
import { graphql } from 'cm6-graphql';
|
||||
import { EditorView } from 'codemirror';
|
||||
import type { EditorProps } from './index';
|
||||
import type {EditorProps} from "./Editor";
|
||||
import { pairs } from './pairs/extension';
|
||||
import { text } from './text/extension';
|
||||
import { twig } from './twig/extension';
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import * as editor from './Editor';
|
||||
|
||||
export type { EditorProps } from './Editor';
|
||||
// TODO: Figure out why code-splitting breaks production build from
|
||||
// showing any content
|
||||
// const editor = await import('./Editor');
|
||||
|
||||
export const Editor = editor.Editor;
|
||||
export const graphql = editor.graphql;
|
||||
export const getIntrospectionQuery = editor.getIntrospectionQuery;
|
||||
export const buildClientSchema = editor.buildClientSchema;
|
||||
export const formatGraphQL = editor.formatSdl;
|
||||
@@ -3,8 +3,8 @@ import type { EditorView } from 'codemirror';
|
||||
import type { HTMLAttributes, ReactNode } from 'react';
|
||||
import { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
|
||||
import type { EditorProps } from './Editor';
|
||||
import { Editor } from './Editor';
|
||||
import type { EditorProps } from './Editor/Editor';
|
||||
import { Editor } from './Editor/Editor';
|
||||
import { IconButton } from './IconButton';
|
||||
import { HStack } from './Stacks';
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import { useFormatText } from '../../hooks/useFormatText';
|
||||
import { useResponseBodyEventSource } from '../../hooks/useResponseBodyEventSource';
|
||||
import { isJSON } from '../../lib/contentType';
|
||||
import { Button } from '../core/Button';
|
||||
import type { EditorProps } from '../core/Editor';
|
||||
import { Editor } from '../core/Editor';
|
||||
import type { EditorProps } from '../core/Editor/Editor';
|
||||
import { Editor } from '../core/Editor/Editor';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { InlineCode } from '../core/InlineCode';
|
||||
import { Separator } from '../core/Separator';
|
||||
|
||||
@@ -10,14 +10,14 @@ import { useToggle } from '../../hooks/useToggle';
|
||||
import { CopyButton } from '../CopyButton';
|
||||
import { Banner } from '../core/Banner';
|
||||
import { Button } from '../core/Button';
|
||||
import type { EditorProps } from '../core/Editor';
|
||||
import { Editor } from '../core/Editor';
|
||||
import { hyperlink } from '../core/Editor/hyperlink/extension';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
import { InlineCode } from '../core/InlineCode';
|
||||
import { Input } from '../core/Input';
|
||||
import { SizeTag } from '../core/SizeTag';
|
||||
import { HStack } from '../core/Stacks';
|
||||
import type { EditorProps } from '../core/Editor/Editor';
|
||||
import { Editor } from '../core/Editor/Editor';
|
||||
|
||||
const extraExtensions = [hyperlink];
|
||||
const LARGE_RESPONSE_BYTES = 2 * 1000 * 1000;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useSearch } from '@tanstack/react-router';
|
||||
import { getRouteApi, useSearch } from '@tanstack/react-router';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { Route } from '../routes/workspaces/$workspaceId';
|
||||
import { useCookieJars } from './useCookieJars';
|
||||
|
||||
export const QUERY_COOKIE_JAR_ID = 'cookie_jar_id';
|
||||
@@ -39,13 +38,18 @@ export function useEnsureActiveCookieJar() {
|
||||
}, [activeCookieJarId, cookieJars, setActiveCookieJarId]);
|
||||
}
|
||||
|
||||
const routeApi = getRouteApi('/workspaces/$workspaceId/');
|
||||
|
||||
function useActiveCookieJarId() {
|
||||
// NOTE: This query param is accessed from Rust side, so do not change
|
||||
const navigate = Route.useNavigate();
|
||||
const { cookieJarId: id } = useSearch({ strict: false });
|
||||
const navigate = routeApi.useNavigate();
|
||||
|
||||
const setId = useCallback(
|
||||
(id: string) => navigate({ search: (prev) => ({ ...prev, cookieJarId: id }) }),
|
||||
(id: string) =>
|
||||
navigate({
|
||||
search: (prev) => ({ ...prev, cookieJarId: id }),
|
||||
}),
|
||||
[navigate],
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useSearch } from '@tanstack/react-router';
|
||||
import { getRouteApi, useSearch } from '@tanstack/react-router';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { Route } from '../routes/workspaces/$workspaceId';
|
||||
import { useEnvironments } from './useEnvironments';
|
||||
|
||||
export function useActiveEnvironment() {
|
||||
@@ -15,14 +14,18 @@ export function useActiveEnvironment() {
|
||||
|
||||
export const QUERY_ENVIRONMENT_ID = 'environment_id';
|
||||
|
||||
const routeApi = getRouteApi('/workspaces/$workspaceId/');
|
||||
|
||||
function useActiveEnvironmentId() {
|
||||
// NOTE: This query param is accessed from Rust side, so do not change
|
||||
const navigate = Route.useNavigate();
|
||||
const { environmentId: id } = useSearch({ strict: false });
|
||||
const navigate = routeApi.useNavigate();
|
||||
|
||||
const setId = useCallback(
|
||||
(environment_id: string | null) =>
|
||||
navigate({ search: (prev) => ({ ...prev, environment_id: environment_id ?? undefined }) }),
|
||||
navigate({
|
||||
search: (prev) => ({ ...prev, environment_id: environment_id ?? undefined }),
|
||||
}),
|
||||
[navigate],
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { jotaiStore } from '../routes/__root';
|
||||
import {jotaiStore} from "../lib/jotai";
|
||||
import { activeRequestIdAtom } from './useActiveRequestId';
|
||||
import { grpcRequestsAtom } from './useGrpcRequests';
|
||||
import { httpRequestsAtom } from './useHttpRequests';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useParams } from '@tanstack/react-router';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { useEffect } from 'react';
|
||||
import { jotaiStore } from '../routes/__root';
|
||||
import {jotaiStore} from "../lib/jotai";
|
||||
|
||||
export const activeRequestIdAtom = atom<string>();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useParams } from '@tanstack/react-router';
|
||||
import type { Workspace } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai/index';
|
||||
import { useEffect } from 'react';
|
||||
import { jotaiStore } from '../routes/__root';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { useWorkspaces } from './useWorkspaces';
|
||||
|
||||
export const activeWorkspaceIdAtom = atom<string>();
|
||||
@@ -17,6 +17,10 @@ function useActiveWorkspaceId(): string | null {
|
||||
return useAtomValue(activeWorkspaceIdAtom) ?? null;
|
||||
}
|
||||
|
||||
export function getActiveWorkspaceId() {
|
||||
return jotaiStore.get(activeWorkspaceIdAtom);
|
||||
}
|
||||
|
||||
export function useSubscribeActiveWorkspaceId() {
|
||||
const { workspaceId } = useParams({ strict: false });
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { useToast } from '../components/ToastContext';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useToast } from './useToast';
|
||||
|
||||
export function useActiveWorkspaceChangedToast() {
|
||||
const toast = useToast();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useCallback } from 'react';
|
||||
import type { DialogProps } from '../components/core/Dialog';
|
||||
import { useDialog } from '../components/DialogContext';
|
||||
import type { AlertProps } from './Alert';
|
||||
import { Alert } from './Alert';
|
||||
import {useDialog} from "./useDialog";
|
||||
|
||||
interface AlertArg {
|
||||
id: string;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useAppInfo } from './useAppInfo';
|
||||
export function useCheckForUpdates() {
|
||||
const alert = useAlert();
|
||||
const appInfo = useAppInfo();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ['check_for_updates'],
|
||||
mutationFn: async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { DialogProps } from '../components/core/Dialog';
|
||||
import { useDialog } from '../components/DialogContext';
|
||||
import type { ConfirmProps } from './Confirm';
|
||||
import { Confirm } from './Confirm';
|
||||
import { useDialog } from './useDialog';
|
||||
|
||||
export function useConfirm() {
|
||||
const dialog = useDialog();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { clear, writeText } from '@tauri-apps/plugin-clipboard-manager';
|
||||
import { useCallback } from 'react';
|
||||
import { useToast } from '../components/ToastContext';
|
||||
import { useToast } from './useToast';
|
||||
|
||||
export function useCopy({ disableToast }: { disableToast?: boolean } = {}) {
|
||||
const toast = useToast();
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { router } from '../main';
|
||||
import { Route } from '../routes/workspaces/$workspaceId/requests/$requestId';
|
||||
import { getActiveRequest } from './useActiveRequest';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { grpcRequestsAtom } from './useGrpcRequests';
|
||||
import { updateModelList } from './useSyncModelStores';
|
||||
|
||||
export function useCreateGrpcRequest() {
|
||||
const workspace = useActiveWorkspace();
|
||||
const setGrpcRequests = useSetAtom(grpcRequestsAtom);
|
||||
const navigate = useNavigate();
|
||||
|
||||
return useFastMutation<
|
||||
GrpcRequest,
|
||||
@@ -46,8 +46,8 @@ export function useCreateGrpcRequest() {
|
||||
// Optimistic update
|
||||
setGrpcRequests(updateModelList(request));
|
||||
|
||||
router.navigate({
|
||||
to: Route.fullPath,
|
||||
navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: {
|
||||
workspaceId: request.workspaceId,
|
||||
requestId: request.id,
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||
import { useSetAtom } from 'jotai/index';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { router } from '../main';
|
||||
import { Route } from '../routes/workspaces/$workspaceId/requests/$requestId';
|
||||
import { getActiveRequest } from './useActiveRequest';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { httpRequestsAtom } from './useHttpRequests';
|
||||
import { updateModelList } from './useSyncModelStores';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
|
||||
export function useCreateHttpRequest() {
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const setHttpRequests = useSetAtom(httpRequestsAtom);
|
||||
const navigate = useNavigate();
|
||||
|
||||
return useFastMutation<HttpRequest, unknown, Partial<HttpRequest>>({
|
||||
mutationKey: ['create_http_request'],
|
||||
@@ -40,8 +40,8 @@ export function useCreateHttpRequest() {
|
||||
// Optimistic update
|
||||
setHttpRequests(updateModelList(request));
|
||||
|
||||
await router.navigate({
|
||||
to: Route.fullPath,
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: { workspaceId: request.workspaceId, requestId: request.id },
|
||||
search: (prev) => ({ ...prev }),
|
||||
});
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import type { Workspace } from '@yaakapp-internal/models';
|
||||
import { useSetAtom } from 'jotai/index';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { router } from '../main';
|
||||
import { Route } from '../routes/workspaces/$workspaceId';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { usePrompt } from './usePrompt';
|
||||
import { updateModelList } from './useSyncModelStores';
|
||||
import { workspacesAtom } from './useWorkspaces';
|
||||
@@ -11,6 +10,7 @@ import { workspacesAtom } from './useWorkspaces';
|
||||
export function useCreateWorkspace() {
|
||||
const prompt = usePrompt();
|
||||
const setWorkspaces = useSetAtom(workspacesAtom);
|
||||
const navigate = useNavigate();
|
||||
|
||||
return useFastMutation<Workspace | null, void, void>({
|
||||
mutationKey: ['create_workspace'],
|
||||
@@ -34,8 +34,8 @@ export function useCreateWorkspace() {
|
||||
// Optimistic update
|
||||
setWorkspaces(updateModelList(workspace));
|
||||
|
||||
router.navigate({
|
||||
to: Route.fullPath,
|
||||
navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: workspace.id },
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import type { Workspace } from '@yaakapp-internal/models';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { router } from '../main';
|
||||
import { Route } from '../routes/workspaces';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useConfirm } from './useConfirm';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { removeModelById } from './useSyncModelStores';
|
||||
import { workspacesAtom } from './useWorkspaces';
|
||||
|
||||
@@ -15,6 +14,7 @@ export function useDeleteWorkspace(workspace: Workspace | null) {
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const confirm = useConfirm();
|
||||
const setWorkspaces = useSetAtom(workspacesAtom);
|
||||
const navigate = useNavigate();
|
||||
|
||||
return useFastMutation<Workspace | null, string>({
|
||||
mutationKey: ['delete_workspace', workspace?.id],
|
||||
@@ -41,7 +41,7 @@ export function useDeleteWorkspace(workspace: Workspace | null) {
|
||||
|
||||
const { id: workspaceId } = workspace;
|
||||
if (workspaceId === activeWorkspace?.id) {
|
||||
router.navigate({ to: Route.fullPath });
|
||||
navigate({ to: '/workspaces' });
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
6
src-web/hooks/useDialog.ts
Normal file
6
src-web/hooks/useDialog.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { useContext } from 'react';
|
||||
import { DialogContext } from '../components/DialogContext';
|
||||
|
||||
export function useDialog() {
|
||||
return useContext(DialogContext).actions;
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { router } from '../main';
|
||||
import { Route } from '../routes/workspaces/$workspaceId/requests/$requestId';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { getGrpcProtoFiles, setGrpcProtoFiles } from './useGrpcProtoFiles';
|
||||
|
||||
export function useDuplicateGrpcRequest({
|
||||
@@ -13,6 +12,7 @@ export function useDuplicateGrpcRequest({
|
||||
id: string | null;
|
||||
navigateAfter: boolean;
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
return useFastMutation<GrpcRequest, string>({
|
||||
mutationKey: ['duplicate_grpc_request', id],
|
||||
mutationFn: async () => {
|
||||
@@ -28,8 +28,8 @@ export function useDuplicateGrpcRequest({
|
||||
await setGrpcProtoFiles(request.id, protoFiles);
|
||||
|
||||
if (navigateAfter) {
|
||||
await router.navigate({
|
||||
to: Route.fullPath,
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: { workspaceId: request.workspaceId, requestId: request.id },
|
||||
search: (prev) => ({ ...prev }),
|
||||
});
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { router } from '../main';
|
||||
import { Route } from '../routes/workspaces/$workspaceId/requests/$requestId';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useDuplicateHttpRequest({
|
||||
id,
|
||||
@@ -12,6 +11,7 @@ export function useDuplicateHttpRequest({
|
||||
id: string | null;
|
||||
navigateAfter: boolean;
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
return useFastMutation<HttpRequest, string>({
|
||||
mutationKey: ['duplicate_http_request', id],
|
||||
mutationFn: async () => {
|
||||
@@ -21,8 +21,8 @@ export function useDuplicateHttpRequest({
|
||||
onSettled: () => trackEvent('http_request', 'duplicate'),
|
||||
onSuccess: async (request) => {
|
||||
if (navigateAfter) {
|
||||
router.navigate({
|
||||
to: Route.fullPath,
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: {
|
||||
workspaceId: request.workspaceId,
|
||||
requestId: request.id,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {useDialog} from "./useDialog";
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useDialog } from '../components/DialogContext';
|
||||
import { ExportDataDialog } from '../components/ExportDataDialog';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useAlert } from './useAlert';
|
||||
import { useWorkspaces } from './useWorkspaces';
|
||||
import { useToast } from '../components/ToastContext';
|
||||
import { useToast } from './useToast';
|
||||
|
||||
export function useExportData() {
|
||||
const workspaces = useWorkspaces();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { EditorProps } from '../components/core/Editor';
|
||||
import { tryFormatJson, tryFormatXml } from '../lib/formatters';
|
||||
import type { EditorProps } from '../components/core/Editor/Editor';
|
||||
|
||||
export function useFormatText({
|
||||
text,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||
import { useToast } from '../components/ToastContext';
|
||||
import { useToast } from './useToast';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useCreateHttpRequest } from './useCreateHttpRequest';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import type {
|
||||
Environment,
|
||||
Folder,
|
||||
@@ -9,19 +9,19 @@ import type {
|
||||
import { Button } from '../components/core/Button';
|
||||
import { FormattedError } from '../components/core/FormattedError';
|
||||
import { VStack } from '../components/core/Stacks';
|
||||
import { useDialog } from '../components/DialogContext';
|
||||
import { ImportDataDialog } from '../components/ImportDataDialog';
|
||||
import { count } from '../lib/pluralize';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { Route } from '../routes/workspaces/$workspaceId';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useAlert } from './useAlert';
|
||||
import { router } from '../main';
|
||||
import { useDialog } from './useDialog';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useImportData() {
|
||||
const dialog = useDialog();
|
||||
const alert = useAlert();
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const importData = async (filePath: string): Promise<boolean> => {
|
||||
const imported: {
|
||||
@@ -65,8 +65,8 @@ export function useImportData() {
|
||||
|
||||
if (importedWorkspace != null) {
|
||||
const environmentId = imported.environments[0]?.id ?? null;
|
||||
router.navigate({
|
||||
to: Route.fullPath,
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId: importedWorkspace.id },
|
||||
search: { environmentId },
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import type { HttpUrlParameter } from '@yaakapp-internal/models';
|
||||
import { useToast } from '../components/ToastContext';
|
||||
import { useToast } from './useToast';
|
||||
import { pluralize } from '../lib/pluralize';
|
||||
import { getHttpRequest } from '../lib/store';
|
||||
import { useRequestEditor } from './useRequestEditor';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||
import type { IntrospectionQuery } from 'graphql';
|
||||
import { buildClientSchema, getIntrospectionQuery, type IntrospectionQuery } from 'graphql';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { buildClientSchema, getIntrospectionQuery } from '../components/core/Editor';
|
||||
import { minPromiseMillis } from '../lib/minPromiseMillis';
|
||||
import { getResponseBodyText } from '../lib/responseBody';
|
||||
import { sendEphemeralRequest } from '../lib/sendEphemeralRequest';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useDialog} from "./useDialog";
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import React from 'react';
|
||||
import { useDialog } from '../components/DialogContext';
|
||||
import { MoveToWorkspaceDialog } from '../components/MoveToWorkspaceDialog';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useRequests } from './useRequests';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { open } from '@tauri-apps/plugin-shell';
|
||||
import { Button } from '../components/core/Button';
|
||||
import { useToast } from '../components/ToastContext';
|
||||
import { useToast } from './useToast';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useListenToTauriEvent } from './useListenToTauriEvent';
|
||||
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { SettingsTab } from '../components/Settings/Settings';
|
||||
import { useRouter } from '@tanstack/react-router';
|
||||
import { SettingsTab } from '../components/Settings/SettingsTab';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { router } from '../main';
|
||||
import { Route as SettingsRoute } from '../routes/workspaces/settings';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
|
||||
export function useOpenSettings(tab = SettingsTab.General) {
|
||||
const workspace = useActiveWorkspace();
|
||||
|
||||
const router = useRouter();
|
||||
return useFastMutation({
|
||||
mutationKey: ['open_settings'],
|
||||
mutationFn: async () => {
|
||||
if (workspace == null) return;
|
||||
const workspaceId = getActiveWorkspaceId();
|
||||
if (workspaceId == null) return;
|
||||
|
||||
trackEvent('dialog', 'show', { id: 'settings', tab: `${tab}` });
|
||||
const location = router.buildLocation({
|
||||
to: SettingsRoute.fullPath,
|
||||
params: { workspaceId: workspace.id },
|
||||
to: '/workspaces/$workspaceId/settings',
|
||||
params: { workspaceId },
|
||||
search: { tab },
|
||||
});
|
||||
await invokeCmd('cmd_new_child_window', {
|
||||
url: location,
|
||||
url: location.href,
|
||||
label: 'settings',
|
||||
title: 'Yaak Settings',
|
||||
innerSize: [600, 550],
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { useNavigate, useRouter } from '@tanstack/react-router';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { router } from '../main';
|
||||
import { Route as WorkspaceRoute } from '../routes/workspaces/$workspaceId';
|
||||
import { Route as RequestRoute } from '../routes/workspaces/$workspaceId/requests/$requestId';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { getRecentCookieJars } from './useRecentCookieJars';
|
||||
import { getRecentEnvironments } from './useRecentEnvironments';
|
||||
import { getRecentRequests } from './useRecentRequests';
|
||||
|
||||
export function useOpenWorkspace() {
|
||||
const router = useRouter();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return useFastMutation({
|
||||
mutationKey: ['open_workspace'],
|
||||
mutationFn: async ({
|
||||
@@ -24,18 +25,22 @@ export function useOpenWorkspace() {
|
||||
|
||||
if (inNewWindow) {
|
||||
const location = router.buildLocation({
|
||||
to: WorkspaceRoute.fullPath,
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId },
|
||||
search,
|
||||
});
|
||||
await invokeCmd('cmd_new_main_window', { url: location });
|
||||
await invokeCmd('cmd_new_main_window', { url: location.href });
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestId != null) {
|
||||
router.navigate({ to: RequestRoute.fullPath, params: { workspaceId, requestId }, search });
|
||||
await navigate({
|
||||
to: '/workspaces/$workspaceId/requests/$requestId',
|
||||
params: { workspaceId, requestId },
|
||||
search,
|
||||
});
|
||||
} else {
|
||||
router.navigate({ to: WorkspaceRoute.fullPath, params: { workspaceId }, search });
|
||||
await navigate({ to: '/workspaces/$workspaceId', params: { workspaceId }, search });
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { DialogProps } from '../components/core/Dialog';
|
||||
import { useDialog } from '../components/DialogContext';
|
||||
import type { PromptProps } from './Prompt';
|
||||
import { Prompt } from './Prompt';
|
||||
import {useDialog} from "./useDialog";
|
||||
|
||||
type Props = Pick<DialogProps, 'title' | 'description'> &
|
||||
Omit<PromptProps, 'onClose' | 'onCancel' | 'onResult'> & { id: string };
|
||||
|
||||
@@ -3,7 +3,7 @@ import { save } from '@tauri-apps/plugin-dialog';
|
||||
import mime from 'mime';
|
||||
import slugify from 'slugify';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { useToast } from '../components/ToastContext';
|
||||
import { useToast } from './useToast';
|
||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||
import { getContentTypeHeader } from '../lib/model_util';
|
||||
import { getHttpRequest } from '../lib/store';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import type { AnyModel } from '@yaakapp-internal/models';
|
||||
import { useSetAtom } from 'jotai/index';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { extractKeyValue } from '../lib/keyValueStore';
|
||||
import { modelsEq } from '../lib/model_util';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
@@ -30,17 +30,6 @@ export function useSyncModelStores() {
|
||||
const queryClient = useQueryClient();
|
||||
const { wasUpdatedExternally } = useRequestUpdateKey(null);
|
||||
|
||||
const setSettings = useSetAtom(settingsAtom);
|
||||
const setWorkspaces = useSetAtom(workspacesAtom);
|
||||
const setCookieJars = useSetAtom(cookieJarsAtom);
|
||||
const setFolders = useSetAtom(foldersAtom);
|
||||
const setPlugins = useSetAtom(pluginsAtom);
|
||||
const setHttpRequests = useSetAtom(httpRequestsAtom);
|
||||
const setHttpResponses = useSetAtom(httpResponsesAtom);
|
||||
const setGrpcConnections = useSetAtom(grpcConnectionsAtom);
|
||||
const setGrpcRequests = useSetAtom(grpcRequestsAtom);
|
||||
const setEnvironments = useSetAtom(environmentsAtom);
|
||||
|
||||
useListenToTauriEvent<ModelPayload>('upserted_model', ({ payload }) => {
|
||||
const { model, windowLabel } = payload;
|
||||
const queryKey =
|
||||
@@ -63,25 +52,25 @@ export function useSyncModelStores() {
|
||||
if (shouldIgnoreModel(model, windowLabel)) return;
|
||||
|
||||
if (model.model === 'workspace') {
|
||||
setWorkspaces(updateModelList(model));
|
||||
jotaiStore.set(workspacesAtom, updateModelList(model));
|
||||
} else if (model.model === 'plugin') {
|
||||
setPlugins(updateModelList(model));
|
||||
jotaiStore.set(pluginsAtom, updateModelList(model));
|
||||
} else if (model.model === 'http_request') {
|
||||
setHttpRequests(updateModelList(model));
|
||||
jotaiStore.set(httpRequestsAtom, updateModelList(model));
|
||||
} else if (model.model === 'folder') {
|
||||
setFolders(updateModelList(model));
|
||||
jotaiStore.set(foldersAtom, updateModelList(model));
|
||||
} else if (model.model === 'http_response') {
|
||||
setHttpResponses(updateModelList(model));
|
||||
jotaiStore.set(httpResponsesAtom, updateModelList(model));
|
||||
} else if (model.model === 'grpc_request') {
|
||||
setGrpcRequests(updateModelList(model));
|
||||
jotaiStore.set(grpcRequestsAtom, updateModelList(model));
|
||||
} else if (model.model === 'grpc_connection') {
|
||||
setGrpcConnections(updateModelList(model));
|
||||
jotaiStore.set(grpcConnectionsAtom, updateModelList(model));
|
||||
} else if (model.model === 'environment') {
|
||||
setEnvironments(updateModelList(model));
|
||||
jotaiStore.set(environmentsAtom, updateModelList(model));
|
||||
} else if (model.model === 'cookie_jar') {
|
||||
setCookieJars(updateModelList(model));
|
||||
jotaiStore.set(cookieJarsAtom, updateModelList(model));
|
||||
} else if (model.model === 'settings') {
|
||||
setSettings(model);
|
||||
jotaiStore.set(settingsAtom, model);
|
||||
} else if (queryKey != null) {
|
||||
// TODO: Convert all models to use Jotai
|
||||
queryClient.setQueryData(queryKey, (current: unknown) => {
|
||||
@@ -104,27 +93,27 @@ export function useSyncModelStores() {
|
||||
console.log('Delete model', payload);
|
||||
|
||||
if (model.model === 'workspace') {
|
||||
setWorkspaces(removeModelById(model));
|
||||
jotaiStore.set(workspacesAtom, removeModelById(model));
|
||||
} else if (model.model === 'plugin') {
|
||||
setPlugins(removeModelById(model));
|
||||
jotaiStore.set(pluginsAtom, removeModelById(model));
|
||||
} else if (model.model === 'http_request') {
|
||||
setHttpRequests(removeModelById(model));
|
||||
jotaiStore.set(httpRequestsAtom, removeModelById(model));
|
||||
} else if (model.model === 'http_response') {
|
||||
setHttpResponses(removeModelById(model));
|
||||
jotaiStore.set(httpResponsesAtom, removeModelById(model));
|
||||
} else if (model.model === 'folder') {
|
||||
setFolders(removeModelById(model));
|
||||
jotaiStore.set(foldersAtom, removeModelById(model));
|
||||
} else if (model.model === 'environment') {
|
||||
setEnvironments(removeModelById(model));
|
||||
jotaiStore.set(environmentsAtom, removeModelById(model));
|
||||
} else if (model.model === 'grpc_request') {
|
||||
setGrpcRequests(removeModelById(model));
|
||||
jotaiStore.set(grpcRequestsAtom, removeModelById(model));
|
||||
} else if (model.model === 'grpc_connection') {
|
||||
setGrpcConnections(removeModelById(model));
|
||||
jotaiStore.set(grpcConnectionsAtom, removeModelById(model));
|
||||
} else if (model.model === 'grpc_event') {
|
||||
queryClient.setQueryData(grpcEventsQueryKey(model), removeModelById(model));
|
||||
} else if (model.model === 'key_value') {
|
||||
queryClient.setQueryData(keyValueQueryKey(model), undefined);
|
||||
} else if (model.model === 'cookie_jar') {
|
||||
setCookieJars(removeModelById(model));
|
||||
jotaiStore.set(cookieJarsAtom, removeModelById(model));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
6
src-web/hooks/useToast.ts
Normal file
6
src-web/hooks/useToast.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { useContext } from 'react';
|
||||
import { ToastContext } from '../components/ToastContext';
|
||||
|
||||
export function useToast() {
|
||||
return useContext(ToastContext).actions;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback } from 'react';
|
||||
import { CommandPalette } from '../components/CommandPalette';
|
||||
import { useDialog } from '../components/DialogContext';
|
||||
import { useDialog } from './useDialog';
|
||||
|
||||
export function useToggleCommandPalette() {
|
||||
const dialog = useDialog();
|
||||
|
||||
5
src-web/lib/constants.ts
Normal file
5
src-web/lib/constants.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
export const HEADER_SIZE_MD = '27px';
|
||||
export const HEADER_SIZE_LG = '38px';
|
||||
|
||||
export const WINDOW_CONTROLS_WIDTH = '10.5rem';
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { EditorProps } from '../components/core/Editor';
|
||||
import type { EditorProps } from '../components/core/Editor/Editor';
|
||||
|
||||
export function languageFromContentType(
|
||||
contentType: string | null,
|
||||
|
||||
3
src-web/lib/jotai.ts
Normal file
3
src-web/lib/jotai.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { createStore } from 'jotai/index';
|
||||
|
||||
export const jotaiStore = createStore();
|
||||
12
src-web/lib/router.ts
Normal file
12
src-web/lib/router.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// Create a new router instance
|
||||
import {createRouter} from "@tanstack/react-router";
|
||||
import {routeTree} from "../routeTree.gen";
|
||||
|
||||
export const router = createRouter({ routeTree });
|
||||
|
||||
// Register the router instance for type safety
|
||||
declare module '@tanstack/react-router' {
|
||||
interface Register {
|
||||
router: typeof router;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import './main.css';
|
||||
import { createRouter, RouterProvider } from '@tanstack/react-router';
|
||||
import { RouterProvider } from '@tanstack/react-router';
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import { type } from '@tauri-apps/plugin-os';
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { routeTree } from './routeTree.gen';
|
||||
import { router } from './lib/router';
|
||||
|
||||
import('react-pdf').then(({ pdfjs }) => {
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
||||
@@ -25,18 +25,7 @@ window.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Backspace' && e.target === document.body) e.preventDefault();
|
||||
});
|
||||
|
||||
// Create a new router instance
|
||||
export const router = createRouter({
|
||||
routeTree,
|
||||
});
|
||||
|
||||
// Register the router instance for type safety
|
||||
declare module '@tanstack/react-router' {
|
||||
interface Register {
|
||||
router: typeof router;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Creating React root');
|
||||
createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<StrictMode>
|
||||
<RouterProvider router={router} />
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev --force",
|
||||
"dev": "vite dev --force --debug hmr",
|
||||
"build": "vite build",
|
||||
"lint": "tsc --noEmit && eslint . --ext .ts,.tsx"
|
||||
},
|
||||
@@ -54,7 +54,6 @@
|
||||
"react-helmet-async": "^2.0.5",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-pdf": "^9.1.0",
|
||||
"react-router-dom": "^6.26.2",
|
||||
"react-use": "^17.5.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"slugify": "^1.6.6",
|
||||
@@ -79,6 +78,7 @@
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"decompress": "^4.2.1",
|
||||
"eslint-plugin-react-refresh": "^0.4.16",
|
||||
"internal-ip": "^8.0.0",
|
||||
"postcss": "^8.4.45",
|
||||
"postcss-nesting": "^13.0.0",
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
import { Route as rootRoute } from './routes/__root'
|
||||
import { Route as IndexImport } from './routes/index'
|
||||
import { Route as WorkspacesIndexImport } from './routes/workspaces/index'
|
||||
import { Route as WorkspacesSettingsImport } from './routes/workspaces/settings'
|
||||
import { Route as WorkspacesWorkspaceIdIndexImport } from './routes/workspaces/$workspaceId/index'
|
||||
import { Route as WorkspacesWorkspaceIdSettingsImport } from './routes/workspaces/$workspaceId/settings'
|
||||
import { Route as WorkspacesWorkspaceIdRequestsRequestIdImport } from './routes/workspaces/$workspaceId/requests/$requestId'
|
||||
|
||||
// Create/Update Routes
|
||||
@@ -31,12 +31,6 @@ const WorkspacesIndexRoute = WorkspacesIndexImport.update({
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const WorkspacesSettingsRoute = WorkspacesSettingsImport.update({
|
||||
id: '/workspaces/settings',
|
||||
path: '/workspaces/settings',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const WorkspacesWorkspaceIdIndexRoute = WorkspacesWorkspaceIdIndexImport.update(
|
||||
{
|
||||
id: '/workspaces/$workspaceId/',
|
||||
@@ -45,6 +39,13 @@ const WorkspacesWorkspaceIdIndexRoute = WorkspacesWorkspaceIdIndexImport.update(
|
||||
} as any,
|
||||
)
|
||||
|
||||
const WorkspacesWorkspaceIdSettingsRoute =
|
||||
WorkspacesWorkspaceIdSettingsImport.update({
|
||||
id: '/workspaces/$workspaceId/settings',
|
||||
path: '/workspaces/$workspaceId/settings',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const WorkspacesWorkspaceIdRequestsRequestIdRoute =
|
||||
WorkspacesWorkspaceIdRequestsRequestIdImport.update({
|
||||
id: '/workspaces/$workspaceId/requests/$requestId',
|
||||
@@ -63,13 +64,6 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof IndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/workspaces/settings': {
|
||||
id: '/workspaces/settings'
|
||||
path: '/workspaces/settings'
|
||||
fullPath: '/workspaces/settings'
|
||||
preLoaderRoute: typeof WorkspacesSettingsImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/workspaces/': {
|
||||
id: '/workspaces/'
|
||||
path: '/workspaces'
|
||||
@@ -77,6 +71,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof WorkspacesIndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/workspaces/$workspaceId/settings': {
|
||||
id: '/workspaces/$workspaceId/settings'
|
||||
path: '/workspaces/$workspaceId/settings'
|
||||
fullPath: '/workspaces/$workspaceId/settings'
|
||||
preLoaderRoute: typeof WorkspacesWorkspaceIdSettingsImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/workspaces/$workspaceId/': {
|
||||
id: '/workspaces/$workspaceId/'
|
||||
path: '/workspaces/$workspaceId'
|
||||
@@ -98,16 +99,16 @@ declare module '@tanstack/react-router' {
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/workspaces/settings': typeof WorkspacesSettingsRoute
|
||||
'/workspaces': typeof WorkspacesIndexRoute
|
||||
'/workspaces/$workspaceId/settings': typeof WorkspacesWorkspaceIdSettingsRoute
|
||||
'/workspaces/$workspaceId': typeof WorkspacesWorkspaceIdIndexRoute
|
||||
'/workspaces/$workspaceId/requests/$requestId': typeof WorkspacesWorkspaceIdRequestsRequestIdRoute
|
||||
}
|
||||
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/workspaces/settings': typeof WorkspacesSettingsRoute
|
||||
'/workspaces': typeof WorkspacesIndexRoute
|
||||
'/workspaces/$workspaceId/settings': typeof WorkspacesWorkspaceIdSettingsRoute
|
||||
'/workspaces/$workspaceId': typeof WorkspacesWorkspaceIdIndexRoute
|
||||
'/workspaces/$workspaceId/requests/$requestId': typeof WorkspacesWorkspaceIdRequestsRequestIdRoute
|
||||
}
|
||||
@@ -115,8 +116,8 @@ export interface FileRoutesByTo {
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRoute
|
||||
'/': typeof IndexRoute
|
||||
'/workspaces/settings': typeof WorkspacesSettingsRoute
|
||||
'/workspaces/': typeof WorkspacesIndexRoute
|
||||
'/workspaces/$workspaceId/settings': typeof WorkspacesWorkspaceIdSettingsRoute
|
||||
'/workspaces/$workspaceId/': typeof WorkspacesWorkspaceIdIndexRoute
|
||||
'/workspaces/$workspaceId/requests/$requestId': typeof WorkspacesWorkspaceIdRequestsRequestIdRoute
|
||||
}
|
||||
@@ -125,22 +126,22 @@ export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths:
|
||||
| '/'
|
||||
| '/workspaces/settings'
|
||||
| '/workspaces'
|
||||
| '/workspaces/$workspaceId/settings'
|
||||
| '/workspaces/$workspaceId'
|
||||
| '/workspaces/$workspaceId/requests/$requestId'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/'
|
||||
| '/workspaces/settings'
|
||||
| '/workspaces'
|
||||
| '/workspaces/$workspaceId/settings'
|
||||
| '/workspaces/$workspaceId'
|
||||
| '/workspaces/$workspaceId/requests/$requestId'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
| '/workspaces/settings'
|
||||
| '/workspaces/'
|
||||
| '/workspaces/$workspaceId/settings'
|
||||
| '/workspaces/$workspaceId/'
|
||||
| '/workspaces/$workspaceId/requests/$requestId'
|
||||
fileRoutesById: FileRoutesById
|
||||
@@ -148,16 +149,16 @@ export interface FileRouteTypes {
|
||||
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
WorkspacesSettingsRoute: typeof WorkspacesSettingsRoute
|
||||
WorkspacesIndexRoute: typeof WorkspacesIndexRoute
|
||||
WorkspacesWorkspaceIdSettingsRoute: typeof WorkspacesWorkspaceIdSettingsRoute
|
||||
WorkspacesWorkspaceIdIndexRoute: typeof WorkspacesWorkspaceIdIndexRoute
|
||||
WorkspacesWorkspaceIdRequestsRequestIdRoute: typeof WorkspacesWorkspaceIdRequestsRequestIdRoute
|
||||
}
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
WorkspacesSettingsRoute: WorkspacesSettingsRoute,
|
||||
WorkspacesIndexRoute: WorkspacesIndexRoute,
|
||||
WorkspacesWorkspaceIdSettingsRoute: WorkspacesWorkspaceIdSettingsRoute,
|
||||
WorkspacesWorkspaceIdIndexRoute: WorkspacesWorkspaceIdIndexRoute,
|
||||
WorkspacesWorkspaceIdRequestsRequestIdRoute:
|
||||
WorkspacesWorkspaceIdRequestsRequestIdRoute,
|
||||
@@ -174,8 +175,8 @@ export const routeTree = rootRoute
|
||||
"filePath": "__root.tsx",
|
||||
"children": [
|
||||
"/",
|
||||
"/workspaces/settings",
|
||||
"/workspaces/",
|
||||
"/workspaces/$workspaceId/settings",
|
||||
"/workspaces/$workspaceId/",
|
||||
"/workspaces/$workspaceId/requests/$requestId"
|
||||
]
|
||||
@@ -183,12 +184,12 @@ export const routeTree = rootRoute
|
||||
"/": {
|
||||
"filePath": "index.tsx"
|
||||
},
|
||||
"/workspaces/settings": {
|
||||
"filePath": "workspaces/settings.tsx"
|
||||
},
|
||||
"/workspaces/": {
|
||||
"filePath": "workspaces/index.tsx"
|
||||
},
|
||||
"/workspaces/$workspaceId/settings": {
|
||||
"filePath": "workspaces/$workspaceId/settings.tsx"
|
||||
},
|
||||
"/workspaces/$workspaceId/": {
|
||||
"filePath": "workspaces/$workspaceId/index.tsx"
|
||||
},
|
||||
|
||||
@@ -2,15 +2,16 @@ import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-qu
|
||||
import { createRootRoute, Outlet } from '@tanstack/react-router';
|
||||
import classNames from 'classnames';
|
||||
import { MotionConfig } from 'framer-motion';
|
||||
import { createStore, Provider as JotaiProvider } from 'jotai';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import React, { Suspense } from 'react';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
import { DialogProvider, Dialogs } from '../components/DialogContext';
|
||||
import { GlobalHooks } from '../components/GlobalHooks';
|
||||
import { ToastProvider, Toasts } from '../components/ToastContext';
|
||||
import { useOsInfo } from '../hooks/useOsInfo';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { ToastProvider, Toasts } from '../components/Toasts';
|
||||
import { DialogProvider, Dialogs } from '../components/Dialogs';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
queryCache: new QueryCache({
|
||||
@@ -53,8 +54,6 @@ export const Route = createRootRoute({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
export const jotaiStore = createStore();
|
||||
|
||||
function RouteComponent() {
|
||||
const osInfo = useOsInfo();
|
||||
return (
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import Settings, { SettingsTab } from '../../components/Settings/Settings'
|
||||
import Settings from '../../../components/Settings/Settings'
|
||||
import { SettingsTab } from '../../../components/Settings/SettingsTab'
|
||||
|
||||
interface SettingsSearchSchema {
|
||||
tab?: SettingsTab
|
||||
}
|
||||
|
||||
export const Route = createFileRoute('/workspaces/settings')({
|
||||
export const Route = createFileRoute('/workspaces/$workspaceId/settings')({
|
||||
component: RouteComponent,
|
||||
validateSearch: (search: Record<string, unknown>): SettingsSearchSchema => ({
|
||||
tab: (search.tab ?? SettingsTab.General) as SettingsTab,
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"autoCodeSplitting": true
|
||||
"autoCodeSplitting": false
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TanStackRouterVite } from '@tanstack/router-plugin/vite';
|
||||
import reactRefresh from 'eslint-plugin-react-refresh';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { internalIpV4 } from 'internal-ip';
|
||||
import { createRequire } from 'node:module';
|
||||
@@ -7,6 +7,8 @@ import { defineConfig, normalizePath } from 'vite';
|
||||
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
||||
import svgr from 'vite-plugin-svgr';
|
||||
import topLevelAwait from 'vite-plugin-top-level-await';
|
||||
// @ts-ignore
|
||||
import { TanStackRouterVite } from '@tanstack/router-plugin/vite';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const cMapsDir = normalizePath(
|
||||
@@ -21,6 +23,7 @@ const mobile = !!/android|ios/.exec(process.env.TAURI_ENV_PLATFORM ?? '');
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
plugins: [
|
||||
reactRefresh.configs.vite,
|
||||
TanStackRouterVite({
|
||||
routesDirectory: './routes',
|
||||
generatedRouteTree: './routeTree.gen.ts',
|
||||
|
||||
Reference in New Issue
Block a user