Focus request/folder after creation

This commit is contained in:
Gregory Schier
2025-11-11 14:11:43 -08:00
parent 8164a61376
commit 7b6278405c
19 changed files with 138 additions and 86 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ use crate::{
}; };
use chrono::Utc; use chrono::Utc;
use cookie::Cookie; use cookie::Cookie;
use log::{debug, error}; use log::error;
use tauri::{AppHandle, Emitter, Manager, Runtime}; use tauri::{AppHandle, Emitter, Manager, Runtime};
use tauri_plugin_clipboard_manager::ClipboardExt; use tauri_plugin_clipboard_manager::ClipboardExt;
use yaak_common::window::WorkspaceWindowTrait; use yaak_common::window::WorkspaceWindowTrait;
+1 -1
View File
@@ -46,7 +46,7 @@ export type HttpUrlParameter = { enabled?: boolean, name: string, value: string,
export type KeyValue = { model: "key_value", id: string, createdAt: string, updatedAt: string, key: string, namespace: string, value: string, }; export type KeyValue = { model: "key_value", id: string, createdAt: string, updatedAt: string, key: string, namespace: string, value: string, };
export type ModelChangeEvent = { "type": "upsert" } | { "type": "delete" }; export type ModelChangeEvent = { "type": "upsert", created: boolean, } | { "type": "delete" };
export type ModelPayload = { model: AnyModel, updateSource: UpdateSource, change: ModelChangeEvent, }; export type ModelPayload = { model: AnyModel, updateSource: UpdateSource, change: ModelChangeEvent, };
+14 -22
View File
@@ -12,31 +12,23 @@ export function initModelStore(store: JotaiStore) {
_store = store; _store = store;
getCurrentWebviewWindow() getCurrentWebviewWindow()
.listen<ModelPayload>('upserted_model', ({ payload }) => { .listen<ModelPayload>('model_write', ({ payload }) => {
if (shouldIgnoreModel(payload)) return; if (shouldIgnoreModel(payload)) return;
mustStore().set(modelStoreDataAtom, (prev: ModelStoreData) => { mustStore().set(modelStoreDataAtom, (prev: ModelStoreData) => {
return { if (payload.change.type === 'upsert') {
...prev, return {
[payload.model.model]: { ...prev,
...prev[payload.model.model], [payload.model.model]: {
[payload.model.id]: payload.model, ...prev[payload.model.model],
}, [payload.model.id]: payload.model,
}; },
}); };
}) } else {
.catch(console.error); const modelData = { ...prev[payload.model.model] };
delete modelData[payload.model.id];
getCurrentWebviewWindow() return { ...prev, [payload.model.model]: modelData };
.listen<ModelPayload>('deleted_model', ({ payload }) => { }
if (shouldIgnoreModel(payload)) return;
console.log('Delete model', payload);
mustStore().set(modelStoreDataAtom, (prev: ModelStoreData) => {
const modelData = { ...prev[payload.model.model] };
delete modelData[payload.model.id];
return { ...prev, [payload.model.model]: modelData };
}); });
}) })
.catch(console.error); .catch(console.error);
+17 -6
View File
@@ -3,11 +3,11 @@ use crate::error::Error::ModelNotFound;
use crate::error::Result; use crate::error::Result;
use crate::models::{AnyModel, UpsertModelInfo}; use crate::models::{AnyModel, UpsertModelInfo};
use crate::util::{ModelChangeEvent, ModelPayload, UpdateSource}; use crate::util::{ModelChangeEvent, ModelPayload, UpdateSource};
use log::error; use log::{error, warn};
use rusqlite::OptionalExtension; use rusqlite::OptionalExtension;
use sea_query::{ use sea_query::{
Asterisk, Expr, IntoColumnRef, IntoIden, IntoTableRef, OnConflict, Query, SimpleExpr, Alias, Asterisk, Expr, Func, IntoColumnRef, IntoIden, IntoTableRef, OnConflict, Query,
SqliteQueryBuilder, ReturningClause, SimpleExpr, SqliteQueryBuilder,
}; };
use sea_query_rusqlite::RusqliteBinder; use sea_query_rusqlite::RusqliteBinder;
use std::fmt::Debug; use std::fmt::Debug;
@@ -152,21 +152,32 @@ impl<'a> DbContext<'a> {
} }
let on_conflict = OnConflict::column(id_iden).update_columns(update_columns).to_owned(); let on_conflict = OnConflict::column(id_iden).update_columns(update_columns).to_owned();
let (sql, params) = Query::insert() let (sql, params) = Query::insert()
.into_table(table) .into_table(table)
.columns(column_vec) .columns(column_vec)
.values_panic(value_vec) .values_panic(value_vec)
.on_conflict(on_conflict) .on_conflict(on_conflict)
.returning_all() .returning(Query::returning().exprs(vec![
Expr::col(Asterisk),
Expr::expr(Func::cust("last_insert_rowid")),
Expr::col("rowid"),
]))
.build_rusqlite(SqliteQueryBuilder); .build_rusqlite(SqliteQueryBuilder);
let mut stmt = self.conn.resolve().prepare(sql.as_str())?; let mut stmt = self.conn.resolve().prepare(sql.as_str())?;
let m: M = stmt.query_row(&*params.as_params(), |row| M::from_row(row))?; let (m, created): (M, bool) = stmt.query_row(&*params.as_params(), |row| {
M::from_row(row).and_then(|m| {
let rowid: i64 = row.get("rowid")?;
let last_rowid: i64 = row.get("last_insert_rowid()")?;
Ok((m, rowid == last_rowid))
})
})?;
let payload = ModelPayload { let payload = ModelPayload {
model: m.clone().into(), model: m.clone().into(),
update_source: source.clone(), update_source: source.clone(),
change: ModelChangeEvent::Upsert, change: ModelChangeEvent::Upsert { created },
}; };
if let Err(e) = self.events_tx.send(payload.clone()) { if let Err(e) = self.events_tx.send(payload.clone()) {
+1 -5
View File
@@ -77,11 +77,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
let app_handle = app_handle.clone(); let app_handle = app_handle.clone();
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
for p in rx { for p in rx {
let name = match p.change { app_handle.emit("model_write", p).unwrap();
ModelChangeEvent::Upsert => "upserted_model",
ModelChangeEvent::Delete => "deleted_model",
};
app_handle.emit(name, p).unwrap();
} }
}); });
} }
+2 -2
View File
@@ -3,7 +3,6 @@ use crate::models::{
AnyModel, Environment, Folder, GrpcRequest, HttpRequest, UpsertModelInfo, WebsocketRequest, AnyModel, Environment, Folder, GrpcRequest, HttpRequest, UpsertModelInfo, WebsocketRequest,
Workspace, WorkspaceIden, Workspace, WorkspaceIden,
}; };
use yaak_common::window::WorkspaceWindowTrait;
use crate::query_manager::QueryManagerExt; use crate::query_manager::QueryManagerExt;
use chrono::{NaiveDateTime, Utc}; use chrono::{NaiveDateTime, Utc};
use log::warn; use log::warn;
@@ -12,6 +11,7 @@ use serde::{Deserialize, Serialize};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use tauri::{AppHandle, Listener, Runtime, WebviewWindow}; use tauri::{AppHandle, Listener, Runtime, WebviewWindow};
use ts_rs::TS; use ts_rs::TS;
use yaak_common::window::WorkspaceWindowTrait;
pub fn generate_prefixed_id(prefix: &str) -> String { pub fn generate_prefixed_id(prefix: &str) -> String {
format!("{prefix}_{}", generate_id()) format!("{prefix}_{}", generate_id())
@@ -45,7 +45,7 @@ pub struct ModelPayload {
#[serde(rename_all = "snake_case", tag = "type")] #[serde(rename_all = "snake_case", tag = "type")]
#[ts(export, export_to = "gen_models.ts")] #[ts(export, export_to = "gen_models.ts")]
pub enum ModelChangeEvent { pub enum ModelChangeEvent {
Upsert, Upsert { created: bool },
Delete, Delete,
} }
+4 -3
View File
@@ -13,7 +13,7 @@ import { showPrompt } from '../lib/prompt';
import { resolvedModelNameWithFolders } from '../lib/resolvedModelName'; import { resolvedModelNameWithFolders } from '../lib/resolvedModelName';
export const createFolder = createFastMutation< export const createFolder = createFastMutation<
void, string | null,
void, void,
Partial<Pick<Folder, 'name' | 'sortPriority' | 'folderId'>> Partial<Pick<Folder, 'name' | 'sortPriority' | 'folderId'>>
>({ >({
@@ -34,13 +34,14 @@ export const createFolder = createFastMutation<
confirmText: 'Create', confirmText: 'Create',
placeholder: 'Name', placeholder: 'Name',
}); });
if (name == null) return; if (name == null) return null;
patch.name = name; patch.name = name;
} }
patch.sortPriority = patch.sortPriority || -Date.now(); patch.sortPriority = patch.sortPriority || -Date.now();
await createWorkspaceModel({ model: 'folder', workspaceId, ...patch }); const id = await createWorkspaceModel({ model: 'folder', workspaceId, ...patch });
return id;
}, },
}); });
+19
View File
@@ -2,9 +2,11 @@ import type { Extension } from '@codemirror/state';
import { Compartment } from '@codemirror/state'; import { Compartment } from '@codemirror/state';
import { debounce } from '@yaakapp-internal/lib'; import { debounce } from '@yaakapp-internal/lib';
import type { import type {
AnyModel,
Folder, Folder,
GrpcRequest, GrpcRequest,
HttpRequest, HttpRequest,
ModelPayload,
WebsocketRequest, WebsocketRequest,
Workspace, Workspace,
} from '@yaakapp-internal/models'; } from '@yaakapp-internal/models';
@@ -34,6 +36,7 @@ import { getCreateDropdownItems } from '../hooks/useCreateDropdownItems';
import { getGrpcRequestActions } from '../hooks/useGrpcRequestActions'; import { getGrpcRequestActions } from '../hooks/useGrpcRequestActions';
import { useHotKey } from '../hooks/useHotKey'; import { useHotKey } from '../hooks/useHotKey';
import { getHttpRequestActions } from '../hooks/useHttpRequestActions'; import { getHttpRequestActions } from '../hooks/useHttpRequestActions';
import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent';
import { sendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest'; import { sendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest';
import { useSidebarHidden } from '../hooks/useSidebarHidden'; import { useSidebarHidden } from '../hooks/useSidebarHidden';
import { deepEqualAtom } from '../lib/atoms'; import { deepEqualAtom } from '../lib/atoms';
@@ -64,6 +67,15 @@ import type { TreeItemProps } from './core/tree/TreeItem';
import { GitDropdown } from './GitDropdown'; import { GitDropdown } from './GitDropdown';
type SidebarModel = Workspace | Folder | HttpRequest | GrpcRequest | WebsocketRequest; type SidebarModel = Workspace | Folder | HttpRequest | GrpcRequest | WebsocketRequest;
function isSidebarLeafModel(m: AnyModel): boolean {
const modelMap: Record<Exclude<SidebarModel['model'], 'workspace'>, null> = {
http_request: null,
grpc_request: null,
websocket_request: null,
folder: null,
};
return m.model in modelMap;
}
const OPACITY_SUBTLE = 'opacity-80'; const OPACITY_SUBTLE = 'opacity-80';
@@ -91,6 +103,13 @@ function Sidebar({ className }: { className?: string }) {
if (!didFocus) filterRef.current?.focus(); if (!didFocus) filterRef.current?.focus();
}, []); }, []);
// Focus any new sidebar models when created
useListenToTauriEvent<ModelPayload>('model_write', ({ payload }) => {
if (!isSidebarLeafModel(payload.model)) return;
if (!(payload.change.type === 'upsert' && payload.change.created)) return;
treeRef.current?.selectItem(payload.model.id, true);
});
useHotKey( useHotKey(
'sidebar.filter', 'sidebar.filter',
() => { () => {
+16 -18
View File
@@ -11,16 +11,7 @@ import {
import { type } from '@tauri-apps/plugin-os'; import { type } from '@tauri-apps/plugin-os';
import classNames from 'classnames'; import classNames from 'classnames';
import type { ComponentType, MouseEvent, ReactElement, Ref, RefAttributes } from 'react'; import type { ComponentType, MouseEvent, ReactElement, Ref, RefAttributes } from 'react';
import { import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
forwardRef,
memo,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
useRef,
useState,
} from 'react';
import { useKey, useKeyPressEvent } from 'react-use'; import { useKey, useKeyPressEvent } from 'react-use';
import type { HotkeyAction, HotKeyOptions } from '../../../hooks/useHotKey'; import type { HotkeyAction, HotKeyOptions } from '../../../hooks/useHotKey';
import { useHotKey } from '../../../hooks/useHotKey'; import { useHotKey } from '../../../hooks/useHotKey';
@@ -72,7 +63,7 @@ export interface TreeHandle {
treeId: string; treeId: string;
focus: () => boolean; focus: () => boolean;
hasFocus: () => boolean; hasFocus: () => boolean;
selectItem: (id: string) => void; selectItem: (id: string, focus?: boolean) => void;
renameItem: (id: string) => void; renameItem: (id: string) => void;
showContextMenu: () => void; showContextMenu: () => void;
} }
@@ -181,11 +172,16 @@ function TreeInner<T extends { id: string }>(
requestAnimationFrame(ensureTabbableItem); requestAnimationFrame(ensureTabbableItem);
}); });
const hasFocus = useCallback(() => {
return treeRef.current?.contains(document.activeElement) ?? false;
}, []);
const setSelected = useCallback( const setSelected = useCallback(
function setSelected(ids: string[], focus: boolean) { (ids: string[], focus: boolean) => {
jotaiStore.set(selectedIdsFamily(treeId), ids); jotaiStore.set(selectedIdsFamily(treeId), ids);
// TODO: Figure out a better way than timeout // TODO: Figure out a better way than timeout
if (focus) setTimeout(tryFocus, 50); if (!focus) return;
setTimeout(tryFocus, 50);
}, },
[treeId, tryFocus], [treeId, tryFocus],
); );
@@ -194,15 +190,15 @@ function TreeInner<T extends { id: string }>(
() => ({ () => ({
treeId, treeId,
focus: tryFocus, focus: tryFocus,
hasFocus: () => treeRef.current?.contains(document.activeElement) ?? false, hasFocus: hasFocus,
renameItem: (id) => treeItemRefs.current[id]?.rename(), renameItem: (id) => treeItemRefs.current[id]?.rename(),
selectItem: (id) => { selectItem: (id, focus) => {
if (jotaiStore.get(selectedIdsFamily(treeId)).includes(id)) { if (jotaiStore.get(selectedIdsFamily(treeId)).includes(id)) {
// Already selected // Already selected
return; return;
} }
setSelected([id], false);
jotaiStore.set(focusIdsFamily(treeId), { anchorId: id, lastId: id }); jotaiStore.set(focusIdsFamily(treeId), { anchorId: id, lastId: id });
setSelected([id], focus === true);
}, },
showContextMenu: async () => { showContextMenu: async () => {
if (getContextMenu == null) return; if (getContextMenu == null) return;
@@ -214,7 +210,7 @@ function TreeInner<T extends { id: string }>(
setShowContextMenu({ items: menuItems, x: rect.x, y: rect.y }); setShowContextMenu({ items: menuItems, x: rect.x, y: rect.y });
}, },
}), }),
[getContextMenu, selectableItems, setSelected, treeId, tryFocus], [getContextMenu, hasFocus, selectableItems, setSelected, treeId, tryFocus],
); );
useImperativeHandle(ref, (): TreeHandle => treeHandle, [treeHandle]); useImperativeHandle(ref, (): TreeHandle => treeHandle, [treeHandle]);
@@ -248,7 +244,9 @@ function TreeInner<T extends { id: string }>(
if (shiftKey) { if (shiftKey) {
const validSelectableItems = getValidSelectableItems(treeId, selectableItems); const validSelectableItems = getValidSelectableItems(treeId, selectableItems);
const anchorIndex = validSelectableItems.findIndex((i) => i.node.item.id === anchorSelectedId); const anchorIndex = validSelectableItems.findIndex(
(i) => i.node.item.id === anchorSelectedId,
);
const currIndex = validSelectableItems.findIndex((v) => v.node.item.id === item.id); const currIndex = validSelectableItems.findIndex((v) => v.node.item.id === item.id);
// Nothing was selected yet, so just select this item // Nothing was selected yet, so just select this item
+2 -1
View File
@@ -7,7 +7,8 @@ function setFontSizeOnDocument(fontSize: number) {
document.documentElement.style.fontSize = `${fontSize}px`; document.documentElement.style.fontSize = `${fontSize}px`;
} }
listen<ModelPayload>('upserted_model', async (event) => { listen<ModelPayload>('model_write', async (event) => {
if (event.payload.change.type !== 'upsert') return;
if (event.payload.model.model !== 'settings') return; if (event.payload.model.model !== 'settings') return;
setFontSizeOnDocument(event.payload.model.interfaceFontSize); setFontSizeOnDocument(event.payload.model.interfaceFontSize);
}).catch(console.error); }).catch(console.error);
+2 -1
View File
@@ -11,7 +11,8 @@ function setFonts(settings: Settings) {
); );
} }
listen<ModelPayload>('upserted_model', async (event) => { listen<ModelPayload>('model_write', async (event) => {
if (event.payload.change.type !== 'upsert') return;
if (event.payload.model.model !== 'settings') return; if (event.payload.model.model !== 'settings') return;
setFonts(event.payload.model); setFonts(event.payload.model);
}).catch(console.error); }).catch(console.error);
+36 -9
View File
@@ -36,46 +36,68 @@ export function getCreateDropdownItems({
folderId: folderIdOption, folderId: folderIdOption,
workspaceId, workspaceId,
activeRequest, activeRequest,
onCreate,
}: { }: {
hideFolder?: boolean; hideFolder?: boolean;
hideIcons?: boolean; hideIcons?: boolean;
folderId?: string | null | 'active-folder'; folderId?: string | null | 'active-folder';
workspaceId: string | null; workspaceId: string | null;
activeRequest: HttpRequest | GrpcRequest | WebsocketRequest | null; activeRequest: HttpRequest | GrpcRequest | WebsocketRequest | null;
onCreate?: (
model: 'http_request' | 'grpc_request' | 'websocket_request' | 'folder',
id: string,
) => void;
}): DropdownItem[] { }): DropdownItem[] {
const folderId = const folderId =
(folderIdOption === 'active-folder' ? activeRequest?.folderId : folderIdOption) ?? null; (folderIdOption === 'active-folder' ? activeRequest?.folderId : folderIdOption) ?? null;
if (workspaceId == null) return [];
if (workspaceId == null) {
return [];
}
return [ return [
{ {
label: 'HTTP', label: 'HTTP',
leftSlot: hideIcons ? undefined : <Icon icon="plus" />, leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: () => createRequestAndNavigate({ model: 'http_request', workspaceId, folderId }), onSelect: async () => {
const id = await createRequestAndNavigate({ model: 'http_request', workspaceId, folderId });
onCreate?.('http_request', id);
},
}, },
{ {
label: 'GraphQL', label: 'GraphQL',
leftSlot: hideIcons ? undefined : <Icon icon="plus" />, leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: () => onSelect: async () => {
createRequestAndNavigate({ const id = await createRequestAndNavigate({
model: 'http_request', model: 'http_request',
workspaceId, workspaceId,
folderId, folderId,
bodyType: BODY_TYPE_GRAPHQL, bodyType: BODY_TYPE_GRAPHQL,
method: 'POST', method: 'POST',
headers: [{ name: 'Content-Type', value: 'application/json', id: generateId() }], headers: [{ name: 'Content-Type', value: 'application/json', id: generateId() }],
}), });
onCreate?.('http_request', id);
},
}, },
{ {
label: 'gRPC', label: 'gRPC',
leftSlot: hideIcons ? undefined : <Icon icon="plus" />, leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: () => createRequestAndNavigate({ model: 'grpc_request', workspaceId, folderId }), onSelect: async () => {
const id = await createRequestAndNavigate({ model: 'grpc_request', workspaceId, folderId });
onCreate?.('grpc_request', id);
},
}, },
{ {
label: 'WebSocket', label: 'WebSocket',
leftSlot: hideIcons ? undefined : <Icon icon="plus" />, leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: () => onSelect: async () => {
createRequestAndNavigate({ model: 'websocket_request', workspaceId, folderId }), const id = await createRequestAndNavigate({
model: 'websocket_request',
workspaceId,
folderId,
});
onCreate?.('websocket_request', id);
},
}, },
...((hideFolder ...((hideFolder
? [] ? []
@@ -84,7 +106,12 @@ export function getCreateDropdownItems({
{ {
label: 'Folder', label: 'Folder',
leftSlot: hideIcons ? undefined : <Icon icon="plus" />, leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
onSelect: () => createFolder.mutate({ folderId }), onSelect: async () => {
const id = await createFolder.mutateAsync({ folderId });
if (id != null) {
onCreate?.('folder', id);
}
},
}, },
]) as DropdownItem[]), ]) as DropdownItem[]),
]; ];
+11 -7
View File
@@ -1,17 +1,21 @@
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 { useEffect } from 'react'; import { useEffect, useRef } from 'react';
/**
* React hook to listen to a Tauri event.
*/
export function useListenToTauriEvent<T>(event: EventName, fn: EventCallback<T>) { export function useListenToTauriEvent<T>(event: EventName, fn: EventCallback<T>) {
useEffect(() => listenToTauriEvent(event, fn), [event, fn]); const handlerRef = useRef(fn);
useEffect(() => {
handlerRef.current = fn;
}, [fn]);
useEffect(() => {
return listenToTauriEvent<T>(event, (p) => handlerRef.current(p));
}, [event]);
} }
export function listenToTauriEvent<T>(event: EventName, fn: EventCallback<T>) { export function listenToTauriEvent<T>(event: EventName, fn: EventCallback<T>) {
const unlisten = listen<T>( const unsubPromise = listen<T>(
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
@@ -19,6 +23,6 @@ export function listenToTauriEvent<T>(event: EventName, fn: EventCallback<T>) {
); );
return () => { return () => {
unlisten.then((fn) => fn()); unsubPromise.then((unsub) => unsub()).catch(console.error);
}; };
} }
+3 -1
View File
@@ -7,7 +7,9 @@ import { jotaiStore } from '../lib/jotai';
const requestUpdateKeyAtom = atom<Record<string, string>>({}); const requestUpdateKeyAtom = atom<Record<string, string>>({});
getCurrentWebviewWindow() getCurrentWebviewWindow()
.listen<ModelPayload>('upserted_model', ({ payload }) => { .listen<ModelPayload>('model_write', ({ payload }) => {
if (payload.change.type !== 'upsert') return;
if ( if (
(payload.model.model === 'http_request' || (payload.model.model === 'http_request' ||
payload.model.model === 'grpc_request' || payload.model.model === 'grpc_request' ||
+1 -4
View File
@@ -34,10 +34,7 @@ const debouncedSync = debounce(async () => {
* simply add long-lived subscribers for the lifetime of the app. * simply add long-lived subscribers for the lifetime of the app.
*/ */
function initModelListeners() { function initModelListeners() {
listenToTauriEvent<ModelPayload>('upserted_model', (p) => { listenToTauriEvent<ModelPayload>('model_write', (p) => {
if (isModelRelevant(p.payload.model)) debouncedSync();
});
listenToTauriEvent<ModelPayload>('deleted_model', (p) => {
if (isModelRelevant(p.payload.model)) debouncedSync(); if (isModelRelevant(p.payload.model)) debouncedSync();
}); });
} }
+3 -2
View File
@@ -11,8 +11,8 @@ export async function createRequestAndNavigate<
if (patch.sortPriority === undefined) { if (patch.sortPriority === undefined) {
if (activeRequest != null) { if (activeRequest != null) {
// Place above currently active request // Place below the currently active request
patch.sortPriority = activeRequest.sortPriority - 0.0001; patch.sortPriority = activeRequest.sortPriority;
} else { } else {
// Place at the very top // Place at the very top
patch.sortPriority = -Date.now(); patch.sortPriority = -Date.now();
@@ -27,4 +27,5 @@ export async function createRequestAndNavigate<
params: { workspaceId: patch.workspaceId }, params: { workspaceId: patch.workspaceId },
search: (prev) => ({ ...prev, request_id: newId }), search: (prev) => ({ ...prev, request_id: newId }),
}); });
return newId;
} }
@@ -13,7 +13,7 @@ export async function duplicateRequestOrFolderAndNavigate(
const newId = await duplicateModel(model); const newId = await duplicateModel(model);
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom); const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
if (workspaceId == null) return; if (workspaceId == null || model.model === 'folder') return;
navigateToRequestOrFolderOrWorkspace(newId, model.model); navigateToRequestOrFolderOrWorkspace(newId, model.model);
} }
+1 -1
View File
@@ -19,7 +19,7 @@ export function setWorkspaceSearchParams(
(router as any).navigate({ (router as any).navigate({
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
search: (prev: any) => { search: (prev: any) => {
console.log('Navigating to', { prev, search }); // console.log('Navigating to', { prev, search });
return { ...prev, ...search }; return { ...prev, ...search };
}, },
}); });
+3 -1
View File
@@ -26,7 +26,9 @@ configureTheme().then(
); );
// Listen for settings changes, the re-compute theme // Listen for settings changes, the re-compute theme
listen<ModelPayload>('upserted_model', async (event) => { listen<ModelPayload>('model_write', async (event) => {
if (event.payload.change.type !== 'upsert') return;
const model = event.payload.model.model; const model = event.payload.model.model;
if (model !== 'settings' && model !== 'plugin') return; if (model !== 'settings' && model !== 'plugin') return;
await configureTheme(); await configureTheme();