Ability to sync environments to folder (#207)

This commit is contained in:
Gregory Schier
2025-05-08 14:10:07 -07:00
committed by GitHub
parent 77cdea2f9f
commit 94d4227bc1
54 changed files with 710 additions and 425 deletions

View File

@@ -1,24 +1,8 @@
import type { EnvironmentVariable } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { useMemo } from 'react';
import { activeEnvironmentAtom } from './useActiveEnvironment';
import { useEnvironmentsBreakdown } from './useEnvironmentsBreakdown';
import { useEnvironmentVariables } from './useEnvironmentVariables';
export function useActiveEnvironmentVariables() {
const { baseEnvironment } = useEnvironmentsBreakdown();
const activeEnvironment = useAtomValue(activeEnvironmentAtom);
return useMemo(() => {
const varMap: Record<string, EnvironmentVariable> = {};
const allVariables = [
...(baseEnvironment?.variables ?? []),
...(activeEnvironment?.variables ?? []),
];
for (const v of allVariables) {
if (!v.enabled || !v.name) continue;
varMap[v.name] = v;
}
return Object.values(varMap);
}, [activeEnvironment, baseEnvironment]);
return useEnvironmentVariables(activeEnvironment?.id ?? null);
}

View File

@@ -1,26 +0,0 @@
import { clear, writeText } from '@tauri-apps/plugin-clipboard-manager';
import { useCallback } from 'react';
import { showToast } from '../lib/toast';
export function useCopy({ disableToast }: { disableToast?: boolean } = {}) {
const copy = useCallback(
(text: string | null) => {
if (text == null) {
clear().catch(console.error);
} else {
writeText(text).catch(console.error);
}
if (text != '' && !disableToast) {
showToast({
id: 'copied',
color: 'success',
icon: 'copy',
message: 'Copied to clipboard',
});
}
},
[disableToast],
);
return copy;
}

View File

@@ -1,15 +1,14 @@
import { useFastMutation } from './useFastMutation';
import type { HttpResponse } from '@yaakapp-internal/models';
import { useCopy } from './useCopy';
import { copyToClipboard } from '../lib/copy';
import { getResponseBodyText } from '../lib/responseBody';
import { useFastMutation } from './useFastMutation';
export function useCopyHttpResponse(response: HttpResponse) {
const copy = useCopy();
return useFastMutation({
mutationKey: ['copy_http_response', response.id],
async mutationFn() {
const body = await getResponseBodyText(response);
copy(body);
copyToClipboard(body);
},
});
}

View File

@@ -1,20 +1,21 @@
import type { Environment } from '@yaakapp-internal/models';
import { createWorkspaceModel } from '@yaakapp-internal/models';
import { jotaiStore } from '../lib/jotai';
import { useAtomValue } from 'jotai';
import { showPrompt } from '../lib/prompt';
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
import { useFastMutation } from './useFastMutation';
export function useCreateEnvironment() {
return useFastMutation<string, unknown, Environment | null>({
mutationKey: ['create_environment'],
const workspaceId = useAtomValue(activeWorkspaceIdAtom);
return useFastMutation<string | null, unknown, Environment | null>({
mutationKey: ['create_environment', workspaceId],
mutationFn: async (baseEnvironment) => {
if (baseEnvironment == null) {
throw new Error('No base environment passed');
}
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
if (workspaceId == null) {
throw new Error('Cannot create environment when no active workspace');
}
@@ -28,17 +29,21 @@ export function useCreateEnvironment() {
defaultValue: 'My Environment',
confirmText: 'Create',
});
if (name == null) throw new Error('No name provided to create environment');
if (name == null) return null;
return createWorkspaceModel({
model: 'environment',
name,
variables: [],
workspaceId,
environmentId: baseEnvironment.id,
base: false,
});
},
onSuccess: async (environmentId) => {
if (environmentId == null) {
return; // Was not created
}
setWorkspaceSearchParams({ environment_id: environmentId });
},
});

View File

@@ -0,0 +1,25 @@
import type { EnvironmentVariable } from '@yaakapp-internal/models';
import { environmentsAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { useMemo } from 'react';
import { useEnvironmentsBreakdown } from './useEnvironmentsBreakdown';
export function useEnvironmentVariables(environmentId: string | null) {
const { baseEnvironment } = useEnvironmentsBreakdown();
const activeEnvironment =
useAtomValue(environmentsAtom).find((e) => e.id === environmentId) ?? null;
return useMemo(() => {
const varMap: Record<string, EnvironmentVariable> = {};
const allVariables = [
...(baseEnvironment?.variables ?? []),
...(activeEnvironment?.variables ?? []),
];
for (const v of allVariables) {
if (!v.enabled || !v.name) continue;
varMap[v.name] = v;
}
return Object.values(varMap);
}, [activeEnvironment, baseEnvironment]);
}

View File

@@ -5,9 +5,12 @@ import { useMemo } from 'react';
export function useEnvironmentsBreakdown() {
const allEnvironments = useAtomValue(environmentsAtom);
return useMemo(() => {
const baseEnvironment = allEnvironments.find((e) => e.environmentId == null) ?? null;
const subEnvironments =
allEnvironments.filter((e) => e.environmentId === (baseEnvironment?.id ?? 'n/a')) ?? [];
return { allEnvironments, baseEnvironment, subEnvironments };
const baseEnvironments = allEnvironments.filter((e) => e.base) ?? [];
const subEnvironments = allEnvironments.filter((e) => !e.base) ?? [];
const baseEnvironment = baseEnvironments[0] ?? null;
const otherBaseEnvironments =
baseEnvironments.filter((e) => e.id !== baseEnvironment?.id) ?? [];
return { allEnvironments, baseEnvironment, subEnvironments, otherBaseEnvironments };
}, [allEnvironments]);
}

View File

@@ -1,3 +1,4 @@
import { copyToClipboard } from '../lib/copy';
import { catppuccinMacchiato } from '../lib/theme/themes/catppuccin';
import { githubLight } from '../lib/theme/themes/github';
import { gruvboxDefault } from '../lib/theme/themes/gruvbox';
@@ -6,11 +7,9 @@ import { monokaiProDefault } from '../lib/theme/themes/monokai-pro';
import { rosePineDefault } from '../lib/theme/themes/rose-pine';
import { yaakDark } from '../lib/theme/themes/yaak';
import { getThemeCSS } from '../lib/theme/window';
import { useCopy } from './useCopy';
import { useListenToTauriEvent } from './useListenToTauriEvent';
export function useGenerateThemeCss() {
const copy = useCopy();
useListenToTauriEvent('generate_theme_css', () => {
const themesCss = [
yaakDark,
@@ -23,6 +22,6 @@ export function useGenerateThemeCss() {
]
.map(getThemeCSS)
.join('\n\n');
copy(themesCss);
copyToClipboard(themesCss);
});
}

View File

@@ -1,10 +1,6 @@
import { invoke } from '@tauri-apps/api/core';
import type { WebsocketConnection, WebsocketEvent } from '@yaakapp-internal/models';
import {
replaceModelsInStore,
websocketConnectionsAtom,
websocketEventsAtom,
} from '@yaakapp-internal/models';
import { replaceModelsInStore , websocketConnectionsAtom, websocketEventsAtom } from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
import { useEffect } from 'react';
import { atomWithKVStorage } from '../lib/atoms/atomWithKVStorage';
@@ -35,13 +31,6 @@ export const activeWebsocketConnectionAtom = atom<WebsocketConnection | null>((g
return activeConnections.find((c) => c.id === pinnedConnectionId) ?? activeConnections[0] ?? null;
});
export const activeWebsocketEventsAtom = atom(async (get) => {
const connection = get(activeWebsocketConnectionAtom);
return invoke<WebsocketEvent[]>('plugin:yaak-models|websocket_events', {
connectionId: connection?.id ?? 'n/a',
});
});
export function setPinnedWebsocketConnectionId(id: string | null) {
const activeRequestId = jotaiStore.get(activeRequestIdAtom);
const activeConnections = jotaiStore.get(activeWebsocketConnectionsAtom);