Hopefully fix weird env routing issue

This commit is contained in:
Gregory Schier
2025-05-16 09:12:36 -07:00
parent 0be7d0283b
commit 73c366dc27
4 changed files with 77 additions and 75 deletions

View File

@@ -0,0 +1,51 @@
import { createWorkspaceModel, type Environment } from '@yaakapp-internal/models';
import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace';
import { createFastMutation } from '../hooks/useFastMutation';
import { jotaiStore } from '../lib/jotai';
import { showPrompt } from '../lib/prompt';
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
export const createEnvironmentAndActivate = createFastMutation<
string | null,
unknown,
Environment | null
>({
mutationKey: ['create_environment'],
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');
}
const name = await showPrompt({
id: 'new-environment',
title: 'New Environment',
description: 'Create multiple environments with different sets of variables',
label: 'Name',
placeholder: 'My Environment',
defaultValue: 'My Environment',
confirmText: 'Create',
});
if (name == null) return null;
return createWorkspaceModel({
model: 'environment',
name,
variables: [],
workspaceId,
base: false,
});
},
onSuccess: async (environmentId) => {
if (environmentId == null) {
return; // Was not created
}
console.log('NAVIGATING', jotaiStore.get(activeWorkspaceIdAtom), environmentId);
setWorkspaceSearchParams({ environment_id: environmentId });
},
});

View File

