mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-20 07:41:22 +02:00
Generalized frontend model store (#193)
This commit is contained in:
25
src-web/lib/atoms/atomWithKVStorage.ts
Normal file
25
src-web/lib/atoms/atomWithKVStorage.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { atom } from 'jotai/index';
|
||||
import { getKeyValue, setKeyValue } from '../keyValueStore';
|
||||
|
||||
export function atomWithKVStorage<T extends object | boolean | number | string | null>(
|
||||
key: string,
|
||||
fallback: T,
|
||||
namespace = 'global',
|
||||
) {
|
||||
const baseAtom = atom<T>(fallback);
|
||||
|
||||
baseAtom.onMount = (setValue) => {
|
||||
setValue(getKeyValue<T>({ namespace, key, fallback }));
|
||||
};
|
||||
|
||||
const derivedAtom = atom<T, [T | ((prev: T) => T)], void>(
|
||||
(get) => get(baseAtom),
|
||||
(get, set, update) => {
|
||||
const nextValue = typeof update === 'function' ? update(get(baseAtom)) : update;
|
||||
set(baseAtom, nextValue);
|
||||
setKeyValue({ namespace, key, value: nextValue }).catch(console.error);
|
||||
},
|
||||
);
|
||||
|
||||
return derivedAtom;
|
||||
}
|
||||
29
src-web/lib/deleteModelWithConfirm.tsx
Normal file
29
src-web/lib/deleteModelWithConfirm.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { AnyModel } from '@yaakapp-internal/models';
|
||||
import { deleteModel, modelTypeLabel } from '@yaakapp-internal/models';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { showConfirmDelete } from './confirm';
|
||||
import { resolvedModelName } from './resolvedModelName';
|
||||
|
||||
export async function deleteModelWithConfirm(model: AnyModel | null): Promise<boolean> {
|
||||
if (model == null ) {
|
||||
console.warn('Tried to delete null model');
|
||||
return false;
|
||||
}
|
||||
|
||||
const confirmed = await showConfirmDelete({
|
||||
id: 'delete-model-' + model.model,
|
||||
title: 'Delete ' + modelTypeLabel(model),
|
||||
description: (
|
||||
<>
|
||||
Permanently delete <InlineCode>{resolvedModelName(model)}</InlineCode>?
|
||||
</>
|
||||
),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await deleteModel(model);
|
||||
return true;
|
||||
}
|
||||
23
src-web/lib/deleteRequestAndNavigate.tsx
Normal file
23
src-web/lib/deleteRequestAndNavigate.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models';
|
||||
import { duplicateModel } from '@yaakapp-internal/models';
|
||||
import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace';
|
||||
import { jotaiStore } from './jotai';
|
||||
import { router } from './router';
|
||||
|
||||
export async function duplicateRequestAndNavigate(
|
||||
model: HttpRequest | GrpcRequest | WebsocketRequest | null,
|
||||
) {
|
||||
if (model == null ){
|
||||
throw new Error('Cannot duplicate null request');
|
||||
}
|
||||
|
||||
const newId = await duplicateModel(model);
|
||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||
if (workspaceId == null) return;
|
||||
|
||||
await router.navigate({
|
||||
to: '/workspaces/$workspaceId',
|
||||
params: { workspaceId },
|
||||
search: (prev) => ({ ...prev, request_id: newId }),
|
||||
});
|
||||
}
|
||||
@@ -1,37 +1,42 @@
|
||||
import type { KeyValue } from '@yaakapp-internal/models';
|
||||
import { invokeCmd } from './tauri';
|
||||
import { createGlobalModel, keyValuesAtom, patchModel } from '@yaakapp-internal/models';
|
||||
import { jotaiStore } from './jotai';
|
||||
|
||||
export async function setKeyValue<T>({
|
||||
namespace = 'global',
|
||||
key,
|
||||
value,
|
||||
key: keyOrKeys,
|
||||
value: rawValue,
|
||||
}: {
|
||||
namespace?: string;
|
||||
key: string | string[];
|
||||
value: T;
|
||||
}): Promise<void> {
|
||||
await invokeCmd('cmd_set_key_value', {
|
||||
namespace,
|
||||
key: buildKeyValueKey(key),
|
||||
value: JSON.stringify(value),
|
||||
});
|
||||
const kv = getKeyValueRaw({ namespace, key: keyOrKeys });
|
||||
const key = buildKeyValueKey(keyOrKeys);
|
||||
const value = JSON.stringify(rawValue);
|
||||
|
||||
if (kv) {
|
||||
await patchModel(kv, { namespace, key, value });
|
||||
} else {
|
||||
await createGlobalModel({ model: 'key_value', namespace, key, value });
|
||||
}
|
||||
}
|
||||
|
||||
export async function getKeyValueRaw({
|
||||
export function getKeyValueRaw({
|
||||
namespace = 'global',
|
||||
key,
|
||||
key: keyOrKeys,
|
||||
}: {
|
||||
namespace?: string;
|
||||
key: string | string[];
|
||||
}) {
|
||||
const kv = (await invokeCmd('cmd_get_key_value', {
|
||||
namespace,
|
||||
key: buildKeyValueKey(key),
|
||||
})) as KeyValue | null;
|
||||
return kv;
|
||||
const key = buildKeyValueKey(keyOrKeys);
|
||||
const kv = jotaiStore
|
||||
.get(keyValuesAtom)
|
||||
.find((kv) => kv.namespace === namespace && kv?.key === key);
|
||||
return kv ?? null;
|
||||
}
|
||||
|
||||
export async function getKeyValue<T>({
|
||||
export function getKeyValue<T>({
|
||||
namespace = 'global',
|
||||
key,
|
||||
fallback,
|
||||
@@ -40,7 +45,7 @@ export async function getKeyValue<T>({
|
||||
key: string | string[];
|
||||
fallback: T;
|
||||
}) {
|
||||
const kv = await getKeyValueRaw({ namespace, key });
|
||||
const kv = getKeyValueRaw({ namespace, key });
|
||||
return extractKeyValueOrFallback(kv, fallback);
|
||||
}
|
||||
|
||||
|
||||
31
src-web/lib/renameModelWithPrompt.tsx
Normal file
31
src-web/lib/renameModelWithPrompt.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { AnyModel} from '@yaakapp-internal/models';
|
||||
import { patchModel } from '@yaakapp-internal/models';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { showPrompt } from './prompt';
|
||||
|
||||
export async function renameModelWithPrompt(model: Extract<AnyModel, { name: string }> | null) {
|
||||
if (model == null) {
|
||||
throw new Error('Tried to rename null model');
|
||||
}
|
||||
|
||||
const name = await showPrompt({
|
||||
id: 'rename-request',
|
||||
title: 'Rename Request',
|
||||
description:
|
||||
model.name === '' ? (
|
||||
'Enter a new name'
|
||||
) : (
|
||||
<>
|
||||
Enter a new name for <InlineCode>{model.name}</InlineCode>
|
||||
</>
|
||||
),
|
||||
label: 'Name',
|
||||
placeholder: 'New Name',
|
||||
defaultValue: model.name,
|
||||
confirmText: 'Save',
|
||||
});
|
||||
|
||||
if (name == null) return;
|
||||
|
||||
await patchModel(model, { name });
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { AnyModel } from '@yaakapp-internal/models';
|
||||
import { foldersAtom } from '../hooks/useFolders';
|
||||
import type { AnyModel} from '@yaakapp-internal/models';
|
||||
import { foldersAtom } from '@yaakapp-internal/models';
|
||||
import { jotaiStore } from './jotai';
|
||||
|
||||
export function resolvedModelName(r: AnyModel | null): string {
|
||||
|
||||
6
src-web/lib/settings.ts
Normal file
6
src-web/lib/settings.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import type { Settings } from '@yaakapp-internal/models';
|
||||
|
||||
export function getSettings(): Promise<Settings> {
|
||||
return invoke<Settings>('plugin:yaak-models|get_settings');
|
||||
}
|
||||
@@ -5,53 +5,24 @@ type TauriCmd =
|
||||
| 'cmd_call_http_authentication_action'
|
||||
| 'cmd_call_http_request_action'
|
||||
| 'cmd_check_for_updates'
|
||||
| 'cmd_create_cookie_jar'
|
||||
| 'cmd_create_environment'
|
||||
| 'cmd_create_grpc_request'
|
||||
| 'cmd_curl_to_request'
|
||||
| 'cmd_delete_all_grpc_connections'
|
||||
| 'cmd_delete_all_http_responses'
|
||||
| 'cmd_delete_cookie_jar'
|
||||
| 'cmd_delete_environment'
|
||||
| 'cmd_delete_folder'
|
||||
| 'cmd_delete_grpc_connection'
|
||||
| 'cmd_delete_grpc_request'
|
||||
| 'cmd_delete_http_request'
|
||||
| 'cmd_delete_http_response'
|
||||
| 'cmd_delete_send_history'
|
||||
| 'cmd_delete_workspace'
|
||||
| 'cmd_dismiss_notification'
|
||||
| 'cmd_duplicate_folder'
|
||||
| 'cmd_duplicate_grpc_request'
|
||||
| 'cmd_duplicate_http_request'
|
||||
| 'cmd_export_data'
|
||||
| 'cmd_filter_response'
|
||||
| 'cmd_format_json'
|
||||
| 'cmd_get_environment'
|
||||
| 'cmd_get_folder'
|
||||
| 'cmd_get_http_authentication_config'
|
||||
| 'cmd_get_http_authentication_summaries'
|
||||
| 'cmd_get_key_value'
|
||||
| 'cmd_get_settings'
|
||||
| 'cmd_get_sse_events'
|
||||
| 'cmd_get_workspace'
|
||||
| 'cmd_get_workspace_meta'
|
||||
| 'cmd_grpc_go'
|
||||
| 'cmd_grpc_reflect'
|
||||
| 'cmd_http_request_actions'
|
||||
| 'cmd_import_data'
|
||||
| 'cmd_install_plugin'
|
||||
| 'cmd_list_cookie_jars'
|
||||
| 'cmd_list_environments'
|
||||
| 'cmd_list_folders'
|
||||
| 'cmd_list_grpc_connections'
|
||||
| 'cmd_list_grpc_events'
|
||||
| 'cmd_list_grpc_requests'
|
||||
| 'cmd_list_http_requests'
|
||||
| 'cmd_list_http_responses'
|
||||
| 'cmd_list_key_values'
|
||||
| 'cmd_list_plugins'
|
||||
| 'cmd_list_workspaces'
|
||||
| 'cmd_metadata'
|
||||
| 'cmd_new_child_window'
|
||||
| 'cmd_new_main_window'
|
||||
@@ -62,19 +33,9 @@ type TauriCmd =
|
||||
| 'cmd_save_response'
|
||||
| 'cmd_send_ephemeral_request'
|
||||
| 'cmd_send_http_request'
|
||||
| 'cmd_set_key_value'
|
||||
| 'cmd_set_update_mode'
|
||||
| 'cmd_template_functions'
|
||||
| 'cmd_template_tokens_to_string'
|
||||
| 'cmd_uninstall_plugin'
|
||||
| 'cmd_update_cookie_jar'
|
||||
| 'cmd_update_environment'
|
||||
| 'cmd_update_folder'
|
||||
| 'cmd_update_grpc_request'
|
||||
| 'cmd_upsert_http_request'
|
||||
| 'cmd_update_settings'
|
||||
| 'cmd_update_workspace'
|
||||
| 'cmd_update_workspace_meta';
|
||||
| 'cmd_uninstall_plugin';
|
||||
|
||||
export async function invokeCmd<T>(cmd: TauriCmd, args?: InvokeArgs): Promise<T> {
|
||||
// console.log('RUN COMMAND', cmd, args);
|
||||
|
||||
Reference in New Issue
Block a user