diff --git a/src-tauri/src/notifications.rs b/src-tauri/src/notifications.rs
index 95b0c94e..4991f5c0 100644
--- a/src-tauri/src/notifications.rs
+++ b/src-tauri/src/notifications.rs
@@ -86,7 +86,7 @@ impl YaakNotifier {
#[cfg(feature = "license")]
let license_check = {
- use yaak_license::{check_license, LicenseCheckStatus};
+ use yaak_license::{LicenseCheckStatus, check_license};
match check_license(window).await {
Ok(LicenseCheckStatus::PersonalUse { .. }) => "personal".to_string(),
Ok(LicenseCheckStatus::CommercialUse) => "commercial".to_string(),
diff --git a/src-web/components/EnvironmentEditDialog.tsx b/src-web/components/EnvironmentEditDialog.tsx
index 6ec6c3be..2c1af24f 100644
--- a/src-web/components/EnvironmentEditDialog.tsx
+++ b/src-web/components/EnvironmentEditDialog.tsx
@@ -10,7 +10,7 @@ import {
} from '../hooks/useEnvironmentsBreakdown';
import { deleteModelWithConfirm } from '../lib/deleteModelWithConfirm';
import { jotaiStore } from '../lib/jotai';
-import { isBaseEnvironment } from '../lib/model_util';
+import { isBaseEnvironment, isSubEnvironment } from '../lib/model_util';
import { resolvedModelName } from '../lib/resolvedModelName';
import { showColorPicker } from '../lib/showColorPicker';
import { Banner } from './core/Banner';
@@ -170,7 +170,18 @@ function EnvironmentEditDialogSidebar({
const getContextMenu = useCallback(
(items: TreeModel[]): ContextMenuProps['items'] => {
const environment = items[0];
- if (environment == null || environment.model !== 'environment') return [];
+ const addEnvironmentItem: DropdownItem = {
+ label: 'Create Sub Environment',
+ leftSlot: ,
+ onSelect: async () => {
+ await createSubEnvironment();
+ },
+ };
+
+ if (environment == null || environment.model !== 'environment') {
+ return [addEnvironmentItem];
+ }
+
const singleEnvironment = items.length === 1;
const menuItems: DropdownItem[] = [
@@ -206,6 +217,7 @@ function EnvironmentEditDialogSidebar({
label: `Make ${environment.public ? 'Private' : 'Sharable'}`,
leftSlot: ,
rightSlot: ,
+ hidden: items.length > 1,
onSelect: async () => {
await patchModel(environment, { public: !environment.public });
},
@@ -215,22 +227,18 @@ function EnvironmentEditDialogSidebar({
label: 'Delete',
hotKeyAction: 'sidebar.selected.delete',
hotKeyLabelOnly: true,
- hidden: !(isBaseEnvironment(environment) && baseEnvironments.length > 1),
+ hidden:
+ (isBaseEnvironment(environment) && baseEnvironments.length <= 1) ||
+ !isSubEnvironment(environment),
leftSlot: ,
onSelect: () => handleDeleteEnvironment(environment),
},
];
+ // Add sub environment to base environment
if (isBaseEnvironment(environment) && singleEnvironment) {
menuItems.push({ type: 'separator' });
- menuItems.push({
- label: 'Create Sub Environment',
- leftSlot: ,
- hidden: !isBaseEnvironment(environment),
- onSelect: async () => {
- await createSubEnvironment();
- },
- });
+ menuItems.push(addEnvironmentItem);
}
return menuItems;
diff --git a/src-web/components/EnvironmentEditor.tsx b/src-web/components/EnvironmentEditor.tsx
index c9799f1b..3b8c2069 100644
--- a/src-web/components/EnvironmentEditor.tsx
+++ b/src-web/components/EnvironmentEditor.tsx
@@ -2,7 +2,7 @@ import type { Environment } from '@yaakapp-internal/models';
import { patchModel } from '@yaakapp-internal/models';
import type { GenericCompletionOption } from '@yaakapp-internal/plugins';
import classNames from 'classnames';
-import React, { useCallback, useMemo } from 'react';
+import { useCallback, useMemo } from 'react';
import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown';
import { useIsEncryptionEnabled } from '../hooks/useIsEncryptionEnabled';
import { useKeyValue } from '../hooks/useKeyValue';
diff --git a/src-web/components/Settings/SettingsGeneral.tsx b/src-web/components/Settings/SettingsGeneral.tsx
index 8f1ebfa3..3c904a60 100644
--- a/src-web/components/Settings/SettingsGeneral.tsx
+++ b/src-web/components/Settings/SettingsGeneral.tsx
@@ -1,7 +1,6 @@
import { revealItemInDir } from '@tauri-apps/plugin-opener';
import { patchModel, settingsAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
-import React from 'react';
import { activeWorkspaceAtom } from '../../hooks/useActiveWorkspace';
import { appInfo } from '../../lib/appInfo';
import { useCheckForUpdates } from '../../hooks/useCheckForUpdates';
diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx
index 0633f617..03ef6b5c 100644
--- a/src-web/components/Sidebar.tsx
+++ b/src-web/components/Sidebar.tsx
@@ -21,8 +21,7 @@ import {
import classNames from 'classnames';
import { atom, useAtomValue } from 'jotai';
import { selectAtom } from 'jotai/utils';
-import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react';
-import { useKey } from 'react-use';
+import { memo, useCallback, useEffect, useMemo, useRef } from 'react';
import { moveToWorkspace } from '../commands/moveToWorkspace';
import { openFolderSettings } from '../commands/openFolderSettings';
import { activeCookieJarAtom } from '../hooks/useActiveCookieJar';
@@ -150,33 +149,31 @@ function Sidebar({ className }: { className?: string }) {
await Promise.all(
items.map((m, i) =>
// Spread item sortPriority out over before/after range
- patchModel(m, { sortPriority: beforePriority + (i + 1) * increment, folderId }),
+ patchModel(m, {
+ sortPriority: beforePriority + (i + 1) * increment,
+ folderId,
+ }),
),
- );
- }
+ );
+ op>
+ }
} catch (e) {
console.error(e);
}
}, []);
- const handleTreeRefInit = useCallback((n: TreeHandle) => {
- treeRef.current = n;
- if (n == null) return;
- const activeId = jotaiStore.get(activeIdAtom);
- if (activeId == null) return;
- const selectedIds = jotaiStore.get(selectedIdsFamily(treeId));
- if (selectedIds.length > 0) return;
- n.selectItem(activeId);
- }, [treeId]);
-
- // Ensure active id is always selected when it changes
- useEffect(() => {
- return jotaiStore.sub(activeIdAtom, () => {
+ const handleTreeRefInit = useCallback(
+ (n: TreeHandle) => {
+ treeRef.current = n;
+ if (n == null) return;
const activeId = jotaiStore.get(activeIdAtom);
if (activeId == null) return;
- treeRef.current?.selectItem(activeId);
- });
- }, []);
+ const selectedIds = jotaiStore.get(selectedIdsFamily(treeId));
+ if (selectedIds.length > 0) return;
+ n.selectItem(activeId);
+ },
+ [treeId],
+ );
const clearFilterText = useCallback(() => {
jotaiStore.set(sidebarFilterAtom, { text: '', key: `${Math.random()}` });
@@ -204,14 +201,6 @@ function Sidebar({ className }: { className?: string }) {
[],
);
- // Focus the first sidebar item on arrow down from filter
- useKey('ArrowDown', (e) => {
- if (e.key === 'ArrowDown' && filterRef.current?.isFocused()) {
- e.preventDefault();
- treeRef.current?.focus();
- }
- });
-
const actions = useMemo(() => {
const enable = () => treeRef.current?.hasFocus() ?? false;
@@ -291,7 +280,11 @@ function Sidebar({ className }: { className?: string }) {
// No children means we're in the root
if (child == null) {
- return getCreateDropdownItems({ workspaceId, activeRequest: null, folderId: null });
+ return getCreateDropdownItems({
+ workspaceId,
+ activeRequest: null,
+ folderId: null,
+ });
}
const workspaces = jotaiStore.get(workspacesAtom);
@@ -355,12 +348,19 @@ function Sidebar({ className }: { className?: string }) {
items.length === 1 && child.model === 'folder'
? [
{ type: 'separator' },
- ...getCreateDropdownItems({ workspaceId, activeRequest: null, folderId: child.id }),
+ ...getCreateDropdownItems({
+ workspaceId,
+ activeRequest: null,
+ folderId: child.id,
+ }),
]
: [];
const menuItems: ContextMenuProps['items'] = [
...initialItems,
- { type: 'separator', hidden: initialItems.filter((v) => !v.hidden).length === 0 },
+ {
+ type: 'separator',
+ hidden: initialItems.filter((v) => !v.hidden).length === 0,
+ },
{
label: 'Rename',
leftSlot: ,
@@ -421,7 +421,9 @@ function Sidebar({ className }: { className?: string }) {
const view = filterRef.current;
if (!view) return;
const ext = filter({ fields: allFields ?? [] });
- view.dispatch({ effects: filterLanguageCompartmentRef.current.reconfigure(ext) });
+ view.dispatch({
+ effects: filterLanguageCompartmentRef.current.reconfigure(ext),
+ });
}, [allFields]);
if (tree == null || hidden) {
@@ -434,7 +436,7 @@ function Sidebar({ className }: { className?: string }) {
aria-hidden={hidden ?? undefined}
className={classNames(className, 'h-full grid grid-rows-[auto_minmax(0,1fr)_auto]')}
>
-
+
{(tree.children?.length ?? 0) > 0 && (
<>
((get) => {
const memoAllPotentialChildrenAtom = deepEqualAtom(allPotentialChildrenAtom);
-const sidebarFilterAtom = atom<{ text: string; key: string }>({ text: '', key: '' });
+const sidebarFilterAtom = atom<{ text: string; key: string }>({
+ text: '',
+ key: '',
+});
const sidebarTreeAtom = atom<[TreeNode
, FieldDef[]] | null>((get) => {
const allModels = get(memoAllPotentialChildrenAtom);
@@ -580,21 +585,24 @@ const sidebarTreeAtom = atom<[TreeNode, FieldDef[]] | null>((get)
const build = (node: TreeNode, depth: number): boolean => {
const childItems = childrenMap[node.item.id] ?? [];
let matchesSelf = true;
- const fields = getItemFields(node.item);
+ const fields = getItemFields(node);
+ const model = node.item.model;
+ const isLeafNode = !(model === 'folder' || model === 'workspace');
+
for (const [field, value] of Object.entries(fields)) {
if (!value) continue;
allFields[field] = allFields[field] ?? new Set();
allFields[field].add(value);
}
+
if (queryAst != null) {
- matchesSelf = evaluate(queryAst, { text: getItemText(node.item), fields });
+ matchesSelf = isLeafNode && evaluate(queryAst, { text: getItemText(node.item), fields });
}
let matchesChild = false;
// Recurse to children
- const m = node.item.model;
- node.children = m === 'folder' || m === 'workspace' ? [] : undefined;
+ node.children = !isLeafNode ? [] : undefined;
if (node.children != null) {
childItems.sort((a, b) => {
@@ -623,7 +631,7 @@ const sidebarTreeAtom = atom<[TreeNode, FieldDef[]] | null>((get)
const root: TreeNode = {
item: activeWorkspace,
- parent: null,
+ parent: null,
children: [],
depth: 0,
};
@@ -633,7 +641,10 @@ const sidebarTreeAtom = atom<[TreeNode, FieldDef[]] | null>((get)
const fields: FieldDef[] = [];
for (const [name, values] of Object.entries(allFields)) {
- fields.push({ name, values: Array.from(values).filter((v) => v.length < 20) });
+ fields.push({
+ name,
+ values: Array.from(values).filter((v) => v.length < 20),
+ });
}
return [root, fields] as const;
});
@@ -716,7 +727,9 @@ const SidebarInnerItem = memo(function SidebarInnerItem({
);
});
-function getItemFields(item: SidebarModel): Record {
+function getItemFields(node: TreeNode): Record {
+ const item = node.item;
+
if (item.model === 'workspace') return {};
const fields: Record = {};
@@ -736,9 +749,20 @@ function getItemFields(item: SidebarModel): Record {
if (item.model === 'grpc_request') fields.type = 'grpc';
else if (item.model === 'websocket_request') fields.type = 'ws';
+ if (node.parent?.item.model === 'folder') {
+ fields.folder = node.parent.item.name;
+ }
+
return fields;
}
function getItemText(item: SidebarModel): string {
- return resolvedModelName(item);
+ const segments = [];
+ if (item.model === 'http_request') {
+ segments.push(item.method);
+ }
+
+ segments.push(resolvedModelName(item));
+
+ return segments.join(' ');
}
diff --git a/src-web/components/WorkspaceEncryptionSetting.tsx b/src-web/components/WorkspaceEncryptionSetting.tsx
index 408ed911..9ff02dd7 100644
--- a/src-web/components/WorkspaceEncryptionSetting.tsx
+++ b/src-web/components/WorkspaceEncryptionSetting.tsx
@@ -1,49 +1,36 @@
-import {
- enableEncryption,
- revealWorkspaceKey,
- setWorkspaceKey,
-} from "@yaakapp-internal/crypto";
-import type { WorkspaceMeta } from "@yaakapp-internal/models";
-import classNames from "classnames";
-import { useAtomValue } from "jotai";
-import { useEffect, useState } from "react";
-import {
- activeWorkspaceAtom,
- activeWorkspaceMetaAtom,
-} from "../hooks/useActiveWorkspace";
-import { createFastMutation } from "../hooks/useFastMutation";
-import { useStateWithDeps } from "../hooks/useStateWithDeps";
-import { CopyIconButton } from "./CopyIconButton";
-import { Banner } from "./core/Banner";
-import type { ButtonProps } from "./core/Button";
-import { Button } from "./core/Button";
-import { IconButton } from "./core/IconButton";
-import { IconTooltip } from "./core/IconTooltip";
-import { Label } from "./core/Label";
-import { PlainInput } from "./core/PlainInput";
-import { HStack, VStack } from "./core/Stacks";
-import { EncryptionHelp } from "./EncryptionHelp";
+import { enableEncryption, revealWorkspaceKey, setWorkspaceKey } from '@yaakapp-internal/crypto';
+import type { WorkspaceMeta } from '@yaakapp-internal/models';
+import classNames from 'classnames';
+import { useAtomValue } from 'jotai';
+import { useEffect, useState } from 'react';
+import { activeWorkspaceAtom, activeWorkspaceMetaAtom } from '../hooks/useActiveWorkspace';
+import { createFastMutation } from '../hooks/useFastMutation';
+import { useStateWithDeps } from '../hooks/useStateWithDeps';
+import { CopyIconButton } from './CopyIconButton';
+import { Banner } from './core/Banner';
+import type { ButtonProps } from './core/Button';
+import { Button } from './core/Button';
+import { IconButton } from './core/IconButton';
+import { IconTooltip } from './core/IconTooltip';
+import { Label } from './core/Label';
+import { PlainInput } from './core/PlainInput';
+import { HStack, VStack } from './core/Stacks';
+import { EncryptionHelp } from './EncryptionHelp';
interface Props {
- size?: ButtonProps["size"];
+ size?: ButtonProps['size'];
expanded?: boolean;
onDone?: () => void;
onEnabledEncryption?: () => void;
}
-export function WorkspaceEncryptionSetting(
- { size, expanded, onDone, onEnabledEncryption }: Props,
-) {
- const [justEnabledEncryption, setJustEnabledEncryption] = useState(
- false,
- );
+export function WorkspaceEncryptionSetting({ size, expanded, onDone, onEnabledEncryption }: Props) {
+ const [justEnabledEncryption, setJustEnabledEncryption] = useState(false);
const [error, setError] = useState(null);
const workspace = useAtomValue(activeWorkspaceAtom);
const workspaceMeta = useAtomValue(activeWorkspaceMetaAtom);
- const [key, setKey] = useState<
- { key: string | null; error: string | null } | null
- >(null);
+ const [key, setKey] = useState<{ key: string | null; error: string | null } | null>(null);
useEffect(() => {
if (workspaceMeta == null) {
@@ -122,7 +109,7 @@ export function WorkspaceEncryptionSetting(
return (
{
setError(null);
@@ -130,30 +117,32 @@ export function WorkspaceEncryptionSetting(
await enableEncryption(workspaceMeta.workspaceId);
setJustEnabledEncryption(true);
} catch (err) {
- setError("Failed to enable encryption: " + err);
+ setError('Failed to enable encryption: ' + err);
}
}}
>
Enable Encryption
- {error && {error} }
- {expanded
- ? (
-
-
-
- )
- : (
- }>
- Workspace encryption
-
- )}
+ {error && (
+
+ {error}
+
+ )}
+ {expanded ? (
+
+
+
+ ) : (
+ }>
+ Workspace encryption
+
+ )}
);
}
const setWorkspaceKeyMut = createFastMutation({
- mutationKey: ["set-workspace-key"],
+ mutationKey: ['set-workspace-key'],
mutationFn: setWorkspaceKey,
});
@@ -166,13 +155,15 @@ function EnterWorkspaceKey({
onEnabled?: () => void;
error?: string | null;
}) {
- const [key, setKey] = useState("");
+ const [key, setKey] = useState('');
return (
- {error ? {error} : (
+ {error ? (
+ {error}
+ ) : (
- This workspace contains encrypted values but no key is configured.
- Please enter the workspace key to access the encrypted data.
+ This workspace contains encrypted values but no key is configured. Please enter the
+ workspace key to access the encrypted data.
)}
{!disableLabel && (
- Workspace encryption key{" "}
-
+ Workspace encryption key{' '}
+
)}
- {encryptionKey && (
-
- )}
+ {encryptionKey && }
- {encryptionKey && (
-
- )}
+ {encryptionKey && }
setShow((v) => !v)}
/>
@@ -258,32 +238,31 @@ function KeyRevealer({
function HighlightedKey({ keyText, show }: { keyText: string; show: boolean }) {
return (
- {show
- ? (
- keyText.split("").map((c, i) => {
- return (
-
- {c}
-
- );
- })
- )
- : •••••••••••••••••••••
}
+ {show ? (
+ keyText.split('').map((c, i) => {
+ return (
+
+ {c}
+
+ );
+ })
+ ) : (
+ •••••••••••••••••••••
+ )}
);
}
const helpAfterEncryption = (
- The following key is used for encryption operations within this workspace.
- It is stored securely using your OS keychain, but it is recommended to back
- it up. If you share this workspace with others, you'll need to send
- them this key to access any encrypted values.
+ The following key is used for encryption operations within this workspace. It is stored securely
+ using your OS keychain, but it is recommended to back it up. If you share this workspace with
+ others, you'll need to send them this key to access any encrypted values.
);
diff --git a/src-web/components/core/tree/Tree.tsx b/src-web/components/core/tree/Tree.tsx
index 38672d6b..5ae2826c 100644
--- a/src-web/components/core/tree/Tree.tsx
+++ b/src-web/components/core/tree/Tree.tsx
@@ -1,8 +1,4 @@
-import type {
- DragEndEvent,
- DragMoveEvent,
- DragStartEvent,
-} from "@dnd-kit/core";
+import type { DragEndEvent, DragMoveEvent, DragStartEvent } from '@dnd-kit/core';
import {
DndContext,
MeasuringStrategy,
@@ -11,16 +7,10 @@ import {
useDroppable,
useSensor,
useSensors,
-} from "@dnd-kit/core";
-import { type } from "@tauri-apps/plugin-os";
-import classNames from "classnames";
-import type {
- ComponentType,
- MouseEvent,
- ReactElement,
- Ref,
- RefAttributes,
-} from "react";
+} from '@dnd-kit/core';
+import { type } from '@tauri-apps/plugin-os';
+import classNames from 'classnames';
+import type { ComponentType, MouseEvent, ReactElement, Ref, RefAttributes } from 'react';
import {
forwardRef,
memo,
@@ -30,14 +20,14 @@ import {
useMemo,
useRef,
useState,
-} from "react";
-import { useKey, useKeyPressEvent } from "react-use";
-import type { HotkeyAction, HotKeyOptions } from "../../../hooks/useHotKey";
-import { useHotKey } from "../../../hooks/useHotKey";
-import { computeSideForDragMove } from "../../../lib/dnd";
-import { jotaiStore } from "../../../lib/jotai";
-import type { ContextMenuProps, DropdownItem } from "../Dropdown";
-import { ContextMenu } from "../Dropdown";
+} from 'react';
+import { useKey, useKeyPressEvent } from 'react-use';
+import type { HotkeyAction, HotKeyOptions } from '../../../hooks/useHotKey';
+import { useHotKey } from '../../../hooks/useHotKey';
+import { computeSideForDragMove } from '../../../lib/dnd';
+import { jotaiStore } from '../../../lib/jotai';
+import type { ContextMenuProps, DropdownItem } from '../Dropdown';
+import { ContextMenu } from '../Dropdown';
import {
collapsedFamily,
draggingIdsFamily,
@@ -45,23 +35,14 @@ import {
hoveredParentFamily,
isCollapsedFamily,
selectedIdsFamily,
-} from "./atoms";
-import type { SelectableTreeNode, TreeNode } from "./common";
-import {
- closestVisibleNode,
- equalSubtree,
- getSelectedItems,
- hasAncestor,
-} from "./common";
-import { TreeDragOverlay } from "./TreeDragOverlay";
-import type {
- TreeItemClickEvent,
- TreeItemHandle,
- TreeItemProps,
-} from "./TreeItem";
-import type { TreeItemListProps } from "./TreeItemList";
-import { TreeItemList } from "./TreeItemList";
-import { useSelectableItems } from "./useSelectableItems";
+} from './atoms';
+import type { SelectableTreeNode, TreeNode } from './common';
+import { closestVisibleNode, equalSubtree, getSelectedItems, hasAncestor } from './common';
+import { TreeDragOverlay } from './TreeDragOverlay';
+import type { TreeItemClickEvent, TreeItemHandle, TreeItemProps } from './TreeItem';
+import type { TreeItemListProps } from './TreeItemList';
+import { TreeItemList } from './TreeItemList';
+import { useSelectableItems } from './useSelectableItems';
/** So we re-calculate after expanding a folder during drag */
const measuring = { droppable: { strategy: MeasuringStrategy.Always } };
@@ -70,21 +51,15 @@ export interface TreeProps {
root: TreeNode;
treeId: string;
getItemKey: (item: T) => string;
- getContextMenu?: (
- items: T[],
- ) => ContextMenuProps["items"] | Promise;
+ getContextMenu?: (items: T[]) => ContextMenuProps['items'] | Promise;
ItemInner: ComponentType<{ treeId: string; item: T }>;
ItemLeftSlotInner?: ComponentType<{ treeId: string; item: T }>;
ItemRightSlot?: ComponentType<{ treeId: string; item: T }>;
className?: string;
onActivate?: (item: T) => void;
- onDragEnd?: (
- opt: { items: T[]; parent: T; children: T[]; insertAt: number },
- ) => void;
+ onDragEnd?: (opt: { items: T[]; parent: T; children: T[]; insertAt: number }) => void;
hotkeys?: {
- actions: Partial<
- Record void } & HotKeyOptions>
- >;
+ actions: Partial void } & HotKeyOptions>>;
};
getEditOptions?: (item: T) => {
defaultValue: string;
@@ -121,24 +96,19 @@ function TreeInner(
) {
const treeRef = useRef(null);
const selectableItems = useSelectableItems(root);
- const [showContextMenu, setShowContextMenu] = useState<
- {
- items: DropdownItem[];
- x: number;
- y: number;
- } | null
- >(null);
+ const [showContextMenu, setShowContextMenu] = useState<{
+ items: DropdownItem[];
+ x: number;
+ y: number;
+ } | null>(null);
const treeItemRefs = useRef>({});
- const handleAddTreeItemRef = useCallback(
- (item: T, r: TreeItemHandle | null) => {
- if (r == null) {
- delete treeItemRefs.current[item.id];
- } else {
- treeItemRefs.current[item.id] = r;
- }
- },
- [],
- );
+ const handleAddTreeItemRef = useCallback((item: T, r: TreeItemHandle | null) => {
+ if (r == null) {
+ delete treeItemRefs.current[item.id];
+ } else {
+ treeItemRefs.current[item.id] = r;
+ }
+ }, []);
// Select the first item on first render
useEffect(() => {
@@ -176,8 +146,8 @@ function TreeInner(
const ensureTabbableItem = useCallback(() => {
const lastSelectedId = jotaiStore.get(focusIdsFamily(treeId)).lastId;
- const lastSelectedItem = selectableItems.find((i) =>
- i.node.item.id === lastSelectedId && !i.node.hidden
+ const lastSelectedItem = selectableItems.find(
+ (i) => i.node.item.id === lastSelectedId && !i.node.hidden,
);
// If no item found, default to selecting the first item (prefer leaf node);
@@ -224,8 +194,7 @@ function TreeInner(
() => ({
treeId,
focus: tryFocus,
- hasFocus: () =>
- treeRef.current?.contains(document.activeElement) ?? false,
+ hasFocus: () => treeRef.current?.contains(document.activeElement) ?? false,
renameItem: (id) => treeItemRefs.current[id]?.rename(),
selectItem: (id) => {
if (jotaiStore.get(selectedIdsFamily(treeId)).includes(id)) {
@@ -240,9 +209,7 @@ function TreeInner(
const items = getSelectedItems(treeId, selectableItems);
const menuItems = await getContextMenu(items);
const lastSelectedId = jotaiStore.get(focusIdsFamily(treeId)).lastId;
- const rect = lastSelectedId
- ? treeItemRefs.current[lastSelectedId]?.rect()
- : null;
+ const rect = lastSelectedId ? treeItemRefs.current[lastSelectedId]?.rect() : null;
if (rect == null) return;
setShowContextMenu({ items: menuItems, x: rect.x, y: rect.y });
},
@@ -264,66 +231,43 @@ function TreeInner(
// If right-clicked an item that was NOT in the multiple-selection, just use that one
// Also update the selection with it
setSelected([item.id], false);
- jotaiStore.set(
- focusIdsFamily(treeId),
- (prev) => ({ ...prev, lastId: item.id }),
- );
+ jotaiStore.set(focusIdsFamily(treeId), (prev) => ({ ...prev, lastId: item.id }));
return getContextMenu([item]);
}
};
}, [getContextMenu, selectableItems, setSelected, treeId]);
- const handleSelect = useCallback["onClick"]>>(
+ const handleSelect = useCallback['onClick']>>(
(item, { shiftKey, metaKey, ctrlKey }) => {
const anchorSelectedId = jotaiStore.get(focusIdsFamily(treeId)).anchorId;
const selectedIdsAtom = selectedIdsFamily(treeId);
const selectedIds = jotaiStore.get(selectedIdsAtom);
// Mark the item as the last one selected
- jotaiStore.set(
- focusIdsFamily(treeId),
- (prev) => ({ ...prev, lastId: item.id }),
- );
+ jotaiStore.set(focusIdsFamily(treeId), (prev) => ({ ...prev, lastId: item.id }));
if (shiftKey) {
- const anchorIndex = selectableItems.findIndex((i) =>
- i.node.item.id === anchorSelectedId
- );
- const currIndex = selectableItems.findIndex((v) =>
- v.node.item.id === item.id
- );
+ const validSelectableItems = getValidSelectableItems(treeId, selectableItems);
+ const anchorIndex = validSelectableItems.findIndex((i) => i.node.item.id === anchorSelectedId);
+ const currIndex = validSelectableItems.findIndex((v) => v.node.item.id === item.id);
+
// Nothing was selected yet, so just select this item
- if (
- selectedIds.length === 0 || anchorIndex === -1 || currIndex === -1
- ) {
+ if (selectedIds.length === 0 || anchorIndex === -1 || currIndex === -1) {
setSelected([item.id], true);
- jotaiStore.set(
- focusIdsFamily(treeId),
- (prev) => ({ ...prev, anchorId: item.id }),
- );
+ jotaiStore.set(focusIdsFamily(treeId), (prev) => ({ ...prev, anchorId: item.id }));
return;
}
- const validSelectableItems = getValidSelectableItems(
- treeId,
- selectableItems,
- );
if (currIndex > anchorIndex) {
// Selecting down
- const itemsToSelect = validSelectableItems.slice(
- anchorIndex,
- currIndex + 1,
- );
+ const itemsToSelect = validSelectableItems.slice(anchorIndex, currIndex + 1);
setSelected(
itemsToSelect.map((v) => v.node.item.id),
true,
);
} else if (currIndex < anchorIndex) {
// Selecting up
- const itemsToSelect = validSelectableItems.slice(
- currIndex,
- anchorIndex + 1,
- );
+ const itemsToSelect = validSelectableItems.slice(currIndex, anchorIndex + 1);
setSelected(
itemsToSelect.map((v) => v.node.item.id),
true,
@@ -331,7 +275,7 @@ function TreeInner(
} else {
setSelected([item.id], true);
}
- } else if (type() === "macos" ? metaKey : ctrlKey) {
+ } else if (type() === 'macos' ? metaKey : ctrlKey) {
const withoutCurr = selectedIds.filter((id) => id !== item.id);
if (withoutCurr.length === selectedIds.length) {
// It wasn't in there, so add it
@@ -343,16 +287,13 @@ function TreeInner(
} else {
// Select single
setSelected([item.id], true);
- jotaiStore.set(
- focusIdsFamily(treeId),
- (prev) => ({ ...prev, anchorId: item.id }),
- );
+ jotaiStore.set(focusIdsFamily(treeId), (prev) => ({ ...prev, anchorId: item.id }));
}
},
[selectableItems, setSelected, treeId],
);
- const handleClick = useCallback["onClick"]>>(
+ const handleClick = useCallback['onClick']>>(
(item, e) => {
if (e.shiftKey || e.ctrlKey || e.metaKey) {
handleSelect(item, e);
@@ -367,13 +308,8 @@ function TreeInner(
const selectPrevItem = useCallback(
(e: TreeItemClickEvent) => {
const lastSelectedId = jotaiStore.get(focusIdsFamily(treeId)).lastId;
- const validSelectableItems = getValidSelectableItems(
- treeId,
- selectableItems,
- );
- const index = validSelectableItems.findIndex((i) =>
- i.node.item.id === lastSelectedId
- );
+ const validSelectableItems = getValidSelectableItems(treeId, selectableItems);
+ const index = validSelectableItems.findIndex((i) => i.node.item.id === lastSelectedId);
const item = validSelectableItems[index - 1];
if (item != null) {
handleSelect(item.node.item, e);
@@ -385,13 +321,8 @@ function TreeInner(
const selectNextItem = useCallback(
(e: TreeItemClickEvent) => {
const lastSelectedId = jotaiStore.get(focusIdsFamily(treeId)).lastId;
- const validSelectableItems = getValidSelectableItems(
- treeId,
- selectableItems,
- );
- const index = validSelectableItems.findIndex((i) =>
- i.node.item.id === lastSelectedId
- );
+ const validSelectableItems = getValidSelectableItems(treeId, selectableItems);
+ const index = validSelectableItems.findIndex((i) => i.node.item.id === lastSelectedId);
const item = validSelectableItems[index + 1];
if (item != null) {
handleSelect(item.node.item, e);
@@ -404,8 +335,7 @@ function TreeInner(
(e: TreeItemClickEvent) => {
const lastSelectedId = jotaiStore.get(focusIdsFamily(treeId)).lastId;
const lastSelectedItem =
- selectableItems.find((i) => i.node.item.id === lastSelectedId)?.node ??
- null;
+ selectableItems.find((i) => i.node.item.id === lastSelectedId)?.node ?? null;
if (lastSelectedItem?.parent != null) {
handleSelect(lastSelectedItem.parent.item, e);
}
@@ -414,7 +344,7 @@ function TreeInner(
);
useKey(
- (e) => e.key === "ArrowUp" || e.key.toLowerCase() === "k",
+ (e) => e.key === 'ArrowUp' || e.key.toLowerCase() === 'k',
(e) => {
if (!isTreeFocused()) return;
e.preventDefault();
@@ -425,7 +355,7 @@ function TreeInner(
);
useKey(
- (e) => e.key === "ArrowDown" || e.key.toLowerCase() === "j",
+ (e) => e.key === 'ArrowDown' || e.key.toLowerCase() === 'j',
(e) => {
if (!isTreeFocused()) return;
e.preventDefault();
@@ -437,26 +367,21 @@ function TreeInner(
// If the selected item is a collapsed folder, expand it. Otherwise, select next item
useKey(
- (e) => e.key === "ArrowRight" || e.key === "l",
+ (e) => e.key === 'ArrowRight' || e.key === 'l',
(e) => {
if (!isTreeFocused()) return;
e.preventDefault();
const collapsed = jotaiStore.get(collapsedFamily(treeId));
const lastSelectedId = jotaiStore.get(focusIdsFamily(treeId)).lastId;
- const lastSelectedItem = selectableItems.find((i) =>
- i.node.item.id === lastSelectedId
- );
+ const lastSelectedItem = selectableItems.find((i) => i.node.item.id === lastSelectedId);
if (
lastSelectedId &&
lastSelectedItem?.node.children != null &&
collapsed[lastSelectedItem.node.item.id] === true
) {
- jotaiStore.set(
- isCollapsedFamily({ treeId, itemId: lastSelectedId }),
- false,
- );
+ jotaiStore.set(isCollapsedFamily({ treeId, itemId: lastSelectedId }), false);
} else {
selectNextItem(e);
}
@@ -468,26 +393,21 @@ function TreeInner(
// If the selected item is in a folder, select its parent.
// If the selected item is an expanded folder, collapse it.
useKey(
- (e) => e.key === "ArrowLeft" || e.key === "h",
+ (e) => e.key === 'ArrowLeft' || e.key === 'h',
(e) => {
if (!isTreeFocused()) return;
e.preventDefault();
const collapsed = jotaiStore.get(collapsedFamily(treeId));
const lastSelectedId = jotaiStore.get(focusIdsFamily(treeId)).lastId;
- const lastSelectedItem = selectableItems.find((i) =>
- i.node.item.id === lastSelectedId
- );
+ const lastSelectedItem = selectableItems.find((i) => i.node.item.id === lastSelectedId);
if (
lastSelectedId &&
lastSelectedItem?.node.children != null &&
collapsed[lastSelectedItem.node.item.id] !== true
) {
- jotaiStore.set(
- isCollapsedFamily({ treeId, itemId: lastSelectedId }),
- true,
- );
+ jotaiStore.set(isCollapsedFamily({ treeId, itemId: lastSelectedId }), true);
} else {
selectParentItem(e);
}
@@ -496,7 +416,7 @@ function TreeInner(
[selectableItems, handleSelect],
);
- useKeyPressEvent("Escape", async () => {
+ useKeyPressEvent('Escape', async () => {
if (!treeRef.current?.contains(document.activeElement)) return;
clearDragState();
const lastSelectedId = jotaiStore.get(focusIdsFamily(treeId)).lastId;
@@ -535,22 +455,19 @@ function TreeInner(
return;
}
- const overSelectableItem =
- selectableItems.find((i) => i.node.item.id === over.id) ?? null;
+ const overSelectableItem = selectableItems.find((i) => i.node.item.id === over.id) ?? null;
if (overSelectableItem == null) {
return;
}
const draggingItems = jotaiStore.get(draggingIdsFamily(treeId));
for (const id of draggingItems) {
- const item = selectableItems.find((i) => i.node.item.id === id)?.node ??
- null;
+ const item = selectableItems.find((i) => i.node.item.id === id)?.node ?? null;
if (item == null) {
return;
}
- const isSameParent =
- item.parent?.item.id === overSelectableItem.node.parent?.item.id;
+ const isSameParent = item.parent?.item.id === overSelectableItem.node.parent?.item.id;
if (item.localDrag && !isSameParent) {
return;
}
@@ -561,15 +478,13 @@ function TreeInner(
const item = node.item;
let hoveredParent = node.parent;
- const dragIndex =
- selectableItems.findIndex((n) => n.node.item.id === item.id) ?? -1;
+ const dragIndex = selectableItems.findIndex((n) => n.node.item.id === item.id) ?? -1;
const hovered = selectableItems[dragIndex]?.node ?? null;
- const hoveredIndex = dragIndex + (side === "above" ? 0 : 1);
- let hoveredChildIndex = overSelectableItem.index +
- (side === "above" ? 0 : 1);
+ const hoveredIndex = dragIndex + (side === 'above' ? 0 : 1);
+ let hoveredChildIndex = overSelectableItem.index + (side === 'above' ? 0 : 1);
// Move into the folder if it's open and we're moving below it
- if (hovered?.children != null && side === "below") {
+ if (hovered?.children != null && side === 'below') {
hoveredParent = hovered;
hoveredChildIndex = 0;
}
@@ -601,9 +516,7 @@ function TreeInner(
const handleDragStart = useCallback(
function handleDragStart(e: DragStartEvent) {
const selectedItems = getSelectedItems(treeId, selectableItems);
- const isDraggingSelectedItem = selectedItems.find((i) =>
- i.id === e.active.id
- );
+ const isDraggingSelectedItem = selectedItems.find((i) => i.id === e.active.id);
// If we started dragging an already-selected item, we'll use that
if (isDraggingSelectedItem) {
@@ -613,9 +526,7 @@ function TreeInner(
);
} else {
// If we started dragging a non-selected item, only drag that item
- const activeItem = selectableItems.find((i) =>
- i.node.item.id === e.active.id
- )?.node.item;
+ const activeItem = selectableItems.find((i) => i.node.item.id === e.active.id)?.node.item;
if (activeItem != null) {
jotaiStore.set(draggingIdsFamily(treeId), [activeItem.id]);
// Also update selection to just be this one
@@ -656,30 +567,25 @@ function TreeInner(
return;
}
- const hoveredParentS = hoveredParentId === root.item.id
- ? { node: root, depth: 0, index: 0 }
- : (selectableItems.find((i) => i.node.item.id === hoveredParentId) ??
- null);
+ const hoveredParentS =
+ hoveredParentId === root.item.id
+ ? { node: root, depth: 0, index: 0 }
+ : (selectableItems.find((i) => i.node.item.id === hoveredParentId) ?? null);
const hoveredParent = hoveredParentS?.node ?? null;
- if (
- hoveredParent == null || hoveredIndex == null || !draggingItems?.length
- ) {
+ if (hoveredParent == null || hoveredIndex == null || !draggingItems?.length) {
return;
}
// Resolve the actual tree nodes for each dragged item (keeps order of draggingItems)
const draggedNodes: TreeNode[] = draggingItems
.map((id) => {
- return selectableItems.find((i) => i.node.item.id === id)?.node ??
- null;
+ return selectableItems.find((i) => i.node.item.id === id)?.node ?? null;
})
.filter((n) => n != null)
// Filter out invalid drags (dragging into descendant)
.filter(
- (n) =>
- hoveredParent.item.id !== n.item.id &&
- !hasAncestor(hoveredParent, n.item.id),
+ (n) => hoveredParent.item.id !== n.item.id && !hasAncestor(hoveredParent, n.item.id),
);
// Work on a local copy of target children
@@ -708,7 +614,7 @@ function TreeInner(
const treeItemListProps: Omit<
TreeItemListProps,
- "nodes" | "treeId" | "activeIdAtom" | "hoveredParent" | "hoveredIndex"
+ 'nodes' | 'treeId' | 'activeIdAtom' | 'hoveredParent' | 'hoveredIndex'
> = {
getItemKey,
getContextMenu: handleGetContextMenu,
@@ -731,17 +637,11 @@ function TreeInner(
[getContextMenu],
);
- const sensors = useSensors(
- useSensor(PointerSensor, { activationConstraint: { distance: 6 } }),
- );
+ const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 6 } }));
return (
<>
-
+
{showContextMenu && (
(
ref={treeRef}
className={classNames(
className,
- "outline-none h-full",
- "overflow-y-auto overflow-x-hidden",
- "grid grid-rows-[auto_1fr]",
+ 'outline-none h-full',
+ 'overflow-y-auto overflow-x-hidden',
+ 'grid grid-rows-[auto_1fr]',
)}
>
(
/>
{/* Assign root ID so we can reuse our same move/end logic */}
-
+
{
for (const key of Object.keys(prevProps)) {
- if (
- prevProps[key as keyof typeof prevProps] !==
- nextProps[key as keyof typeof nextProps]
- ) {
+ if (prevProps[key as keyof typeof prevProps] !== nextProps[key as keyof typeof nextProps]) {
return false;
}
}
@@ -864,7 +758,7 @@ function TreeHotKey({
...options,
enable: () => {
if (enable == null) return true;
- if (typeof enable === "function") return enable();
+ if (typeof enable === 'function') return enable();
else return enable;
},
},
@@ -878,7 +772,7 @@ function TreeHotKeys({
selectableItems,
}: {
treeId: string;
- hotkeys: TreeProps["hotkeys"];
+ hotkeys: TreeProps['hotkeys'];
selectableItems: SelectableTreeNode[];
}) {
if (hotkeys == null) return null;
diff --git a/src-web/hooks/useEnvironmentsBreakdown.ts b/src-web/hooks/useEnvironmentsBreakdown.ts
index 8ad84e2b..e510a637 100644
--- a/src-web/hooks/useEnvironmentsBreakdown.ts
+++ b/src-web/hooks/useEnvironmentsBreakdown.ts
@@ -4,7 +4,18 @@ import { atom, useAtomValue } from 'jotai';
export const environmentsBreakdownAtom = atom((get) => {
const allEnvironments = get(environmentsAtom);
const baseEnvironments = allEnvironments.filter((e) => e.parentModel === 'workspace') ?? [];
- const subEnvironments = allEnvironments.filter((e) => e.parentModel === 'environment') ?? [];
+
+ const subEnvironments =
+ allEnvironments
+ .filter((e) => e.parentModel === 'environment')
+ ?.sort((a, b) => {
+ if (a.sortPriority === b.sortPriority) {
+ return a.updatedAt > b.updatedAt ? 1 : -1;
+ } else {
+ return a.sortPriority - b.sortPriority;
+ }
+ }) ?? [];
+
const folderEnvironments =
allEnvironments.filter((e) => e.parentModel === 'folder' && e.parentId != null) ?? [];
diff --git a/src-web/hooks/useRecentCookieJars.ts b/src-web/hooks/useRecentCookieJars.ts
index d3662b4f..af9a5825 100644
--- a/src-web/hooks/useRecentCookieJars.ts
+++ b/src-web/hooks/useRecentCookieJars.ts
@@ -4,7 +4,6 @@ import { useEffect, useMemo } from 'react';
import { jotaiStore } from '../lib/jotai';
import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
import { activeCookieJarAtom } from './useActiveCookieJar';
-import { activeWorkspaceIdAtom } from './useActiveWorkspace';
import { useKeyValue } from './useKeyValue';
const kvKey = (workspaceId: string) => 'recent_cookie_jars::' + workspaceId;
@@ -13,9 +12,8 @@ const fallback: string[] = [];
export function useRecentCookieJars() {
const cookieJars = useAtomValue(cookieJarsAtom);
- const activeWorkspaceId = useAtomValue(activeWorkspaceIdAtom);
const kv = useKeyValue({
- key: kvKey(activeWorkspaceId ?? 'n/a'),
+ key: kvKey(cookieJars[0]?.workspaceId ?? 'n/a'),
namespace,
fallback,
});
@@ -31,18 +29,16 @@ export function useRecentCookieJars() {
export function useSubscribeRecentCookieJars() {
useEffect(() => {
return jotaiStore.sub(activeCookieJarAtom, async () => {
- const activeWorkspaceId = jotaiStore.get(activeWorkspaceIdAtom);
- const activeCookieJarId = jotaiStore.get(activeCookieJarAtom)?.id ?? null;
- if (activeWorkspaceId == null) return;
- if (activeCookieJarId == null) return;
+ const activeCookieJar = jotaiStore.get(activeCookieJarAtom);
+ if (activeCookieJar == null) return;
- const key = kvKey(activeWorkspaceId);
+ const key = kvKey(activeCookieJar.workspaceId);
const recentIds = getKeyValue({ namespace, key, fallback });
- if (recentIds[0] === activeCookieJarId) return; // Short-circuit
+ if (recentIds[0] === activeCookieJar.id) return; // Short-circuit
- const withoutActiveId = recentIds.filter((id) => id !== activeCookieJarId);
- const value = [activeCookieJarId, ...withoutActiveId];
+ const withoutActiveId = recentIds.filter((id) => id !== activeCookieJar.id);
+ const value = [activeCookieJar.id, ...withoutActiveId];
await setKeyValue({ namespace, key, value });
});
}, []);
diff --git a/src-web/hooks/useRecentEnvironments.ts b/src-web/hooks/useRecentEnvironments.ts
index 2e7f9f1c..ea4a88bf 100644
--- a/src-web/hooks/useRecentEnvironments.ts
+++ b/src-web/hooks/useRecentEnvironments.ts
@@ -1,9 +1,7 @@
-import { useAtomValue } from 'jotai';
import { useEffect, useMemo } from 'react';
import { jotaiStore } from '../lib/jotai';
import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
-import { activeEnvironmentIdAtom } from './useActiveEnvironment';
-import { activeWorkspaceAtom, activeWorkspaceIdAtom } from './useActiveWorkspace';
+import { activeEnvironmentAtom } from './useActiveEnvironment';
import { useEnvironmentsBreakdown } from './useEnvironmentsBreakdown';
import { useKeyValue } from './useKeyValue';
@@ -12,10 +10,9 @@ const namespace = 'global';
const fallback: string[] = [];
export function useRecentEnvironments() {
- const { subEnvironments } = useEnvironmentsBreakdown();
- const activeWorkspace = useAtomValue(activeWorkspaceAtom);
+ const { subEnvironments, allEnvironments } = useEnvironmentsBreakdown();
const kv = useKeyValue({
- key: kvKey(activeWorkspace?.id ?? 'n/a'),
+ key: kvKey(allEnvironments[0]?.workspaceId ?? 'n/a'),
namespace,
fallback,
});
@@ -30,19 +27,16 @@ export function useRecentEnvironments() {
export function useSubscribeRecentEnvironments() {
useEffect(() => {
- return jotaiStore.sub(activeEnvironmentIdAtom, async () => {
- const activeWorkspaceId = jotaiStore.get(activeWorkspaceIdAtom);
- const activeEnvironmentId = jotaiStore.get(activeEnvironmentIdAtom);
- if (activeWorkspaceId == null) return;
- if (activeEnvironmentId == null) return;
-
- const key = kvKey(activeWorkspaceId);
+ return jotaiStore.sub(activeEnvironmentAtom, async () => {
+ const activeEnvironment = jotaiStore.get(activeEnvironmentAtom);
+ if (activeEnvironment == null) return;
+ const key = kvKey(activeEnvironment.workspaceId);
const recentIds = getKeyValue({ namespace, key, fallback });
- if (recentIds[0] === activeEnvironmentId) return; // Short-circuit
+ if (recentIds[0] === activeEnvironment.id) return; // Short-circuit
- const withoutActiveId = recentIds.filter((id) => id !== activeEnvironmentId);
- const value = [activeEnvironmentId, ...withoutActiveId];
+ const withoutActiveId = recentIds.filter((id) => id !== activeEnvironment.id);
+ const value = [activeEnvironment.id, ...withoutActiveId];
await setKeyValue({ namespace, key, value });
});
}, []);
diff --git a/src-web/hooks/useRecentRequests.ts b/src-web/hooks/useRecentRequests.ts
index 87bac6fc..9d0af70c 100644
--- a/src-web/hooks/useRecentRequests.ts
+++ b/src-web/hooks/useRecentRequests.ts
@@ -1,11 +1,9 @@
-import { useAtomValue } from 'jotai';
import { useEffect, useMemo } from 'react';
import { jotaiStore } from '../lib/jotai';
import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
-import { activeRequestIdAtom } from './useActiveRequestId';
-import { activeWorkspaceIdAtom } from './useActiveWorkspace';
import { useKeyValue } from './useKeyValue';
import { useAllRequests } from './useAllRequests';
+import { activeRequestAtom } from './useActiveRequest';
const kvKey = (workspaceId: string) => 'recent_requests::' + workspaceId;
const namespace = 'global';
@@ -13,10 +11,9 @@ const fallback: string[] = [];
export function useRecentRequests() {
const requests = useAllRequests();
- const activeWorkspaceId = useAtomValue(activeWorkspaceIdAtom);
const { set: setRecentRequests, value: recentRequests } = useKeyValue({
- key: kvKey(activeWorkspaceId ?? 'n/a'),
+ key: kvKey(requests[0]?.workspaceId ?? 'n/a'),
namespace,
fallback,
});
@@ -31,19 +28,17 @@ export function useRecentRequests() {
export function useSubscribeRecentRequests() {
useEffect(() => {
- return jotaiStore.sub(activeRequestIdAtom, async () => {
- const activeWorkspaceId = jotaiStore.get(activeWorkspaceIdAtom);
- const activeRequestId = jotaiStore.get(activeRequestIdAtom);
- if (activeWorkspaceId == null) return;
- if (activeRequestId == null) return;
+ return jotaiStore.sub(activeRequestAtom, async () => {
+ const activeRequest = jotaiStore.get(activeRequestAtom);
+ if (activeRequest == null) return;
- const key = kvKey(activeWorkspaceId);
+ const key = kvKey(activeRequest.workspaceId);
const recentIds = getKeyValue({ namespace, key, fallback });
- if (recentIds[0] === activeRequestId) return; // Short-circuit
+ if (recentIds[0] === activeRequest.id) return; // Short-circuit
- const withoutActiveId = recentIds.filter((id) => id !== activeRequestId);
- const value = [activeRequestId, ...withoutActiveId];
+ const withoutActiveId = recentIds.filter((id) => id !== activeRequest.id);
+ const value = [activeRequest.id, ...withoutActiveId];
await setKeyValue({ namespace, key, value });
});
}, []);