@@ -5,6 +5,7 @@ import { useAtomValue } from 'jotai';
import type { KeyboardEvent, ReactNode } from 'react'; import type { KeyboardEvent, ReactNode } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { createFolder } from '../commands/commands'; import { createFolder } from '../commands/commands';
import { createEnvironmentAndActivate } from '../commands/createEnvironment';
import { openSettings } from '../commands/openSettings'; import { openSettings } from '../commands/openSettings';
import { switchWorkspace } from '../commands/switchWorkspace'; import { switchWorkspace } from '../commands/switchWorkspace';
import { useActiveCookieJar } from '../hooks/useActiveCookieJar'; import { useActiveCookieJar } from '../hooks/useActiveCookieJar';
@@ -12,7 +13,6 @@ import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
import { useActiveRequest } from '../hooks/useActiveRequest'; import { useActiveRequest } from '../hooks/useActiveRequest';
import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace'; import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace';
import { useAllRequests } from '../hooks/useAllRequests'; import { useAllRequests } from '../hooks/useAllRequests';
import { useCreateEnvironment } from '../hooks/useCreateEnvironment';
import { useCreateWorkspace } from '../hooks/useCreateWorkspace'; import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
import { useDebouncedState } from '../hooks/useDebouncedState'; import { useDebouncedState } from '../hooks/useDebouncedState';
import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown'; import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown';
@@ -72,7 +72,6 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
const activeCookieJar = useActiveCookieJar(); const activeCookieJar = useActiveCookieJar();
const [recentRequests] = useRecentRequests(); const [recentRequests] = useRecentRequests();
const [, setSidebarHidden] = useSidebarHidden(); const [, setSidebarHidden] = useSidebarHidden();
const { mutate: createEnvironment } = useCreateEnvironment();
const { mutate: sendRequest } = useSendAnyHttpRequest(); const { mutate: sendRequest } = useSendAnyHttpRequest();
const workspaceCommands = useMemo<CommandPaletteItem[]>(() => { const workspaceCommands = useMemo<CommandPaletteItem[]>(() => {
@@ -139,7 +138,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
{ {
key: 'environment.create', key: 'environment.create',
label: 'Create Environment', label: 'Create Environment',
onSelect: () => createEnvironment(baseEnvironment), onSelect: () => createEnvironmentAndActivate.mutate(baseEnvironment),
}, },
{ {
key: 'sidebar.toggle', key: 'sidebar.toggle',
@@ -190,7 +189,6 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
activeEnvironment, activeEnvironment,
activeRequest, activeRequest,
baseEnvironment, baseEnvironment,
createEnvironment,
createWorkspace, createWorkspace,
httpRequestActions, httpRequestActions,
sendRequest, sendRequest,

View File

@@ -4,7 +4,7 @@ import type { GenericCompletionOption } from '@yaakapp-internal/plugins';
import classNames from 'classnames'; import classNames from 'classnames';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import React, { useCallback, useMemo, useState } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
import { useCreateEnvironment } from '../hooks/useCreateEnvironment'; import { createEnvironmentAndActivate } from '../commands/createEnvironment';
import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown'; import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown';
import { useIsEncryptionEnabled } from '../hooks/useIsEncryptionEnabled'; import { useIsEncryptionEnabled } from '../hooks/useIsEncryptionEnabled';
import { useKeyValue } from '../hooks/useKeyValue'; import { useKeyValue } from '../hooks/useKeyValue';
@@ -41,7 +41,6 @@ interface Props {
} }
export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) { export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
const createEnvironment = useCreateEnvironment();
const { baseEnvironment, otherBaseEnvironments, subEnvironments, allEnvironments } = const { baseEnvironment, otherBaseEnvironments, subEnvironments, allEnvironments } =
useEnvironmentsBreakdown(); useEnvironmentsBreakdown();
const [selectedEnvironmentId, setSelectedEnvironmentId] = useState<string | null>( const [selectedEnvironmentId, setSelectedEnvironmentId] = useState<string | null>(
@@ -55,7 +54,7 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
const handleCreateEnvironment = async () => { const handleCreateEnvironment = async () => {
if (baseEnvironment == null) return; if (baseEnvironment == null) return;
const id = await createEnvironment.mutateAsync(baseEnvironment); const id = await createEnvironmentAndActivate.mutateAsync(baseEnvironment);
if (id != null) setSelectedEnvironmentId(id); if (id != null) setSelectedEnvironmentId(id);
}; };
@@ -162,30 +161,30 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
}; };
const EnvironmentEditor = function ({ const EnvironmentEditor = function ({
environment: activeEnvironment, environment: selectedEnvironment,
className, className,
}: { }: {
environment: Environment; environment: Environment;
className?: string; className?: string;
}) { }) {
const activeWorkspaceId = activeEnvironment.workspaceId; const workspaceId = selectedEnvironment.workspaceId;
const isEncryptionEnabled = useIsEncryptionEnabled(); const isEncryptionEnabled = useIsEncryptionEnabled();
const valueVisibility = useKeyValue<boolean>({ const valueVisibility = useKeyValue<boolean>({
namespace: 'global', namespace: 'global',
key: ['environmentValueVisibility', activeWorkspaceId], key: ['environmentValueVisibility', workspaceId],
fallback: false, fallback: false,
}); });
const { allEnvironments } = useEnvironmentsBreakdown(); const { allEnvironments } = useEnvironmentsBreakdown();
const handleChange = useCallback( const handleChange = useCallback(
(variables: PairWithId[]) => patchModel(activeEnvironment, { variables }), (variables: PairWithId[]) => patchModel(selectedEnvironment, { variables }),
[activeEnvironment], [selectedEnvironment],
); );
const [forceUpdateKey, regenerateForceUpdateKey] = useRandomKey(); const [forceUpdateKey, regenerateForceUpdateKey] = useRandomKey();
// Gather a list of env names from other environments to help the user get them aligned // Gather a list of env names from other environments to help the user get them aligned
const nameAutocomplete = useMemo<GenericCompletionConfig>(() => { const nameAutocomplete = useMemo<GenericCompletionConfig>(() => {
const options: GenericCompletionOption[] = []; const options: GenericCompletionOption[] = [];
if (activeEnvironment.base) { if (selectedEnvironment.base) {
return { options }; return { options };
} }
@@ -195,7 +194,7 @@ const EnvironmentEditor = function ({
const containingEnvs = allEnvironments.filter((e) => const containingEnvs = allEnvironments.filter((e) =>
e.variables.some((v) => v.name === name), e.variables.some((v) => v.name === name),
); );
const isAlreadyInActive = containingEnvs.find((e) => e.id === activeEnvironment.id); const isAlreadyInActive = containingEnvs.find((e) => e.id === selectedEnvironment.id);
if (isAlreadyInActive) continue; if (isAlreadyInActive) continue;
options.push({ options.push({
label: name, label: name,
@@ -204,7 +203,7 @@ const EnvironmentEditor = function ({
}); });
} }
return { options }; return { options };
}, [activeEnvironment.base, activeEnvironment.id, allEnvironments]); }, [selectedEnvironment.base, selectedEnvironment.id, allEnvironments]);
const validateName = useCallback((name: string) => { const validateName = useCallback((name: string) => {
// Empty just means the variable doesn't have a name yet and is unusable // Empty just means the variable doesn't have a name yet and is unusable
@@ -217,11 +216,11 @@ const EnvironmentEditor = function ({
if (!isEncryptionEnabled) { if (!isEncryptionEnabled) {
return true; return true;
} else { } else {
return !activeEnvironment.variables.every( return !selectedEnvironment.variables.every(
(v) => v.value === '' || analyzeTemplate(v.value) !== 'insecure', (v) => v.value === '' || analyzeTemplate(v.value) !== 'insecure',
); );
} }
}, [activeEnvironment.variables, isEncryptionEnabled]); }, [selectedEnvironment.variables, isEncryptionEnabled]);
const encryptEnvironment = (environment: Environment) => { const encryptEnvironment = (environment: Environment) => {
withEncryptionEnabled(async () => { withEncryptionEnabled(async () => {
@@ -238,10 +237,10 @@ const EnvironmentEditor = function ({
return ( return (
<VStack space={4} className={classNames(className, 'pl-4')}> <VStack space={4} className={classNames(className, 'pl-4')}>
<Heading className="w-full flex items-center gap-0.5"> <Heading className="w-full flex items-center gap-0.5">
<div className="mr-2">{activeEnvironment?.name}</div> <div className="mr-2">{selectedEnvironment?.name}</div>
{isEncryptionEnabled ? ( {isEncryptionEnabled ? (
promptToEncrypt ? ( promptToEncrypt ? (
<BadgeButton color="notice" onClick={() => encryptEnvironment(activeEnvironment)}> <BadgeButton color="notice" onClick={() => encryptEnvironment(selectedEnvironment)}>
Encrypt All Variables Encrypt All Variables
</BadgeButton> </BadgeButton>
) : ( ) : (
@@ -257,9 +256,9 @@ const EnvironmentEditor = function ({
</> </>
)} )}
</Heading> </Heading>
{activeEnvironment.public && promptToEncrypt && ( {selectedEnvironment.public && promptToEncrypt && (
<DismissibleBanner <DismissibleBanner
id={`warn-unencrypted-${activeEnvironment.id}`} id={`warn-unencrypted-${selectedEnvironment.id}`}
color="notice" color="notice"
className="mr-3" className="mr-3"
> >
@@ -277,11 +276,15 @@ const EnvironmentEditor = function ({
valueType={valueType} valueType={valueType}
valueAutocompleteVariables valueAutocompleteVariables
valueAutocompleteFunctions valueAutocompleteFunctions
forcedEnvironmentId={activeEnvironment.id} forceUpdateKey={`${selectedEnvironment.id}::${forceUpdateKey}`}
forceUpdateKey={`${activeEnvironment.id}::${forceUpdateKey}`} pairs={selectedEnvironment.variables}
pairs={activeEnvironment.variables}
onChange={handleChange} onChange={handleChange}
stateKey={`environment.${activeEnvironment.id}`} stateKey={`environment.${selectedEnvironment.id}`}
forcedEnvironmentId={
// Editing the base environment should resolve variables using the active environment.
// Editing a sub environment should resolve variables as if it's the active environment
selectedEnvironment.base ? undefined : selectedEnvironment.id
}
/> />
</div> </div>
</VStack> </VStack>

View File

@@ -1,50 +0,0 @@
import type { Environment } from '@yaakapp-internal/models';
import { createWorkspaceModel } from '@yaakapp-internal/models';
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() {
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');
}
if (workspaceId == null) {
throw new Error('Cannot create environment when no active workspace');
}
const name = await showPrompt({
id: 'new-environment',
title: 'New Environment',
description: 'Create multiple environments with different sets of variables',
label: 'Name',
placeholder: 'My Environment',
defaultValue: 'My Environment',
confirmText: 'Create',
});
if (name == null) return null;
return createWorkspaceModel({
model: 'environment',
name,
variables: [],
workspaceId,
base: false,
});
},
onSuccess: async (environmentId) => {
if (environmentId == null) {
return; // Was not created
}
setWorkspaceSearchParams({ environment_id: environmentId });
},
});
}