mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-26 11:21:16 +01:00
A bunch more small things
This commit is contained in:
@@ -1,157 +0,0 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { EditorView } from 'codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { json } from '@codemirror/lang-json';
|
||||
import { html } from '@codemirror/lang-html';
|
||||
import { tags } from '@lezer/highlight';
|
||||
import {
|
||||
bracketMatching,
|
||||
defaultHighlightStyle,
|
||||
foldGutter,
|
||||
foldKeymap,
|
||||
HighlightStyle,
|
||||
indentOnInput,
|
||||
LanguageSupport,
|
||||
syntaxHighlighting,
|
||||
} from '@codemirror/language';
|
||||
import {
|
||||
crosshairCursor,
|
||||
drawSelection,
|
||||
dropCursor,
|
||||
highlightActiveLine,
|
||||
highlightActiveLineGutter,
|
||||
highlightSpecialChars,
|
||||
keymap,
|
||||
lineNumbers,
|
||||
rectangularSelection,
|
||||
} from '@codemirror/view';
|
||||
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
|
||||
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search';
|
||||
import {
|
||||
autocompletion,
|
||||
closeBrackets,
|
||||
closeBracketsKeymap,
|
||||
completionKeymap,
|
||||
} from '@codemirror/autocomplete';
|
||||
import { lintKeymap } from '@codemirror/lint';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
|
||||
const myHighlightStyle = HighlightStyle.define([
|
||||
{
|
||||
tag: [tags.documentMeta, tags.blockComment, tags.lineComment, tags.docComment, tags.comment],
|
||||
color: '#757b93',
|
||||
},
|
||||
{ tag: tags.name, color: '#4699de' },
|
||||
{ tag: tags.variableName, color: '#31c434' },
|
||||
{ tag: tags.attributeName, color: '#b06fff' },
|
||||
{ tag: tags.attributeValue, color: '#ff964b' },
|
||||
{ tag: [tags.keyword, tags.string], color: '#e8b045' },
|
||||
{ tag: tags.comment, color: '#f5d', fontStyle: 'italic' },
|
||||
]);
|
||||
|
||||
const syntaxExtensions: Record<string, LanguageSupport> = {
|
||||
'application/json': json(),
|
||||
'application/javascript': javascript(),
|
||||
'text/html': html(),
|
||||
};
|
||||
|
||||
const extensions = [
|
||||
lineNumbers(),
|
||||
highlightActiveLineGutter(),
|
||||
highlightSpecialChars(),
|
||||
history(),
|
||||
foldGutter({
|
||||
markerDOM: (open) => {
|
||||
const el = document.createElement('div');
|
||||
el.classList.add('fold-gutter-icon');
|
||||
el.tabIndex = -1;
|
||||
if (open) {
|
||||
el.setAttribute('data-open', '');
|
||||
}
|
||||
return el;
|
||||
},
|
||||
}),
|
||||
drawSelection(),
|
||||
dropCursor(),
|
||||
EditorState.allowMultipleSelections.of(true),
|
||||
indentOnInput(),
|
||||
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||||
bracketMatching(),
|
||||
closeBrackets(),
|
||||
autocompletion(),
|
||||
rectangularSelection(),
|
||||
crosshairCursor(),
|
||||
highlightActiveLine(),
|
||||
highlightSelectionMatches(),
|
||||
keymap.of([
|
||||
...closeBracketsKeymap,
|
||||
...defaultKeymap,
|
||||
...searchKeymap,
|
||||
...historyKeymap,
|
||||
...foldKeymap,
|
||||
...completionKeymap,
|
||||
...lintKeymap,
|
||||
]),
|
||||
syntaxHighlighting(myHighlightStyle),
|
||||
];
|
||||
|
||||
export default function useCodeMirror({
|
||||
initialValue,
|
||||
value,
|
||||
contentType,
|
||||
onChange,
|
||||
}: {
|
||||
initialValue?: string;
|
||||
value?: string;
|
||||
contentType: string;
|
||||
onChange?: (value: string) => void;
|
||||
}) {
|
||||
const [cm, setCm] = useState<EditorView | null>(null);
|
||||
const ref = useRef(null);
|
||||
useEffect(() => {
|
||||
if (ref.current === null) return;
|
||||
const state = EditorState.create({
|
||||
doc: initialValue,
|
||||
extensions: getExtensions({ contentType, onChange }),
|
||||
});
|
||||
const view = new EditorView({
|
||||
state,
|
||||
parent: ref.current,
|
||||
});
|
||||
|
||||
setCm(view);
|
||||
|
||||
return () => view?.destroy();
|
||||
}, [ref.current]);
|
||||
|
||||
useEffect(() => {
|
||||
if (cm === null) return;
|
||||
|
||||
const newState = EditorState.create({
|
||||
doc: value ?? cm.state.doc,
|
||||
extensions: getExtensions({ contentType, onChange }),
|
||||
});
|
||||
cm.setState(newState);
|
||||
}, [cm, contentType, value, onChange]);
|
||||
|
||||
return { ref, cm };
|
||||
}
|
||||
|
||||
function getExtensions({
|
||||
contentType,
|
||||
onChange,
|
||||
}: {
|
||||
contentType: string;
|
||||
onChange?: (value: string) => void;
|
||||
}) {
|
||||
const ext = syntaxExtensions[contentType];
|
||||
return ext
|
||||
? [
|
||||
...extensions,
|
||||
...(onChange
|
||||
? [EditorView.updateListener.of((update) => onChange(update.state.doc.toString()))]
|
||||
: []),
|
||||
ext,
|
||||
]
|
||||
: extensions;
|
||||
}
|
||||
64
src-web/hooks/useRequest.ts
Normal file
64
src-web/hooks/useRequest.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { convertDates, HttpRequest } from '../lib/models';
|
||||
import { responsesQueryKey } from './useResponses';
|
||||
|
||||
export function useRequests(workspaceId: string) {
|
||||
return useQuery(['requests'], async () => {
|
||||
const requests = (await invoke('requests', { workspaceId })) as HttpRequest[];
|
||||
return requests.map(convertDates);
|
||||
});
|
||||
}
|
||||
|
||||
export function useRequestUpdate(request: HttpRequest | null) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<HttpRequest, unknown, Partial<HttpRequest>>({
|
||||
mutationFn: async (patch) => {
|
||||
if (request == null) {
|
||||
throw new Error("Can't update a null request");
|
||||
}
|
||||
// console.error('UPDATE REQUEST', patch);
|
||||
const req = await invoke('upsert_request', { ...request, ...patch });
|
||||
return convertDates(req as HttpRequest);
|
||||
},
|
||||
onSuccess: (req) => {
|
||||
queryClient.setQueryData(['requests'], (requests: HttpRequest[] = []) =>
|
||||
requests.map((r) => (r.id === req.id ? req : r)),
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useRequestCreate(workspaceId: string) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<HttpRequest, unknown, Partial<Omit<HttpRequest, 'workspaceId'>>>({
|
||||
mutationFn: async (patch) => {
|
||||
const req = await invoke('upsert_request', {
|
||||
url: '',
|
||||
method: 'GET',
|
||||
name: 'New Request',
|
||||
headers: [],
|
||||
...patch,
|
||||
workspaceId,
|
||||
});
|
||||
return convertDates(req as HttpRequest);
|
||||
},
|
||||
onSuccess: (req) => {
|
||||
queryClient.setQueryData(['requests'], (requests: HttpRequest[] = []) => [...requests, req]);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useSendRequest(request: HttpRequest | null) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<void, string>({
|
||||
mutationFn: async () => {
|
||||
if (request == null) return;
|
||||
await invoke('send_request', { requestId: request.id });
|
||||
},
|
||||
onSuccess: async () => {
|
||||
if (request == null) return;
|
||||
await queryClient.invalidateQueries(responsesQueryKey(request.id));
|
||||
},
|
||||
});
|
||||
}
|
||||
49
src-web/hooks/useResponses.ts
Normal file
49
src-web/hooks/useResponses.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { convertDates, HttpResponse } from '../lib/models';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
export function responsesQueryKey(requestId: string) {
|
||||
return ['responses', { requestId }];
|
||||
}
|
||||
|
||||
export function useResponses(requestId: string) {
|
||||
return useQuery<HttpResponse[]>({
|
||||
initialData: [],
|
||||
queryKey: responsesQueryKey(requestId),
|
||||
queryFn: async () => {
|
||||
const responses = (await invoke('responses', { requestId })) as HttpResponse[];
|
||||
return responses.map(convertDates);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteResponse(response?: HttpResponse) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async () => {
|
||||
if (response == null) return;
|
||||
await invoke('delete_response', { id: response.id });
|
||||
},
|
||||
onSuccess: () => {
|
||||
if (response == null) return;
|
||||
queryClient.setQueryData(
|
||||
['responses', { requestId: response.requestId }],
|
||||
(responses: HttpResponse[] = []) => responses.filter((r) => r.id !== response.id),
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteAllResponses(requestId?: string) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async () => {
|
||||
if (requestId == null) return;
|
||||
await invoke('delete_all_responses', { requestId });
|
||||
},
|
||||
onSuccess: () => {
|
||||
if (requestId == null) return;
|
||||
queryClient.setQueryData(['responses', { requestId: requestId }], []);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,61 +1,10 @@
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { useQuery, UseQueryResult } from 'react-query';
|
||||
import { convertDates, Workspace } from '../lib/models';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
interface BaseModel {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
deletedAt: Date | null;
|
||||
}
|
||||
|
||||
export interface Request extends BaseModel {
|
||||
name: string;
|
||||
url: string;
|
||||
body: string | null;
|
||||
method: string;
|
||||
}
|
||||
|
||||
export interface Workspace extends BaseModel {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export function useWorkspaces(): UseQueryResult<Workspace[]> {
|
||||
return useQuery('workspaces', async () => {
|
||||
export function useWorkspaces() {
|
||||
return useQuery(['workspaces'], async () => {
|
||||
const workspaces = (await invoke('workspaces')) as Workspace[];
|
||||
return workspaces.map(convertDates);
|
||||
});
|
||||
}
|
||||
|
||||
export function useRequests(workspaceId: string): UseQueryResult<Request[]> {
|
||||
return useQuery('requests', async () => {
|
||||
const requests = (await invoke('requests', { workspaceId })) as Request[];
|
||||
return requests.map(convertDates);
|
||||
});
|
||||
}
|
||||
|
||||
export function useWorkspace(): UseQueryResult<{ workspace: Workspace; requests: Request[] }> {
|
||||
return useQuery('workspace', async () => {
|
||||
const workspaces = (await invoke('workspaces')) as Workspace[];
|
||||
const requests = (await invoke('requests', { workspaceId: workspaces[0].id })) as Request[];
|
||||
return {
|
||||
workspace: convertDates(workspaces[0]),
|
||||
requests: requests.map(convertDates),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function convertDates<T extends BaseModel>(m: T): T {
|
||||
return {
|
||||
...m,
|
||||
createdAt: convertDate(m.createdAt),
|
||||
updatedAt: convertDate(m.updatedAt),
|
||||
deletedAt: m.deletedAt ? convertDate(m.deletedAt) : null,
|
||||
};
|
||||
}
|
||||
|
||||
function convertDate(d: string | Date): Date {
|
||||
const date = new Date(d);
|
||||
const userTimezoneOffset = date.getTimezoneOffset() * 60000;
|
||||
return new Date(date.getTime() - userTimezoneOffset);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user