mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-22 17:39:12 +01:00
Cookie Support (#19)
This commit is contained in:
@@ -9,7 +9,7 @@ export interface AlertProps {
|
||||
|
||||
export function Alert({ onHide, body }: AlertProps) {
|
||||
return (
|
||||
<VStack space={3}>
|
||||
<VStack space={3} className="pb-4">
|
||||
<div>{body}</div>
|
||||
<HStack space={2} justifyContent="end">
|
||||
<Button className="focus" color="primary" onClick={onHide}>
|
||||
|
||||
@@ -30,7 +30,7 @@ export function Confirm({ onHide, onResult, variant = 'confirm' }: ConfirmProps)
|
||||
};
|
||||
|
||||
return (
|
||||
<HStack space={2} justifyContent="end" className="mt-6">
|
||||
<HStack space={2} justifyContent="end" className="mt-2 mb-4">
|
||||
<Button className="focus" color="gray" onClick={handleHide}>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
@@ -12,9 +12,18 @@ export interface PromptProps {
|
||||
name: InputProps['name'];
|
||||
defaultValue?: InputProps['defaultValue'];
|
||||
placeholder?: InputProps['placeholder'];
|
||||
confirmLabel?: string;
|
||||
}
|
||||
|
||||
export function Prompt({ onHide, label, name, defaultValue, placeholder, onResult }: PromptProps) {
|
||||
export function Prompt({
|
||||
onHide,
|
||||
label,
|
||||
name,
|
||||
defaultValue,
|
||||
placeholder,
|
||||
onResult,
|
||||
confirmLabel = 'Save',
|
||||
}: PromptProps) {
|
||||
const [value, setValue] = useState<string>(defaultValue ?? '');
|
||||
const handleSubmit = useCallback(
|
||||
(e: FormEvent<HTMLFormElement>) => {
|
||||
@@ -27,7 +36,7 @@ export function Prompt({ onHide, label, name, defaultValue, placeholder, onResul
|
||||
|
||||
return (
|
||||
<form
|
||||
className="grid grid-rows-[auto_auto] grid-cols-[minmax(0,1fr)] gap-6"
|
||||
className="grid grid-rows-[auto_auto] grid-cols-[minmax(0,1fr)] gap-4 mb-4"
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Input
|
||||
@@ -45,7 +54,7 @@ export function Prompt({ onHide, label, name, defaultValue, placeholder, onResul
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" className="focus" color="primary">
|
||||
Save
|
||||
{confirmLabel}
|
||||
</Button>
|
||||
</HStack>
|
||||
</form>
|
||||
|
||||
22
src-web/hooks/useActiveCookieJar.ts
Normal file
22
src-web/hooks/useActiveCookieJar.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { NAMESPACE_GLOBAL } from '../lib/keyValueStore';
|
||||
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
|
||||
import { useCookieJars } from './useCookieJars';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
|
||||
export function useActiveCookieJar() {
|
||||
const workspaceId = useActiveWorkspaceId();
|
||||
const cookieJars = useCookieJars();
|
||||
|
||||
const kv = useKeyValue<string | null>({
|
||||
namespace: NAMESPACE_GLOBAL,
|
||||
key: ['activeCookieJar', workspaceId ?? 'n/a'],
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
const activeCookieJar = cookieJars.find((cookieJar) => cookieJar.id === kv.value);
|
||||
|
||||
return {
|
||||
activeCookieJar: activeCookieJar ?? null,
|
||||
setActiveCookieJarId: kv.set,
|
||||
};
|
||||
}
|
||||
22
src-web/hooks/useCookieJars.ts
Normal file
22
src-web/hooks/useCookieJars.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import type { CookieJar } from '../lib/models';
|
||||
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
|
||||
|
||||
export function cookieJarsQueryKey({ workspaceId }: { workspaceId: string }) {
|
||||
return ['cookie_jars', { workspaceId }];
|
||||
}
|
||||
|
||||
export function useCookieJars() {
|
||||
const workspaceId = useActiveWorkspaceId();
|
||||
return (
|
||||
useQuery({
|
||||
enabled: workspaceId != null,
|
||||
queryKey: cookieJarsQueryKey({ workspaceId: workspaceId ?? 'n/a' }),
|
||||
queryFn: async () => {
|
||||
if (workspaceId == null) return [];
|
||||
return (await invoke('list_cookie_jars', { workspaceId })) as CookieJar[];
|
||||
},
|
||||
}).data ?? []
|
||||
);
|
||||
}
|
||||
35
src-web/hooks/useCreateCookieJar.ts
Normal file
35
src-web/hooks/useCreateCookieJar.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import type { CookieJar, HttpRequest } from '../lib/models';
|
||||
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
|
||||
import { usePrompt } from './usePrompt';
|
||||
import { requestsQueryKey } from './useRequests';
|
||||
|
||||
export function useCreateCookieJar() {
|
||||
const workspaceId = useActiveWorkspaceId();
|
||||
const queryClient = useQueryClient();
|
||||
const prompt = usePrompt();
|
||||
|
||||
return useMutation<HttpRequest>({
|
||||
mutationFn: async () => {
|
||||
if (workspaceId === null) {
|
||||
throw new Error("Cannot create cookie jar when there's no active workspace");
|
||||
}
|
||||
const name = await prompt({
|
||||
name: 'name',
|
||||
title: 'New CookieJar',
|
||||
label: 'Name',
|
||||
defaultValue: 'My Jar',
|
||||
});
|
||||
return invoke('create_cookie_jar', { workspaceId, name });
|
||||
},
|
||||
onSettled: () => trackEvent('CookieJar', 'Create'),
|
||||
onSuccess: async (request) => {
|
||||
queryClient.setQueryData<HttpRequest[]>(
|
||||
requestsQueryKey({ workspaceId: request.workspaceId }),
|
||||
(requests) => [...(requests ?? []), request],
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
37
src-web/hooks/useDeleteCookieJar.tsx
Normal file
37
src-web/hooks/useDeleteCookieJar.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import type { CookieJar, Workspace } from '../lib/models';
|
||||
import { useConfirm } from './useConfirm';
|
||||
import { cookieJarsQueryKey } from './useCookieJars';
|
||||
|
||||
export function useDeleteCookieJar(cookieJar: CookieJar | null) {
|
||||
const queryClient = useQueryClient();
|
||||
const confirm = useConfirm();
|
||||
|
||||
return useMutation<CookieJar | null, string>({
|
||||
mutationFn: async () => {
|
||||
const confirmed = await confirm({
|
||||
title: 'Delete CookieJar',
|
||||
variant: 'delete',
|
||||
description: (
|
||||
<>
|
||||
Permanently delete <InlineCode>{cookieJar?.name}</InlineCode>?
|
||||
</>
|
||||
),
|
||||
});
|
||||
if (!confirmed) return null;
|
||||
return invoke('delete_cookie_jar', { cookieJarId: cookieJar?.id });
|
||||
},
|
||||
onSettled: () => trackEvent('CookieJar', 'Delete'),
|
||||
onSuccess: async (cookieJar) => {
|
||||
if (cookieJar === null) return;
|
||||
|
||||
const { id: cookieJarId, workspaceId } = cookieJar;
|
||||
queryClient.setQueryData<CookieJar[]>(cookieJarsQueryKey({ workspaceId }), (cookieJars) =>
|
||||
cookieJars?.filter((e) => e.id !== cookieJarId),
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -47,7 +47,7 @@ export function useImportData() {
|
||||
render: ({ hide }) => {
|
||||
const { workspaces, environments, folders, requests } = imported;
|
||||
return (
|
||||
<VStack space={3}>
|
||||
<VStack space={3} className="pb-4">
|
||||
<ul className="list-disc pl-6">
|
||||
<li>{count('Workspace', workspaces.length)}</li>
|
||||
<li>{count('Environment', environments.length)}</li>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { dialog } from '@tauri-apps/api';
|
||||
import type { DialogProps } from '../components/core/Dialog';
|
||||
import { useDialog } from '../components/DialogContext';
|
||||
import type { PromptProps } from './Prompt';
|
||||
@@ -13,8 +12,8 @@ export function usePrompt() {
|
||||
label,
|
||||
defaultValue,
|
||||
placeholder,
|
||||
}: Pick<DialogProps, 'title' | 'description'> &
|
||||
Pick<PromptProps, 'name' | 'label' | 'defaultValue' | 'placeholder'>) =>
|
||||
confirmLabel,
|
||||
}: Pick<DialogProps, 'title' | 'description'> & Omit<PromptProps, 'onResult' | 'onHide'>) =>
|
||||
new Promise((onResult: PromptProps['onResult']) => {
|
||||
dialog.show({
|
||||
title,
|
||||
@@ -22,7 +21,7 @@ export function usePrompt() {
|
||||
hideX: true,
|
||||
size: 'sm',
|
||||
render: ({ hide }) =>
|
||||
Prompt({ onHide: hide, onResult, name, label, defaultValue, placeholder }),
|
||||
Prompt({ onHide: hide, onResult, name, label, defaultValue, placeholder, confirmLabel }),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,12 +5,14 @@ import slugify from 'slugify';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import type { HttpResponse } from '../lib/models';
|
||||
import { getRequest } from '../lib/store';
|
||||
import { useActiveCookieJar } from './useActiveCookieJar';
|
||||
import { useActiveEnvironmentId } from './useActiveEnvironmentId';
|
||||
import { useAlert } from './useAlert';
|
||||
|
||||
export function useSendAnyRequest(options: { download?: boolean } = {}) {
|
||||
const environmentId = useActiveEnvironmentId();
|
||||
const alert = useAlert();
|
||||
const { activeCookieJar } = useActiveCookieJar();
|
||||
return useMutation<HttpResponse | null, string, string | null>({
|
||||
mutationFn: async (id) => {
|
||||
const request = await getRequest(id);
|
||||
@@ -33,6 +35,7 @@ export function useSendAnyRequest(options: { download?: boolean } = {}) {
|
||||
requestId: id,
|
||||
environmentId,
|
||||
downloadDir: downloadDir,
|
||||
cookieJarId: activeCookieJar?.id,
|
||||
});
|
||||
},
|
||||
onSettled: () => trackEvent('HttpRequest', 'Send'),
|
||||
|
||||
@@ -17,7 +17,6 @@ export function useSyncWindowTitle() {
|
||||
newTitle += ` – ${fallbackRequestName(activeRequest)}`;
|
||||
}
|
||||
|
||||
console.log('Skipping setting window title to ', newTitle);
|
||||
// TODO: This resets the stoplight position so we can't use it yet
|
||||
// appWindow.setTitle(newTitle).catch(console.error);
|
||||
}, [activeEnvironment, activeRequest, activeWorkspace]);
|
||||
|
||||
30
src-web/hooks/useUpdateCookieJar.ts
Normal file
30
src-web/hooks/useUpdateCookieJar.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import type { CookieJar } from '../lib/models';
|
||||
import { getCookieJar } from '../lib/store';
|
||||
import { cookieJarsQueryKey } from './useCookieJars';
|
||||
|
||||
export function useUpdateCookieJar(id: string | null) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<void, unknown, Partial<CookieJar> | ((j: CookieJar) => CookieJar)>({
|
||||
mutationFn: async (v) => {
|
||||
const cookieJar = await getCookieJar(id);
|
||||
if (cookieJar == null) {
|
||||
throw new Error("Can't update a null workspace");
|
||||
}
|
||||
|
||||
const newCookieJar = typeof v === 'function' ? v(cookieJar) : { ...cookieJar, ...v };
|
||||
console.log('NEW COOKIE JAR', newCookieJar.cookies.length);
|
||||
await invoke('update_cookie_jar', { cookieJar: newCookieJar });
|
||||
},
|
||||
onMutate: async (v) => {
|
||||
const cookieJar = await getCookieJar(id);
|
||||
if (cookieJar === null) return;
|
||||
|
||||
const newCookieJar = typeof v === 'function' ? v(cookieJar) : { ...cookieJar, ...v };
|
||||
queryClient.setQueryData<CookieJar[]>(cookieJarsQueryKey(cookieJar), (cookieJars) =>
|
||||
(cookieJars ?? []).map((j) => (j.id === newCookieJar.id ? newCookieJar : j)),
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -21,7 +21,6 @@ export function useUpdateWorkspace(id: string | null) {
|
||||
if (workspace === null) return;
|
||||
|
||||
const newWorkspace = typeof v === 'function' ? v(workspace) : { ...workspace, ...v };
|
||||
console.log('NEW WORKSPACE', newWorkspace);
|
||||
queryClient.setQueryData<Workspace[]>(workspacesQueryKey(workspace), (workspaces) =>
|
||||
(workspaces ?? []).map((w) => (w.id === newWorkspace.id ? newWorkspace : w)),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user