diff --git a/package-lock.json b/package-lock.json index 91085083..743cbf4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2634,9 +2634,9 @@ } }, "node_modules/@tanstack/history": { - "version": "1.90.0", - "resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.90.0.tgz", - "integrity": "sha512-riNhDGm+fAwxgZRJ0J/36IZis1UDHsDCNIxfEodbw6BgTWJr0ah+G20V4HT91uBXiCqYFvX3somlfTLhS5yHDA==", + "version": "1.95.0", + "resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.95.0.tgz", + "integrity": "sha512-w1/yWuIBqmG0Z0MPMf1OuOCce7FXyVH4L4dIA4rvpnjIUCH8qRUgloFAVg37nTMUbOmhMsY2NZDxCpKBv+CLJg==", "license": "MIT", "engines": { "node": ">=12" @@ -2647,9 +2647,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.62.8", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.62.8.tgz", - "integrity": "sha512-4fV31vDsUyvNGrKIOUNPrZztoyL187bThnoQOvAXEVlZbSiuPONpfx53634MKKdvsDir5NyOGm80ShFaoHS/mw==", + "version": "5.62.16", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.62.16.tgz", + "integrity": "sha512-9Sgft7Qavcd+sN0V25xVyo0nfmcZXBuODy3FVG7BMWTg1HMLm8wwG5tNlLlmSic1u7l1v786oavn+STiFaPH2g==", "license": "MIT", "funding": { "type": "github", @@ -2668,12 +2668,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.62.8", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.62.8.tgz", - "integrity": "sha512-8TUstKxF/fysHonZsWg/hnlDVgasTdHx6Q+f1/s/oPKJBJbKUWPZEHwLTMOZgrZuroLMiqYKJ9w69Abm8mWP0Q==", + "version": "5.62.16", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.62.16.tgz", + "integrity": "sha512-XJIZNj65d2IdvU8VBESmrPakfIm6FSdHDzrS1dPrAwmq3ZX+9riMh/ZfbNQHAWnhrgmq7KoXpgZSRyXnqMYT9A==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.62.8" + "@tanstack/query-core": "5.62.16" }, "funding": { "type": "github", @@ -2702,13 +2702,13 @@ } }, "node_modules/@tanstack/react-router": { - "version": "1.91.3", - "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.91.3.tgz", - "integrity": "sha512-T6k50ApwcWKYjJB4VSK2WhXu/p40luynNJg5QC3oIqk24p0tLlgXIblXoTJzy7lVvDmQ4lwHCP9dBTvLy5NhVA==", + "version": "1.95.1", + "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.95.1.tgz", + "integrity": "sha512-P5x4yNhcdkYsCEoYeGZP8Q9Jlxf0WXJa4G/xvbmM905seZc9FqJqvCSRvX3dWTPOXRABhl4g+8DHqfft0c/AvQ==", "license": "MIT", "dependencies": { - "@tanstack/history": "1.90.0", - "@tanstack/react-store": "^0.6.1", + "@tanstack/history": "1.95.0", + "@tanstack/react-store": "^0.7.0", "jsesc": "^3.0.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" @@ -2721,24 +2721,18 @@ "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/router-generator": "^1.87.7", "react": ">=18", "react-dom": ">=18" - }, - "peerDependenciesMeta": { - "@tanstack/router-generator": { - "optional": true - } } }, "node_modules/@tanstack/react-store": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.6.1.tgz", - "integrity": "sha512-6gOopOpPp1cAXkEyTEv6tMbAywwFunvIdCKN/SpEiButUayjXU+Q5Sp5Y3hREN3VMR4OA5+RI5SPhhJoqP9e4w==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.7.0.tgz", + "integrity": "sha512-S/Rq17HaGOk+tQHV/yrePMnG1xbsKZIl/VsNWnNXt4XW+tTY8dTlvpJH2ZQ3GRALsusG5K6Q3unAGJ2pd9W/Ng==", "license": "MIT", "dependencies": { - "@tanstack/store": "0.6.0", - "use-sync-external-store": "^1.2.2" + "@tanstack/store": "0.7.0", + "use-sync-external-store": "^1.4.0" }, "funding": { "type": "github", @@ -2793,7 +2787,7 @@ "version": "1.87.7", "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.87.7.tgz", "integrity": "sha512-w9Px1C6DM0YNVXvu1VjUuZ5el0ykOeofEmEZBW83VUTzvCXFpcjPCHncU9FO9uXup8NFIxNfGz+xpwf93GoFnQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@tanstack/virtual-file-routes": "^1.87.6", @@ -2860,9 +2854,9 @@ } }, "node_modules/@tanstack/store": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.6.0.tgz", - "integrity": "sha512-+m2OBglsjXcLmmKOX6/9v8BDOCtyxhMmZLsRUDswOOSdIIR9mvv6i0XNKsmTh3AlYU8c1mRcodC8/Vyf+69VlQ==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.7.0.tgz", + "integrity": "sha512-CNIhdoUsmD2NolYuaIs8VfWM467RK6oIBAW4nPEKZhg1smZ+/CwtCdpURgp7nxSqOaV9oKkzdWD80+bC66F/Jg==", "license": "MIT", "funding": { "type": "github", @@ -2883,7 +2877,7 @@ "version": "1.87.6", "resolved": "https://registry.npmjs.org/@tanstack/virtual-file-routes/-/virtual-file-routes-1.87.6.tgz", "integrity": "sha512-PTpeM8SHL7AJM0pJOacFvHribbUODS51qe9NsMqku4mogh6BWObY1EeVmeGnp9o3VngAEsf+rJMs2zqIVz3WFA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -7386,7 +7380,7 @@ "version": "4.8.1", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -11586,7 +11580,7 @@ "version": "3.4.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" @@ -12562,7 +12556,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "devOptional": true, + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" @@ -14152,7 +14146,7 @@ "version": "4.19.2", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "esbuild": "~0.23.0", @@ -14563,7 +14557,7 @@ "version": "0.23.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", - "devOptional": true, + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -15788,7 +15782,7 @@ "version": "3.24.1", "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", - "devOptional": true, + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -15937,8 +15931,8 @@ "@lezer/lr": "^1.3.3", "@react-hook/size": "^2.1.2", "@tailwindcss/container-queries": "^0.1.1", - "@tanstack/react-query": "^5.62.8", - "@tanstack/react-router": "^1.91.3", + "@tanstack/react-query": "^5.62.16", + "@tanstack/react-router": "^1.95.1", "@tanstack/react-virtual": "^3.11.2", "@tauri-apps/api": "^2.0.1", "@tauri-apps/plugin-clipboard-manager": "^2.0.0", diff --git a/src-tauri/src/render.rs b/src-tauri/src/render.rs index cb24c1bb..1cfd2da7 100644 --- a/src-tauri/src/render.rs +++ b/src-tauri/src/render.rs @@ -424,8 +424,6 @@ mod placeholder_tests { ], ..Default::default() }); - println!("HELLO?: {result:?}"); - assert_eq!(result.url, "example.com/aaa/bar"); assert_eq!(result.url_parameters.len(), 1); assert_eq!(result.url_parameters[0].name, "b"); diff --git a/src-tauri/yaak-models/bindings/models.ts b/src-tauri/yaak-models/bindings/models.ts index ca75982f..402936bd 100644 --- a/src-tauri/yaak-models/bindings/models.ts +++ b/src-tauri/yaak-models/bindings/models.ts @@ -1,59 +1,250 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type AnyModel = CookieJar | Environment | Folder | GrpcConnection | GrpcEvent | GrpcRequest | HttpRequest | HttpResponse | Plugin | Settings | KeyValue | Workspace; +export type AnyModel = + | CookieJar + | Environment + | Folder + | GrpcConnection + | GrpcEvent + | GrpcRequest + | HttpRequest + | HttpResponse + | Plugin + | Settings + | KeyValue + | Workspace; -export type Cookie = { raw_cookie: string, domain: CookieDomain, expires: CookieExpires, path: [string, boolean], }; +export type Cookie = { + raw_cookie: string; + domain: CookieDomain; + expires: CookieExpires; + path: [string, boolean]; +}; -export type CookieDomain = { "HostOnly": string } | { "Suffix": string } | "NotPresent" | "Empty"; +export type CookieDomain = { HostOnly: string } | { Suffix: string } | 'NotPresent' | 'Empty'; -export type CookieExpires = { "AtUtc": string } | "SessionEnd"; +export type CookieExpires = { AtUtc: string } | 'SessionEnd'; -export type CookieJar = { model: "cookie_jar", id: string, createdAt: string, updatedAt: string, workspaceId: string, cookies: Array, name: string, }; +export type CookieJar = { + model: 'cookie_jar'; + id: string; + createdAt: string; + updatedAt: string; + workspaceId: string; + cookies: Array; + name: string; +}; -export type Environment = { model: "environment", id: string, workspaceId: string, environmentId: string | null, createdAt: string, updatedAt: string, name: string, variables: Array, }; +export type Environment = { + model: 'environment'; + id: string; + workspaceId: string; + environmentId: string | null; + createdAt: string; + updatedAt: string; + name: string; + variables: Array; +}; -export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id: string, }; +export type EnvironmentVariable = { enabled?: boolean; name: string; value: string; id: string }; -export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, }; +export type Folder = { + model: 'folder'; + id: string; + createdAt: string; + updatedAt: string; + workspaceId: string; + folderId: string | null; + name: string; + description: string; + sortPriority: number; +}; -export type GrpcConnection = { model: "grpc_connection", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, elapsed: number, error: string | null, method: string, service: string, status: number, state: GrpcConnectionState, trailers: { [key in string]?: string }, url: string, }; +export type GrpcConnection = { + model: 'grpc_connection'; + id: string; + createdAt: string; + updatedAt: string; + workspaceId: string; + requestId: string; + elapsed: number; + error: string | null; + method: string; + service: string; + status: number; + state: GrpcConnectionState; + trailers: { [key in string]?: string }; + url: string; +}; -export type GrpcConnectionState = "initialized" | "connected" | "closed"; +export type GrpcConnectionState = 'initialized' | 'connected' | 'closed'; -export type GrpcEvent = { model: "grpc_event", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, connectionId: string, content: string, error: string | null, eventType: GrpcEventType, metadata: { [key in string]?: string }, status: number | null, }; +export type GrpcEvent = { + model: 'grpc_event'; + id: string; + createdAt: string; + updatedAt: string; + workspaceId: string; + requestId: string; + connectionId: string; + content: string; + error: string | null; + eventType: GrpcEventType; + metadata: { [key in string]?: string }; + status: number | null; +}; -export type GrpcEventType = "info" | "error" | "client_message" | "server_message" | "connection_start" | "connection_end"; +export type GrpcEventType = + | 'info' + | 'error' + | 'client_message' + | 'server_message' + | 'connection_start' + | 'connection_end'; -export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id: string, }; +export type GrpcMetadataEntry = { enabled?: boolean; name: string; value: string; id: string }; -export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record, description: string, message: string, metadata: Array, method: string | null, name: string, service: string | null, sortPriority: number, url: string, }; +export type GrpcRequest = { + model: 'grpc_request'; + id: string; + createdAt: string; + updatedAt: string; + workspaceId: string; + folderId: string | null; + authenticationType: string | null; + authentication: Record; + description: string; + message: string; + metadata: Array; + method: string | null; + name: string; + service: string | null; + sortPriority: number; + url: string; +}; -export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record, authenticationType: string | null, body: Record, bodyType: string | null, description: string, headers: Array, method: string, name: string, sortPriority: number, url: string, urlParameters: Array, }; +export type HttpRequest = { + model: 'http_request'; + id: string; + createdAt: string; + updatedAt: string; + workspaceId: string; + folderId: string | null; + authentication: Record; + authenticationType: string | null; + body: Record; + bodyType: string | null; + description: string; + headers: Array; + method: string; + name: string; + sortPriority: number; + url: string; + urlParameters: Array; +}; -export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, }; +export type HttpRequestHeader = { enabled?: boolean; name: string; value: string; id: string }; -export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, }; +export type HttpResponse = { + model: 'http_response'; + id: string; + createdAt: string; + updatedAt: string; + workspaceId: string; + requestId: string; + bodyPath: string | null; + contentLength: number | null; + elapsed: number; + elapsedHeaders: number; + error: string | null; + headers: Array; + remoteAddr: string | null; + status: number; + statusReason: string | null; + state: HttpResponseState; + url: string; + version: string | null; +}; -export type HttpResponseHeader = { name: string, value: string, }; +export type HttpResponseHeader = { name: string; value: string }; -export type HttpResponseState = "initialized" | "connected" | "closed"; +export type HttpResponseState = 'initialized' | 'connected' | 'closed'; -export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, }; +export type HttpUrlParameter = { enabled?: boolean; name: string; value: string; id: string }; -export type KeyValue = { model: "key_value", createdAt: string, updatedAt: string, key: string, namespace: string, value: string, }; +export type KeyValue = { + model: 'key_value'; + createdAt: string; + updatedAt: string; + key: string; + namespace: string; + value: string; +}; -export type ModelPayload = { model: AnyModel, windowLabel: string, updateSource: UpdateSource, }; +export type ModelPayload = { model: AnyModel; windowLabel: string; updateSource: UpdateSource }; -export type Plugin = { model: "plugin", id: string, createdAt: string, updatedAt: string, checkedAt: string | null, directory: string, enabled: boolean, url: string | null, }; +export type Plugin = { + model: 'plugin'; + id: string; + createdAt: string; + updatedAt: string; + checkedAt: string | null; + directory: string; + enabled: boolean; + url: string | null; +}; -export type ProxySetting = { "type": "enabled", http: string, https: string, auth: ProxySettingAuth | null, } | { "type": "disabled" }; +export type ProxySetting = + | { type: 'enabled'; http: string; https: string; auth: ProxySettingAuth | null } + | { + type: 'disabled'; + }; -export type ProxySettingAuth = { user: string, password: string, }; +export type ProxySettingAuth = { user: string; password: string }; -export type Settings = { model: "settings", id: string, createdAt: string, updatedAt: string, appearance: string, editorFontSize: number, editorSoftWrap: boolean, interfaceFontSize: number, interfaceScale: number, openWorkspaceNewWindow: boolean | null, telemetry: boolean, theme: string, themeDark: string, themeLight: string, updateChannel: string, proxy: ProxySetting | null, }; +export type Settings = { + model: 'settings'; + id: string; + createdAt: string; + updatedAt: string; + appearance: string; + editorFontSize: number; + editorSoftWrap: boolean; + interfaceFontSize: number; + interfaceScale: number; + openWorkspaceNewWindow: boolean | null; + telemetry: boolean; + theme: string; + themeDark: string; + themeLight: string; + updateChannel: string; + proxy: ProxySetting | null; +}; -export type SyncState = { model: "sync_state", id: string, workspaceId: string, createdAt: string, updatedAt: string, flushedAt: string, modelId: string, checksum: string, relPath: string, syncDir: string, }; +export type SyncState = { + model: 'sync_state'; + id: string; + workspaceId: string; + createdAt: string; + updatedAt: string; + flushedAt: string; + modelId: string; + checksum: string; + relPath: string; + syncDir: string; +}; -export type UpdateSource = "sync" | "window" | "plugin" | "background"; +export type UpdateSource = 'sync' | 'window' | 'plugin' | 'background'; -export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, settingSyncDir: string | null, }; +export type Workspace = { + model: 'workspace'; + id: string; + createdAt: string; + updatedAt: string; + name: string; + description: string; + settingValidateCertificates: boolean; + settingFollowRedirects: boolean; + settingRequestTimeout: number; + settingSyncDir: string | null; +}; diff --git a/src-tauri/yaak-sync/index.ts b/src-tauri/yaak-sync/index.ts index 07a2e52d..d033b8e5 100644 --- a/src-tauri/yaak-sync/index.ts +++ b/src-tauri/yaak-sync/index.ts @@ -15,7 +15,6 @@ export async function calculateSync(workspace: Workspace) { } export async function applySync(workspace: Workspace, syncOps: SyncOp[]) { - console.log('Applying sync', syncOps); return invoke('plugin:yaak-sync|apply', { workspaceId: workspace.id, dir: workspace.settingSyncDir, @@ -23,17 +22,14 @@ export async function applySync(workspace: Workspace, syncOps: SyncOp[]) { }); } -export function useWatchWorkspace(workspace: Workspace | null, cb: (e: WatchEvent) => void) { +export function useWatchWorkspace(workspace: Workspace | null, callback: (e: WatchEvent) => void) { const workspaceId = workspace?.id ?? null; useEffect(() => { if (workspaceId == null) return; - console.log('Watching workspace', workspaceId); const channel = new Channel(); - channel.onmessage = (event) => { - cb(event); - }; + channel.onmessage = callback; const promise = invoke('plugin:yaak-sync|watch', { workspaceId, channel }); return () => { diff --git a/src-web/components/CommandPaletteDialog.tsx b/src-web/components/CommandPaletteDialog.tsx index 3ea597ea..f35468f0 100644 --- a/src-web/components/CommandPaletteDialog.tsx +++ b/src-web/components/CommandPaletteDialog.tsx @@ -1,4 +1,3 @@ -import { useNavigate } from '@tanstack/react-router'; import classNames from 'classnames'; import { fuzzyFilter } from 'fuzzbunny'; import type { KeyboardEvent, ReactNode } from 'react'; @@ -13,7 +12,6 @@ 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'; @@ -29,7 +27,9 @@ import { useScrollIntoView } from '../hooks/useScrollIntoView'; import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest'; import { useSidebarHidden } from '../hooks/useSidebarHidden'; import { useWorkspaces } from '../hooks/useWorkspaces'; +import { showDialog, toggleDialog } from '../lib/dialog'; import { fallbackRequestName } from '../lib/fallbackRequestName'; +import { router } from '../lib/router'; import { CookieDialog } from './CookieDialog'; import { Button } from './core/Button'; import { Heading } from './core/Heading'; @@ -70,16 +70,14 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { const openWorkspace = useOpenWorkspace(); const createHttpRequest = useCreateHttpRequest(); const { createFolder } = useCommands(); - const [activeCookieJar] = useActiveCookieJar(); + const activeCookieJar = useActiveCookieJar(); const createGrpcRequest = useCreateGrpcRequest(); const createEnvironment = useCreateEnvironment(); - const dialog = useDialog(); const sendRequest = useSendAnyHttpRequest(); const renameRequest = useRenameRequest(activeRequest?.id ?? null); const deleteRequest = useDeleteRequest(activeRequest?.id ?? null); const [, setSidebarHidden] = useSidebarHidden(); const openSettings = useOpenSettings(); - const navigate = useNavigate(); const { baseEnvironment } = useEnvironments(); const workspaceCommands = useMemo(() => { @@ -109,7 +107,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { key: 'cookies.show', label: 'Show Cookies', onSelect: async () => { - dialog.show({ + showDialog({ id: 'cookies', title: 'Manage Cookies', size: 'full', @@ -127,7 +125,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { label: 'Edit Environment', action: 'environmentEditor.toggle', onSelect: () => { - dialog.toggle({ + toggleDialog({ id: 'environment-editor', noPadding: true, size: 'lg', @@ -195,7 +193,6 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { createHttpRequest, createWorkspace, deleteRequest.mutate, - dialog, httpRequestActions, openSettings.mutate, renameRequest.mutate, @@ -284,13 +281,10 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { ), onSelect: async () => { - await navigate({ - to: '/workspaces/$workspaceId/requests/$requestId', - params: { - workspaceId: r.workspaceId, - requestId: r.id, - }, - search: (prev) => ({ ...prev }), + await router.navigate({ + to: '/workspaces/$workspaceId', + params: { workspaceId: r.workspaceId }, + search: (prev) => ({ ...prev, request_id: r.id }), }); }, }); @@ -331,7 +325,6 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { }, [ workspaceCommands, sortedRequests, - navigate, sortedEnvironments, activeEnvironment?.id, setActiveEnvironmentId, diff --git a/src-web/components/CookieDropdown.tsx b/src-web/components/CookieDropdown.tsx index 360452f6..64a57578 100644 --- a/src-web/components/CookieDropdown.tsx +++ b/src-web/components/CookieDropdown.tsx @@ -1,11 +1,11 @@ -import { memo, useCallback } from 'react'; -import { useActiveCookieJar } from '../hooks/useActiveCookieJar'; +import { memo, useMemo } from 'react'; +import { setActiveCookieJar, useActiveCookieJar } from '../hooks/useActiveCookieJar'; import { cookieJarsAtom } from '../hooks/useCookieJars'; import { useCreateCookieJar } from '../hooks/useCreateCookieJar'; import { useDeleteCookieJar } from '../hooks/useDeleteCookieJar'; -import { useDialog } from '../hooks/useDialog'; import { usePrompt } from '../hooks/usePrompt'; import { useUpdateCookieJar } from '../hooks/useUpdateCookieJar'; +import { showDialog } from '../lib/dialog'; import { jotaiStore } from '../lib/jotai'; import { CookieDialog } from './CookieDialog'; import { Dropdown, type DropdownItem } from './core/Dropdown'; @@ -14,21 +14,20 @@ import { IconButton } from './core/IconButton'; import { InlineCode } from './core/InlineCode'; export const CookieDropdown = memo(function CookieDropdown() { - const [activeCookieJar, setActiveCookieJarId] = useActiveCookieJar(); + const activeCookieJar = useActiveCookieJar(); const updateCookieJar = useUpdateCookieJar(activeCookieJar?.id ?? null); const deleteCookieJar = useDeleteCookieJar(activeCookieJar ?? null); const createCookieJar = useCreateCookieJar(); - const dialog = useDialog(); const prompt = usePrompt(); - const getItems = useCallback((): DropdownItem[] => { + const items = useMemo((): DropdownItem[] => { const cookieJars = jotaiStore.get(cookieJarsAtom) ?? []; return [ ...cookieJars.map((j) => ({ key: j.id, label: j.name, leftSlot: , - onSelect: () => setActiveCookieJarId(j.id), + onSelect: () => setActiveCookieJar(j), })), ...((cookieJars.length > 0 && activeCookieJar != null ? [ @@ -39,7 +38,7 @@ export const CookieDropdown = memo(function CookieDropdown() { leftSlot: , onSelect: () => { if (activeCookieJar == null) return; - dialog.show({ + showDialog({ id: 'cookies', title: 'Manage Cookies', size: 'full', @@ -90,18 +89,10 @@ export const CookieDropdown = memo(function CookieDropdown() { onSelect: () => createCookieJar.mutate(), }, ]; - }, [ - activeCookieJar, - createCookieJar, - deleteCookieJar, - dialog, - prompt, - setActiveCookieJarId, - updateCookieJar, - ]); + }, [activeCookieJar, createCookieJar, deleteCookieJar, prompt, updateCookieJar]); return ( - + ); diff --git a/src-web/components/DialogContext.tsx b/src-web/components/DialogContext.tsx deleted file mode 100644 index 68f48ae6..00000000 --- a/src-web/components/DialogContext.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { createContext } from 'react'; -import type { DialogState } from './Dialogs'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const DialogContext = createContext({} as DialogState); diff --git a/src-web/components/Dialogs.tsx b/src-web/components/Dialogs.tsx index f81922ba..dff43a23 100644 --- a/src-web/components/Dialogs.tsx +++ b/src-web/components/Dialogs.tsx @@ -1,74 +1,29 @@ -import React, { useContext, useMemo, useState } from 'react'; -import { trackEvent } from '../lib/analytics'; +import { useAtomValue } from 'jotai'; +import React from 'react'; +import { dialogsAtom, hideDialog } from '../lib/dialog'; import { Dialog, type DialogProps } from './core/Dialog'; -import { DialogContext } from './DialogContext'; -type DialogEntry = { +export type DialogInstance = { id: string; render: ({ hide }: { hide: () => void }) => React.ReactNode; } & Omit; -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([]); - const actions = useMemo( - () => ({ - 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 {children}; -}; - -function DialogInstance({ id, render, onClose, ...props }: DialogEntry) { - const { actions } = useContext(DialogContext); - const children = render({ hide: () => actions.hide(id) }); - return ( - { - onClose?.(); - actions.hide(id); - }} - {...props} - > - {children} - - ); -} - export function Dialogs() { - const { dialogs } = useContext(DialogContext); + const dialogs = useAtomValue(dialogsAtom); return ( <> - {dialogs.map((props: DialogEntry) => ( - + {dialogs.map(({ render, onClose, id, ...props }: DialogInstance) => ( + { + onClose?.(); + hideDialog(id); + }} + {...props} + > + {render({ hide: () => hideDialog(id) })} + ))} ); diff --git a/src-web/components/EnvironmentActionsDropdown.tsx b/src-web/components/EnvironmentActionsDropdown.tsx index 5794bfc0..9364ba5e 100644 --- a/src-web/components/EnvironmentActionsDropdown.tsx +++ b/src-web/components/EnvironmentActionsDropdown.tsx @@ -2,12 +2,12 @@ import classNames from 'classnames'; import { memo, useCallback, useMemo } from 'react'; import { useActiveEnvironment } from '../hooks/useActiveEnvironment'; import { useEnvironments } from '../hooks/useEnvironments'; +import { toggleDialog } from '../lib/dialog'; import type { ButtonProps } from './core/Button'; import { Button } from './core/Button'; import type { DropdownItem } from './core/Dropdown'; import { Dropdown } from './core/Dropdown'; import { Icon } from './core/Icon'; -import { useDialog } from '../hooks/useDialog'; import { EnvironmentEditDialog } from './EnvironmentEditDialog'; type Props = { @@ -20,17 +20,16 @@ export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdo }: Props) { const { subEnvironments, baseEnvironment } = useEnvironments(); const [activeEnvironment, setActiveEnvironmentId] = useActiveEnvironment(); - const dialog = useDialog(); const showEnvironmentDialog = useCallback(() => { - dialog.toggle({ + toggleDialog({ id: 'environment-editor', noPadding: true, size: 'lg', className: 'h-[80vh]', render: () => , }); - }, [dialog, activeEnvironment]); + }, [activeEnvironment]); const items: DropdownItem[] = useMemo( () => [ diff --git a/src-web/components/GlobalHooks.tsx b/src-web/components/GlobalHooks.tsx index b32d8c54..4ddb64a8 100644 --- a/src-web/components/GlobalHooks.tsx +++ b/src-web/components/GlobalHooks.tsx @@ -7,7 +7,7 @@ import { useSubscribeActiveCookieJarId, } from '../hooks/useActiveCookieJar'; import { useSubscribeActiveEnvironmentId } from '../hooks/useActiveEnvironment'; -import { useActiveRequest } from '../hooks/useActiveRequest'; +import { getActiveRequest, useActiveRequest } from '../hooks/useActiveRequest'; import { useSubscribeActiveRequestId } from '../hooks/useActiveRequestId'; import { useActiveWorkspace, useSubscribeActiveWorkspaceId } from '../hooks/useActiveWorkspace'; import { useActiveWorkspaceChangedToast } from '../hooks/useActiveWorkspaceChangedToast'; @@ -78,6 +78,7 @@ export function GlobalHooks() { navigateAfter: true, }); useHotKey('http_request.duplicate', async () => { + const activeRequest = getActiveRequest(); if (activeRequest?.model === 'http_request') { await duplicateHttpRequest.mutateAsync(); } else { diff --git a/src-web/components/GraphQLEditor.tsx b/src-web/components/GraphQLEditor.tsx index be5b79ca..70127c79 100644 --- a/src-web/components/GraphQLEditor.tsx +++ b/src-web/components/GraphQLEditor.tsx @@ -5,8 +5,8 @@ import type { EditorView } from 'codemirror'; import { formatSdl } from 'format-graphql'; import { useEffect, useMemo, useRef, useState } from 'react'; import { useLocalStorage } from 'react-use'; -import { useDialog } from '../hooks/useDialog'; import { useIntrospectGraphQL } from '../hooks/useIntrospectGraphQL'; +import { showDialog } from '../lib/dialog'; import { tryFormatJson } from '../lib/formatters'; import { Button } from './core/Button'; import { Dropdown } from './core/Dropdown'; @@ -62,8 +62,6 @@ export function GraphQLEditor({ request, onChange, baseRequest, ...extraEditorPr updateSchema(editorViewRef.current, schema ?? undefined); }, [schema]); - const dialog = useDialog(); - const actions = useMemo( () => [
@@ -122,7 +120,7 @@ export function GraphQLEditor({ request, onChange, baseRequest, ...extraEditorPr color="danger" isLoading={isLoading} onClick={() => { - dialog.show({ + showDialog({ title: 'Introspection Failed', size: 'dynamic', id: 'introspection-failed', @@ -161,7 +159,6 @@ export function GraphQLEditor({ request, onChange, baseRequest, ...extraEditorPr clear, schema, setAutoIntrospectDisabled, - dialog, ], ); diff --git a/src-web/components/GrpcEditor.tsx b/src-web/components/GrpcEditor.tsx index 61ab7f50..1e12c24c 100644 --- a/src-web/components/GrpcEditor.tsx +++ b/src-web/components/GrpcEditor.tsx @@ -1,5 +1,6 @@ import { jsonLanguage } from '@codemirror/lang-json'; import { linter } from '@codemirror/lint'; +import type { GrpcRequest } from '@yaakapp-internal/models'; import classNames from 'classnames'; import type { EditorView } from 'codemirror'; import { @@ -10,19 +11,18 @@ import { updateSchema, } from 'codemirror-json-schema'; import { useEffect, useMemo, useRef } from 'react'; -import { useAlert } from '../hooks/useAlert'; import type { ReflectResponseService } from '../hooks/useGrpc'; +import { showAlert } from '../lib/alert'; +import { showDialog } from '../lib/dialog'; import { tryFormatJson } from '../lib/formatters'; -import type { GrpcRequest } from '@yaakapp-internal/models'; import { pluralizeCount } from '../lib/pluralize'; import { Button } from './core/Button'; +import type { EditorProps } from './core/Editor/Editor'; +import { Editor } from './core/Editor/Editor'; import { FormattedError } from './core/FormattedError'; import { InlineCode } from './core/InlineCode'; import { VStack } from './core/Stacks'; -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 & { services: ReflectResponseService[] | null; @@ -42,9 +42,6 @@ export function GrpcEditor({ }: Props) { const editorViewRef = useRef(null); - const alert = useAlert(); - const dialog = useDialog(); - // Find the schema for the selected service and method and update the editor useEffect(() => { if ( @@ -59,7 +56,7 @@ export function GrpcEditor({ const s = services.find((s) => s.name === request.service); if (s == null) { console.log('Failed to find service', { service: request.service, services }); - alert({ + showAlert({ id: 'grpc-find-service-error', title: "Couldn't Find Service", body: ( @@ -74,7 +71,7 @@ export function GrpcEditor({ const schema = s.methods.find((m) => m.name === request.method)?.schema; if (request.method != null && schema == null) { console.log('Failed to find method', { method: request.method, methods: s?.methods }); - alert({ + showAlert({ id: 'grpc-find-schema-error', title: "Couldn't Find Method", body: ( @@ -94,7 +91,7 @@ export function GrpcEditor({ try { updateSchema(editorViewRef.current, JSON.parse(schema)); } catch (err) { - alert({ + showAlert({ id: 'grpc-parse-schema-error', title: 'Failed to Parse Schema', body: ( @@ -108,7 +105,7 @@ export function GrpcEditor({ ), }); } - }, [alert, services, request.method, request.service]); + }, [services, request.method, request.service]); const extraExtensions = useMemo( () => [ @@ -143,7 +140,7 @@ export function GrpcEditor({ } isLoading={reflectionLoading} onClick={() => { - dialog.show({ + showDialog({ title: 'Configure Schema', size: 'md', id: 'reflection-failed', @@ -172,7 +169,6 @@ export function GrpcEditor({
, ], [ - dialog, protoFiles.length, reflectionError, reflectionLoading, diff --git a/src-web/components/MoveToWorkspaceDialog.tsx b/src-web/components/MoveToWorkspaceDialog.tsx index ca71d27c..e08c364b 100644 --- a/src-web/components/MoveToWorkspaceDialog.tsx +++ b/src-web/components/MoveToWorkspaceDialog.tsx @@ -1,11 +1,11 @@ -import { useNavigate } from '@tanstack/react-router'; import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models'; import React, { useState } from 'react'; import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest'; import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest'; import { useWorkspaces } from '../hooks/useWorkspaces'; import { fallbackRequestName } from '../lib/fallbackRequestName'; -import {showToast} from "../lib/toast"; +import { router } from '../lib/router'; +import { showToast } from '../lib/toast'; import { Button } from './core/Button'; import { InlineCode } from './core/InlineCode'; import { Select } from './core/Select'; @@ -21,7 +21,6 @@ export function MoveToWorkspaceDialog({ onDone, request, activeWorkspaceId }: Pr const workspaces = useWorkspaces(); const updateHttpRequest = useUpdateAnyHttpRequest(); const updateGrpcRequest = useUpdateAnyGrpcRequest(); - const navigate = useNavigate(); const [selectedWorkspaceId, setSelectedWorkspaceId] = useState(activeWorkspaceId); return ( @@ -69,7 +68,7 @@ export function MoveToWorkspaceDialog({ onDone, request, activeWorkspaceId }: Pr color="secondary" className="mr-auto min-w-[5rem]" onClick={async () => { - await navigate({ + await router.navigate({ to: '/workspaces/$workspaceId', params: { workspaceId: selectedWorkspaceId }, }); diff --git a/src-web/components/RecentRequestsDropdown.tsx b/src-web/components/RecentRequestsDropdown.tsx index b4244141..00dce15a 100644 --- a/src-web/components/RecentRequestsDropdown.tsx +++ b/src-web/components/RecentRequestsDropdown.tsx @@ -1,6 +1,5 @@ -import { useNavigate } from '@tanstack/react-router'; import classNames from 'classnames'; -import { useCallback, useMemo, useRef } from 'react'; +import { useMemo, useRef } from 'react'; import { useKeyPressEvent } from 'react-use'; import { useActiveRequest } from '../hooks/useActiveRequest'; import { getActiveWorkspaceId } from '../hooks/useActiveWorkspace'; @@ -10,6 +9,7 @@ import { httpRequestsAtom } from '../hooks/useHttpRequests'; import { useRecentRequests } from '../hooks/useRecentRequests'; import { fallbackRequestName } from '../lib/fallbackRequestName'; import { jotaiStore } from '../lib/jotai'; +import { router } from '../lib/router'; import { Button } from './core/Button'; import type { DropdownItem, DropdownRef } from './core/Dropdown'; import { Dropdown } from './core/Dropdown'; @@ -24,7 +24,6 @@ export function RecentRequestsDropdown({ className }: Props) { const dropdownRef = useRef(null); const [allRecentRequestIds] = useRecentRequests(); const recentRequestIds = useMemo(() => allRecentRequestIds.slice(1), [allRecentRequestIds]); - const navigate = useNavigate(); // Handle key-up useKeyPressEvent('Control', undefined, () => { @@ -42,7 +41,7 @@ export function RecentRequestsDropdown({ className }: Props) { dropdownRef.current?.prev?.(); }); - const getItems = useCallback(() => { + const items = useMemo(() => { const activeWorkspaceId = getActiveWorkspaceId(); if (activeWorkspaceId === null) return []; @@ -58,13 +57,10 @@ export function RecentRequestsDropdown({ className }: Props) { // leftSlot: , leftSlot: , onSelect: async () => { - await navigate({ - to: '/workspaces/$workspaceId/requests/$requestId', - params: { - requestId: request.id, - workspaceId: activeWorkspaceId, - }, - search: (prev) => ({ ...prev }), + await router.navigate({ + to: '/workspaces/$workspaceId', + params: { workspaceId: activeWorkspaceId }, + search: (prev) => ({ ...prev, request_id: request.id }), }); }, }); @@ -82,10 +78,10 @@ export function RecentRequestsDropdown({ className }: Props) { } return recentRequestItems.slice(0, 20); - }, [navigate, recentRequestIds]); + }, [recentRequestIds]); return ( - +