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;
|
||||
|
||||
Reference in New Issue
Block a user