mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-24 18:31:16 +01:00
Some fixes around environments
This commit is contained in:
@@ -41,9 +41,9 @@ export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdo
|
||||
leftSlot: e.id === activeEnvironment?.id ? <Icon icon="check" /> : <Icon icon="empty" />,
|
||||
onSelect: async () => {
|
||||
if (e.id !== activeEnvironment?.id) {
|
||||
setActiveEnvironmentId(e.id);
|
||||
await setActiveEnvironmentId(e.id);
|
||||
} else {
|
||||
setActiveEnvironmentId(null);
|
||||
await setActiveEnvironmentId(null);
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -40,7 +40,8 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
||||
const selectedEnvironment = allEnvironments.find((e) => e.id === selectedEnvironmentId);
|
||||
|
||||
const handleCreateEnvironment = async () => {
|
||||
const e = await createEnvironment.mutateAsync();
|
||||
if (baseEnvironment == null) return;
|
||||
const e = await createEnvironment.mutateAsync(baseEnvironment);
|
||||
if (e == null) return;
|
||||
setSelectedEnvironmentId(e.id);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { save } from '@tauri-apps/plugin-dialog';
|
||||
import type { Workspace } from '@yaakapp-internal/models';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import slugify from 'slugify';
|
||||
import type { Workspace } from '@yaakapp-internal/models';
|
||||
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { count } from '../lib/pluralize';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { Button } from './core/Button';
|
||||
@@ -11,16 +13,32 @@ import { HStack, VStack } from './core/Stacks';
|
||||
interface Props {
|
||||
onHide: () => void;
|
||||
onSuccess: (path: string) => void;
|
||||
activeWorkspace: Workspace;
|
||||
workspaces: Workspace[];
|
||||
}
|
||||
|
||||
export function ExportDataDialog({
|
||||
export function ExportDataDialog({ onHide, onSuccess }: Props) {
|
||||
const allWorkspaces = useWorkspaces();
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
if (activeWorkspace == null || allWorkspaces.length === 0) return null;
|
||||
|
||||
return (
|
||||
<ExportDataDialogContent
|
||||
onHide={onHide}
|
||||
onSuccess={onSuccess}
|
||||
allWorkspaces={allWorkspaces}
|
||||
activeWorkspace={activeWorkspace}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function ExportDataDialogContent({
|
||||
onHide,
|
||||
onSuccess,
|
||||
activeWorkspace,
|
||||
workspaces: allWorkspaces,
|
||||
}: Props) {
|
||||
allWorkspaces,
|
||||
}: Props & {
|
||||
allWorkspaces: Workspace[];
|
||||
activeWorkspace: Workspace;
|
||||
}) {
|
||||
const [selectedWorkspaces, setSelectedWorkspaces] = useState<Record<string, boolean>>({
|
||||
[activeWorkspace.id]: true,
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@ export function WorkspaceSettingsDialog({ workspaceId }: Props) {
|
||||
|
||||
return (
|
||||
<VStack space={3} className="pb-3 max-h-[50vh]">
|
||||
{workspace.id}
|
||||
<PlainInput
|
||||
label="Workspace Name"
|
||||
defaultValue={workspace.name}
|
||||
|
||||
@@ -35,11 +35,16 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
||||
const openWorkspaceNewWindow = settings?.openWorkspaceNewWindow ?? null;
|
||||
const deleteSendHistory = useDeleteSendHistory();
|
||||
|
||||
const orderedWorkspaces = useMemo(
|
||||
() => [...workspaces].sort((a, b) => (a.name.localeCompare(b.name) > 0 ? 1 : -1)),
|
||||
[workspaces],
|
||||
);
|
||||
|
||||
const { workspaceItems, extraItems } = useMemo<{
|
||||
workspaceItems: RadioDropdownItem[];
|
||||
extraItems: DropdownItem[];
|
||||
}>(() => {
|
||||
const workspaceItems: RadioDropdownItem[] = workspaces.map((w) => ({
|
||||
const workspaceItems: RadioDropdownItem[] = orderedWorkspaces.map((w) => ({
|
||||
key: w.id,
|
||||
label: w.name,
|
||||
value: w.id,
|
||||
@@ -84,13 +89,13 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
||||
|
||||
return { workspaceItems, extraItems };
|
||||
}, [
|
||||
activeWorkspace,
|
||||
activeWorkspace?.id,
|
||||
activeWorkspaceId,
|
||||
createWorkspace.mutate,
|
||||
deleteSendHistory.mutate,
|
||||
deleteWorkspace.mutate,
|
||||
dialog,
|
||||
workspaces,
|
||||
orderedWorkspaces,
|
||||
]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getRouteApi, useSearch } from '@tanstack/react-router';
|
||||
import { useNavigate, useSearch } from '@tanstack/react-router';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useCookieJars } from './useCookieJars';
|
||||
|
||||
@@ -38,17 +38,15 @@ export function useEnsureActiveCookieJar() {
|
||||
}, [activeCookieJarId, cookieJars, setActiveCookieJarId]);
|
||||
}
|
||||
|
||||
const routeApi = getRouteApi('/workspaces/$workspaceId/');
|
||||
|
||||
function useActiveCookieJarId() {
|
||||
// NOTE: This query param is accessed from Rust side, so do not change
|
||||
const { cookieJarId: id } = useSearch({ strict: false });
|
||||
const navigate = routeApi.useNavigate();
|
||||
const { cookie_jar_id: id } = useSearch({ strict: false });
|
||||
const navigate = useNavigate({ from: '/workspaces/$workspaceId' });
|
||||
|
||||
const setId = useCallback(
|
||||
(id: string) =>
|
||||
navigate({
|
||||
search: (prev) => ({ ...prev, cookieJarId: id }),
|
||||
search: (prev) => ({ ...prev, cookie_jar_id: id }),
|
||||
}),
|
||||
[navigate],
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getRouteApi, useSearch } from '@tanstack/react-router';
|
||||
import { useNavigate, useSearch } from '@tanstack/react-router';
|
||||
import { useCallback } from 'react';
|
||||
import { useEnvironments } from './useEnvironments';
|
||||
|
||||
@@ -11,17 +11,15 @@ export function useActiveEnvironment() {
|
||||
|
||||
export const QUERY_ENVIRONMENT_ID = 'environment_id';
|
||||
|
||||
const routeApi = getRouteApi('/workspaces/$workspaceId/');
|
||||
|
||||
function useActiveEnvironmentId() {
|
||||
// NOTE: This query param is accessed from Rust side, so do not change
|
||||
const { environmentId: id } = useSearch({ strict: false });
|
||||
const navigate = routeApi.useNavigate();
|
||||
const { environment_id: id} = useSearch({ strict: false });
|
||||
const navigate = useNavigate({ from: '/workspaces/$workspaceId' });
|
||||
|
||||
const setId = useCallback(
|
||||
(environment_id: string | null) =>
|
||||
(environmentId: string | null) =>
|
||||
navigate({
|
||||
search: (prev) => ({ ...prev, environment_id: environment_id ?? undefined }),
|
||||
search: (prev) => ({ ...prev, environment_id: environmentId ?? undefined }),
|
||||
}),
|
||||
[navigate],
|
||||
);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import type { Environment } from '@yaakapp-internal/models';
|
||||
import {useSetAtom} from "jotai";
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useActiveEnvironment } from './useActiveEnvironment';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import {environmentsAtom} from "./useEnvironments";
|
||||
import { environmentsAtom } from './useEnvironments';
|
||||
import { useFastMutation } from './useFastMutation';
|
||||
import { usePrompt } from './usePrompt';
|
||||
import {updateModelList} from "./useSyncModelStores";
|
||||
import { updateModelList } from './useSyncModelStores';
|
||||
|
||||
export function useCreateEnvironment() {
|
||||
const [, setActiveEnvironmentId] = useActiveEnvironment();
|
||||
@@ -15,9 +15,9 @@ export function useCreateEnvironment() {
|
||||
const workspace = useActiveWorkspace();
|
||||
const setEnvironments = useSetAtom(environmentsAtom);
|
||||
|
||||
return useFastMutation<Environment | null, unknown, void>({
|
||||
return useFastMutation<Environment | null, unknown, Environment>({
|
||||
mutationKey: ['create_environment'],
|
||||
mutationFn: async () => {
|
||||
mutationFn: async (baseEnvironment) => {
|
||||
const name = await prompt({
|
||||
id: 'new-environment',
|
||||
title: 'New Environment',
|
||||
@@ -33,6 +33,7 @@ export function useCreateEnvironment() {
|
||||
name,
|
||||
variables: [],
|
||||
workspaceId: workspace?.id,
|
||||
environmentId: baseEnvironment.id,
|
||||
});
|
||||
},
|
||||
onSettled: () => trackEvent('environment', 'create'),
|
||||
|
||||
@@ -7,9 +7,8 @@ export const environmentsAtom = atom<Environment[]>([]);
|
||||
export function useEnvironments() {
|
||||
const allEnvironments = useAtomValue(environmentsAtom);
|
||||
const baseEnvironment = allEnvironments.find((e) => e.environmentId == null);
|
||||
const subEnvironments = allEnvironments.filter(
|
||||
(e) => e.environmentId === (baseEnvironment?.id ?? 'n/a'),
|
||||
);
|
||||
const subEnvironments =
|
||||
allEnvironments.filter((e) => e.environmentId === (baseEnvironment?.id ?? 'n/a')) ?? [];
|
||||
|
||||
return { baseEnvironment, subEnvironments, allEnvironments } as const;
|
||||
}
|
||||
|
||||
@@ -29,8 +29,6 @@ export function useExportData() {
|
||||
render: ({ hide }) => (
|
||||
<ExportDataDialog
|
||||
onHide={hide}
|
||||
workspaces={workspaces}
|
||||
activeWorkspace={activeWorkspace}
|
||||
onSuccess={() => {
|
||||
toast.show({
|
||||
color: 'success',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { MutationKey } from '@tanstack/react-query';
|
||||
import { useCallback } from 'react';
|
||||
import { useToast } from './useToast';
|
||||
|
||||
export function useFastMutation<TData = unknown, TError = unknown, TVariables = void>({
|
||||
mutationKey,
|
||||
@@ -7,13 +8,17 @@ export function useFastMutation<TData = unknown, TError = unknown, TVariables =
|
||||
onSuccess,
|
||||
onError,
|
||||
onSettled,
|
||||
toastyError,
|
||||
}: {
|
||||
mutationKey: MutationKey;
|
||||
mutationFn: (vars: TVariables) => Promise<TData>;
|
||||
onSettled?: () => void;
|
||||
onError?: (err: TError) => void;
|
||||
onSuccess?: (data: TData) => void;
|
||||
toastyError?: boolean;
|
||||
}) {
|
||||
const toast = useToast();
|
||||
|
||||
const mutateAsync = useCallback(
|
||||
async (variables: TVariables) => {
|
||||
try {
|
||||
@@ -22,8 +27,14 @@ export function useFastMutation<TData = unknown, TError = unknown, TVariables =
|
||||
return data;
|
||||
} catch (err: unknown) {
|
||||
const e = err as TError;
|
||||
console.log('MUTATION FAILED', mutationKey, e);
|
||||
console.log('Fast mutation error', mutationKey, e);
|
||||
onError?.(e);
|
||||
if (toastyError) {
|
||||
toast.show({
|
||||
id: 'error-' + mutationKey.join('.'),
|
||||
message: String(e),
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
onSettled?.();
|
||||
}
|
||||
|
||||
@@ -20,8 +20,11 @@ export function useSyncWorkspaceChildModels() {
|
||||
const setEnvironments = useSetAtom(environmentsAtom);
|
||||
|
||||
const workspace = useActiveWorkspace();
|
||||
const workspaceId = workspace?.id ?? 'n/a';
|
||||
const workspaceId = workspace?.id;
|
||||
useEffect(() => {
|
||||
if (workspaceId == null) {
|
||||
return;
|
||||
}
|
||||
(async function () {
|
||||
console.log('Syncing model stores', { workspaceId });
|
||||
// Set the things we need first, first
|
||||
|
||||
@@ -13,6 +13,7 @@ export function useUpdateEnvironment(id: string | null) {
|
||||
unknown,
|
||||
Partial<Environment> | ((r: Environment) => Environment)
|
||||
>({
|
||||
toastyError: true,
|
||||
mutationKey: ['update_environment', id],
|
||||
mutationFn: async (v) => {
|
||||
const environment = await getEnvironment(id);
|
||||
|
||||
@@ -80,5 +80,10 @@ type TauriCmd =
|
||||
|
||||
export async function invokeCmd<T>(cmd: TauriCmd, args?: InvokeArgs): Promise<T> {
|
||||
// console.log('RUN COMMAND', cmd, args);
|
||||
return invoke(cmd, args);
|
||||
try {
|
||||
return await invoke(cmd, args);
|
||||
} catch (err) {
|
||||
console.warn('Tauri command error', cmd, err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,15 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||
import { Workspace } from '../../../components/Workspace';
|
||||
|
||||
interface WorkspaceSearchSchema {
|
||||
cookieJarId?: string | null;
|
||||
environmentId?: string | null;
|
||||
cookie_jar_id?: string | null;
|
||||
environment_id?: string | null;
|
||||
}
|
||||
|
||||
export const Route = createFileRoute('/workspaces/$workspaceId/')({
|
||||
component: RouteComponent,
|
||||
validateSearch: (search: Record<string, unknown>): WorkspaceSearchSchema => ({
|
||||
environmentId: search.environment_id as string,
|
||||
cookieJarId: search.cookie_jar_id as string,
|
||||
environment_id: search.environment_id as string,
|
||||
cookie_jar_id: search.cookie_jar_id as string,
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user