Fix tauri event listener hook

This commit is contained in:
Gregory Schier
2025-01-04 07:01:31 -08:00
parent 36cecb2d29
commit 592c1228f1
12 changed files with 30 additions and 105 deletions

View File

@@ -39,7 +39,7 @@ export type SidebarModel = Folder | GrpcRequest | HttpRequest;
export function Sidebar({ className }: Props) { export function Sidebar({ className }: Props) {
const [hidden, setHidden] = useSidebarHidden(); const [hidden, setHidden] = useSidebarHidden();
const sidebarRef = useRef<HTMLLIElement>(null); const sidebarRef = useRef<HTMLElement>(null);
const activeWorkspace = useActiveWorkspace(); const activeWorkspace = useActiveWorkspace();
const httpResponses = useHttpResponses(); const httpResponses = useHttpResponses();
const grpcConnections = useGrpcConnections(); const grpcConnections = useGrpcConnections();
@@ -326,7 +326,7 @@ export function Sidebar({ className }: Props) {
return ( return (
<aside <aside
aria-hidden={hidden} aria-hidden={hidden ?? undefined}
ref={sidebarRef} ref={sidebarRef}
onFocus={handleFocus} onFocus={handleFocus}
onBlur={handleBlur} onBlur={handleBlur}

View File

@@ -87,9 +87,9 @@ export function Dialog({
)} )}
{description ? ( {description ? (
<p className="px-6 text-text-subtle" id={descriptionId}> <div className="px-6 text-text-subtle" id={descriptionId}>
{description} {description}
</p> </div>
) : ( ) : (
<span /> <span />
)} )}

View File

@@ -2,15 +2,11 @@ import { useNavigate } from '@tanstack/react-router';
import type { Folder, Workspace } from '@yaakapp-internal/models'; import type { Folder, Workspace } from '@yaakapp-internal/models';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { trackEvent } from '../lib/analytics'; import { trackEvent } from '../lib/analytics';
import { jotaiStore } from '../lib/jotai';
import { invokeCmd } from '../lib/tauri'; import { invokeCmd } from '../lib/tauri';
import { getActiveWorkspaceId } from './useActiveWorkspace'; import { getActiveWorkspaceId } from './useActiveWorkspace';
import { createFastMutation } from './useFastMutation'; import { createFastMutation } from './useFastMutation';
import { foldersAtom } from './useFolders';
import { usePrompt } from './usePrompt'; import { usePrompt } from './usePrompt';
import { updateModelList } from './useSyncModelStores';
import { useToast } from './useToast'; import { useToast } from './useToast';
import { workspacesAtom } from './useWorkspaces';
function makeCommands({ function makeCommands({
navigate, navigate,
@@ -25,9 +21,6 @@ function makeCommands({
mutationKey: ['create_workspace'], mutationKey: ['create_workspace'],
mutationFn: (patch) => invokeCmd<Workspace>('cmd_update_workspace', { workspace: patch }), mutationFn: (patch) => invokeCmd<Workspace>('cmd_update_workspace', { workspace: patch }),
onSuccess: async (workspace) => { onSuccess: async (workspace) => {
// Optimistic update
jotaiStore.set(workspacesAtom, updateModelList(workspace));
await navigate({ await navigate({
to: '/workspaces/$workspaceId', to: '/workspaces/$workspaceId',
params: { workspaceId: workspace.id }, params: { workspaceId: workspace.id },
@@ -65,12 +58,6 @@ function makeCommands({
patch.sortPriority = patch.sortPriority || -Date.now(); patch.sortPriority = patch.sortPriority || -Date.now();
return invokeCmd<Folder>('cmd_update_folder', { folder: { workspaceId, ...patch } }); return invokeCmd<Folder>('cmd_update_folder', { folder: { workspaceId, ...patch } });
}, },
onSuccess: async (folder) => {
if (folder == null) return;
// Optimistic update
jotaiStore.set(foldersAtom, updateModelList(folder));
},
onSettled: () => trackEvent('folder', 'create'), onSettled: () => trackEvent('folder', 'create'),
}), }),
} as const; } as const;

View File

@@ -1,16 +1,12 @@
import type { CookieJar } from '@yaakapp-internal/models'; import type { CookieJar } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai';
import { trackEvent } from '../lib/analytics'; import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri'; import { invokeCmd } from '../lib/tauri';
import { getActiveWorkspaceId } from './useActiveWorkspace'; import { getActiveWorkspaceId } from './useActiveWorkspace';
import { cookieJarsAtom } from './useCookieJars';
import { useFastMutation } from './useFastMutation'; import { useFastMutation } from './useFastMutation';
import { usePrompt } from './usePrompt'; import { usePrompt } from './usePrompt';
import { updateModelList } from './useSyncModelStores';
export function useCreateCookieJar() { export function useCreateCookieJar() {
const prompt = usePrompt(); const prompt = usePrompt();
const setCookieJars = useSetAtom(cookieJarsAtom);
return useFastMutation<CookieJar | null>({ return useFastMutation<CookieJar | null>({
mutationKey: ['create_cookie_jar'], mutationKey: ['create_cookie_jar'],
@@ -31,12 +27,6 @@ export function useCreateCookieJar() {
return invokeCmd('cmd_create_cookie_jar', { workspaceId, name }); return invokeCmd('cmd_create_cookie_jar', { workspaceId, name });
}, },
onSuccess: (cookieJar) => {
if (cookieJar == null) return;
// Optimistic update
setCookieJars(updateModelList(cookieJar));
},
onSettled: () => trackEvent('cookie_jar', 'create'), onSettled: () => trackEvent('cookie_jar', 'create'),
}); });
} }

View File

@@ -1,18 +1,14 @@
import type { Environment } from '@yaakapp-internal/models'; import type { Environment } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai';
import { trackEvent } from '../lib/analytics'; import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri'; import { invokeCmd } from '../lib/tauri';
import { useActiveEnvironment } from './useActiveEnvironment'; import { useActiveEnvironment } from './useActiveEnvironment';
import { getActiveWorkspaceId } from './useActiveWorkspace'; import { getActiveWorkspaceId } from './useActiveWorkspace';
import { environmentsAtom } from './useEnvironments';
import { useFastMutation } from './useFastMutation'; import { useFastMutation } from './useFastMutation';
import { usePrompt } from './usePrompt'; import { usePrompt } from './usePrompt';
import { updateModelList } from './useSyncModelStores';
export function useCreateEnvironment() { export function useCreateEnvironment() {
const [, setActiveEnvironmentId] = useActiveEnvironment(); const [, setActiveEnvironmentId] = useActiveEnvironment();
const prompt = usePrompt(); const prompt = usePrompt();
const setEnvironments = useSetAtom(environmentsAtom);
return useFastMutation<Environment | null, unknown, Environment | null>({ return useFastMutation<Environment | null, unknown, Environment | null>({
mutationKey: ['create_environment'], mutationKey: ['create_environment'],
@@ -43,10 +39,6 @@ export function useCreateEnvironment() {
onSettled: () => trackEvent('environment', 'create'), onSettled: () => trackEvent('environment', 'create'),
onSuccess: async (environment) => { onSuccess: async (environment) => {
if (environment == null) return; if (environment == null) return;
// Optimistic update
setEnvironments(updateModelList(environment));
await setActiveEnvironmentId(environment.id); await setActiveEnvironmentId(environment.id);
}, },
}); });

View File

@@ -1,17 +1,13 @@
import { useNavigate } from '@tanstack/react-router'; import { useNavigate } from '@tanstack/react-router';
import type { GrpcRequest } from '@yaakapp-internal/models'; import type { GrpcRequest } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai';
import { trackEvent } from '../lib/analytics'; import { trackEvent } from '../lib/analytics';
import { jotaiStore } from '../lib/jotai'; import { jotaiStore } from '../lib/jotai';
import { invokeCmd } from '../lib/tauri'; import { invokeCmd } from '../lib/tauri';
import { getActiveRequest } from './useActiveRequest'; import { getActiveRequest } from './useActiveRequest';
import { activeWorkspaceAtom } from './useActiveWorkspace'; import { activeWorkspaceAtom } from './useActiveWorkspace';
import { useFastMutation } from './useFastMutation'; import { useFastMutation } from './useFastMutation';
import { grpcRequestsAtom } from './useGrpcRequests';
import { updateModelList } from './useSyncModelStores';
export function useCreateGrpcRequest() { export function useCreateGrpcRequest() {
const setGrpcRequests = useSetAtom(grpcRequestsAtom);
const navigate = useNavigate(); const navigate = useNavigate();
return useFastMutation< return useFastMutation<
@@ -44,9 +40,6 @@ export function useCreateGrpcRequest() {
}, },
onSettled: () => trackEvent('grpc_request', 'create'), onSettled: () => trackEvent('grpc_request', 'create'),
onSuccess: async (request) => { onSuccess: async (request) => {
// Optimistic update
setGrpcRequests(updateModelList(request));
await navigate({ await navigate({
to: '/workspaces/$workspaceId/requests/$requestId', to: '/workspaces/$workspaceId/requests/$requestId',
params: { params: {

View File

@@ -1,16 +1,12 @@
import { useNavigate } from '@tanstack/react-router'; import { useNavigate } from '@tanstack/react-router';
import type { HttpRequest } from '@yaakapp-internal/models'; import type { HttpRequest } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai/index';
import { trackEvent } from '../lib/analytics'; import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri'; import { invokeCmd } from '../lib/tauri';
import { getActiveRequest } from './useActiveRequest'; import { getActiveRequest } from './useActiveRequest';
import { getActiveWorkspaceId } from './useActiveWorkspace'; import { getActiveWorkspaceId } from './useActiveWorkspace';
import { useFastMutation } from './useFastMutation'; import { useFastMutation } from './useFastMutation';
import { httpRequestsAtom } from './useHttpRequests';
import { updateModelList } from './useSyncModelStores';
export function useCreateHttpRequest() { export function useCreateHttpRequest() {
const setHttpRequests = useSetAtom(httpRequestsAtom);
const navigate = useNavigate(); const navigate = useNavigate();
return useFastMutation<HttpRequest, unknown, Partial<HttpRequest>>({ return useFastMutation<HttpRequest, unknown, Partial<HttpRequest>>({
@@ -38,9 +34,6 @@ export function useCreateHttpRequest() {
}, },
onSettled: () => trackEvent('http_request', 'create'), onSettled: () => trackEvent('http_request', 'create'),
onSuccess: async (request) => { onSuccess: async (request) => {
// Optimistic update
setHttpRequests(updateModelList(request));
await navigate({ await navigate({
to: '/workspaces/$workspaceId/requests/$requestId', to: '/workspaces/$workspaceId/requests/$requestId',
params: { workspaceId: request.workspaceId, requestId: request.id }, params: { workspaceId: request.workspaceId, requestId: request.id },

View File

@@ -1,18 +1,14 @@
import { useNavigate } from '@tanstack/react-router'; import { useNavigate } from '@tanstack/react-router';
import type { Workspace } from '@yaakapp-internal/models'; import type { Workspace } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai';
import { InlineCode } from '../components/core/InlineCode'; import { InlineCode } from '../components/core/InlineCode';
import { trackEvent } from '../lib/analytics'; import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri'; import { invokeCmd } from '../lib/tauri';
import { getActiveWorkspace } from './useActiveWorkspace'; import { getActiveWorkspace } from './useActiveWorkspace';
import { useConfirm } from './useConfirm'; import { useConfirm } from './useConfirm';
import { useFastMutation } from './useFastMutation'; import { useFastMutation } from './useFastMutation';
import { removeModelById } from './useSyncModelStores';
import { workspacesAtom } from './useWorkspaces';
export function useDeleteActiveWorkspace() { export function useDeleteActiveWorkspace() {
const confirm = useConfirm(); const confirm = useConfirm();
const setWorkspaces = useSetAtom(workspacesAtom);
const navigate = useNavigate(); const navigate = useNavigate();
return useFastMutation<Workspace | null, string>({ return useFastMutation<Workspace | null, string>({
@@ -35,10 +31,6 @@ export function useDeleteActiveWorkspace() {
onSettled: () => trackEvent('workspace', 'delete'), onSettled: () => trackEvent('workspace', 'delete'),
onSuccess: async (workspace) => { onSuccess: async (workspace) => {
if (workspace === null) return; if (workspace === null) return;
// Optimistic update
setWorkspaces(removeModelById(workspace));
await navigate({ to: '/workspaces' }); await navigate({ to: '/workspaces' });
}, },
}); });

View File

@@ -1,18 +1,14 @@
import { useFastMutation } from './useFastMutation';
import type { GrpcRequest } from '@yaakapp-internal/models'; import type { GrpcRequest } from '@yaakapp-internal/models';
import {useSetAtom} from "jotai";
import { InlineCode } from '../components/core/InlineCode'; import { InlineCode } from '../components/core/InlineCode';
import { trackEvent } from '../lib/analytics'; import { trackEvent } from '../lib/analytics';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { fallbackRequestName } from '../lib/fallbackRequestName';
import { getGrpcRequest } from '../lib/store'; import { getGrpcRequest } from '../lib/store';
import { invokeCmd } from '../lib/tauri'; import { invokeCmd } from '../lib/tauri';
import { useConfirm } from './useConfirm'; import { useConfirm } from './useConfirm';
import {grpcRequestsAtom} from "./useGrpcRequests"; import { useFastMutation } from './useFastMutation';
import {removeModelById} from "./useSyncModelStores";
export function useDeleteAnyGrpcRequest() { export function useDeleteAnyGrpcRequest() {
const confirm = useConfirm(); const confirm = useConfirm();
const setGrpcRequests = useSetAtom(grpcRequestsAtom);
return useFastMutation<GrpcRequest | null, string, string>({ return useFastMutation<GrpcRequest | null, string, string>({
mutationKey: ['delete_any_grpc_request'], mutationKey: ['delete_any_grpc_request'],
@@ -33,12 +29,6 @@ export function useDeleteAnyGrpcRequest() {
if (!confirmed) return null; if (!confirmed) return null;
return invokeCmd('cmd_delete_grpc_request', { requestId: id }); return invokeCmd('cmd_delete_grpc_request', { requestId: id });
}, },
onSuccess: (request) => {
if (request == null) return;
// Optimistic update
setGrpcRequests(removeModelById(request));
},
onSettled: () => trackEvent('grpc_request', 'delete'), onSettled: () => trackEvent('grpc_request', 'delete'),
}); });
} }

View File

@@ -1,18 +1,14 @@
import { useFastMutation } from './useFastMutation';
import type { HttpRequest } from '@yaakapp-internal/models'; import type { HttpRequest } from '@yaakapp-internal/models';
import { useSetAtom } from 'jotai';
import { InlineCode } from '../components/core/InlineCode'; import { InlineCode } from '../components/core/InlineCode';
import { trackEvent } from '../lib/analytics'; import { trackEvent } from '../lib/analytics';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { fallbackRequestName } from '../lib/fallbackRequestName';
import { getHttpRequest } from '../lib/store'; import { getHttpRequest } from '../lib/store';
import { invokeCmd } from '../lib/tauri'; import { invokeCmd } from '../lib/tauri';
import { useConfirm } from './useConfirm'; import { useConfirm } from './useConfirm';
import { httpRequestsAtom } from './useHttpRequests'; import { useFastMutation } from './useFastMutation';
import { removeModelById } from './useSyncModelStores';
export function useDeleteAnyHttpRequest() { export function useDeleteAnyHttpRequest() {
const confirm = useConfirm(); const confirm = useConfirm();
const setHttpRequests = useSetAtom(httpRequestsAtom);
return useFastMutation<HttpRequest | null, string, string>({ return useFastMutation<HttpRequest | null, string, string>({
mutationKey: ['delete_any_http_request'], mutationKey: ['delete_any_http_request'],
@@ -33,12 +29,6 @@ export function useDeleteAnyHttpRequest() {
if (!confirmed) return null; if (!confirmed) return null;
return invokeCmd<HttpRequest>('cmd_delete_http_request', { requestId: id }); return invokeCmd<HttpRequest>('cmd_delete_http_request', { requestId: id });
}, },
onSuccess: (request) => {
if (request == null) return;
// Optimistic update
setHttpRequests(removeModelById(request));
},
onSettled: () => trackEvent('http_request', 'delete'), onSettled: () => trackEvent('http_request', 'delete'),
}); });
} }

View File

@@ -1,35 +1,22 @@
import type { EventCallback, EventName } from '@tauri-apps/api/event'; import type {EventCallback, EventName} from '@tauri-apps/api/event';
import { listen } from '@tauri-apps/api/event'; import {listen} from '@tauri-apps/api/event';
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; import {getCurrentWebviewWindow} from '@tauri-apps/api/webviewWindow';
import type { DependencyList } from 'react'; import {useEffect} from 'react';
import { useEffect } from 'react';
/** /**
* React hook to listen to a Tauri event. * React hook to listen to a Tauri event.
*/ */
export function useListenToTauriEvent<T>( export function useListenToTauriEvent<T>(event: EventName, fn: EventCallback<T>) {
event: EventName,
fn: EventCallback<T>,
deps: DependencyList = [],
) {
useEffect(() => { useEffect(() => {
let unMounted = false; const unlisten = listen(
let unsubFn: (() => void) | undefined = undefined;
listen(
event, event,
fn, fn,
// Listen to `emit_all()` events or events specific to the current window // Listen to `emit_all()` events or events specific to the current window
{ target: { label: getCurrentWebviewWindow().label, kind: 'Window' } }, { target: { label: getCurrentWebviewWindow().label, kind: 'Window' } },
).then((unsub) => { );
if (unMounted) unsub();
else unsubFn = unsub;
});
return () => { return () => {
unMounted = true; unlisten.then((fn) => fn());
unsubFn?.(); }
}; }, [event, fn]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [event, fn, ...deps]);
} }

View File

@@ -20,9 +20,20 @@ if (osType !== 'macos') {
} }
window.addEventListener('keydown', (e) => { window.addEventListener('keydown', (e) => {
// Hack to not go back in history on backspace. Check for document body const rx = /input|select|textarea/i;
// or else it will prevent backspace in input fields.
if (e.key === 'Backspace' && e.target === document.body) e.preventDefault(); const target = e.target;
if (e.key !== 'Backspace') return;
if (!(target instanceof Element)) return;
if (target.getAttribute('contenteditable') !== null) return;
if (
!rx.test(target.tagName) ||
('disabled' in target && target.disabled) ||
('readOnly' in target && target.readOnly)
) {
e.preventDefault();
}
}); });
console.log('Creating React root'); console.log('Creating React root');