Show folders in sync confirm dialog

This commit is contained in:
Gregory Schier
2025-02-09 08:35:29 -08:00
parent 325c88f251
commit 3aaa0355e1
15 changed files with 58 additions and 37 deletions

View File

@@ -7,7 +7,7 @@ import { getActiveWorkspaceId } from '../hooks/useActiveWorkspace';
import { createFastMutation } from '../hooks/useFastMutation'; import { createFastMutation } from '../hooks/useFastMutation';
import { trackEvent } from '../lib/analytics'; import { trackEvent } from '../lib/analytics';
import { showConfirm } from '../lib/confirm'; import { showConfirm } from '../lib/confirm';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { resolvedModelNameWithFolders } from '../lib/resolvedModelName';
import { pluralizeCount } from '../lib/pluralize'; import { pluralizeCount } from '../lib/pluralize';
import { showPrompt } from '../lib/prompt'; import { showPrompt } from '../lib/prompt';
import { invokeCmd } from '../lib/tauri'; import { invokeCmd } from '../lib/tauri';
@@ -70,14 +70,15 @@ export const syncWorkspace = createFastMutation<
(o) => o.type === 'dbDelete' && o.model.model === 'workspace', (o) => o.type === 'dbDelete' && o.model.model === 'workspace',
); );
console.log('Filesystem changes detected', { dbOps, ops }); console.log('Directory changes detected', { dbOps, ops });
const confirmed = force const confirmed = force
? true ? true
: await showConfirm({ : await showConfirm({
id: 'commit-sync', id: 'commit-sync',
title: 'Filesystem Changes Detected', title: 'Changes Detected',
confirmText: 'Apply Changes', confirmText: 'Apply Changes',
color: isDeletingWorkspace ? 'danger' : 'primary',
description: ( description: (
<VStack space={3}> <VStack space={3}>
{isDeletingWorkspace && ( {isDeletingWorkspace && (
@@ -86,8 +87,8 @@ export const syncWorkspace = createFastMutation<
</Banner> </Banner>
)} )}
<p> <p>
{pluralizeCount('file', dbOps.length)} in the directory have changed. Do you want to {pluralizeCount('file', dbOps.length)} in the directory{' '}
apply the updates to your workspace? {dbOps.length === 1 ? 'has' : 'have'} changed. Do you want to update your workspace?
</p> </p>
<div className="overflow-y-auto max-h-[10rem]"> <div className="overflow-y-auto max-h-[10rem]">
<table className="w-full text-sm mb-auto min-w-full max-w-full divide-y divide-surface-highlight"> <table className="w-full text-sm mb-auto min-w-full max-w-full divide-y divide-surface-highlight">
@@ -105,15 +106,15 @@ export const syncWorkspace = createFastMutation<
if (op.type === 'dbCreate') { if (op.type === 'dbCreate') {
label = 'create'; label = 'create';
name = fallbackRequestName(op.fs.model); name = resolvedModelNameWithFolders(op.fs.model);
color = 'text-success'; color = 'text-success';
} else if (op.type === 'dbUpdate') { } else if (op.type === 'dbUpdate') {
label = 'update'; label = 'update';
name = fallbackRequestName(op.fs.model); name = resolvedModelNameWithFolders(op.fs.model);
color = 'text-info'; color = 'text-info';
} else if (op.type === 'dbDelete') { } else if (op.type === 'dbDelete') {
label = 'delete'; label = 'delete';
name = fallbackRequestName(op.model); name = resolvedModelNameWithFolders(op.model);
color = 'text-danger'; color = 'text-danger';
} else { } else {
return null; return null;

View File

@@ -4,7 +4,7 @@ import { InlineCode } from '../components/core/InlineCode';
import { createFastMutation } from '../hooks/useFastMutation'; import { createFastMutation } from '../hooks/useFastMutation';
import { trackEvent } from '../lib/analytics'; import { trackEvent } from '../lib/analytics';
import { showConfirmDelete } from '../lib/confirm'; import { showConfirmDelete } from '../lib/confirm';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { resolvedModelName } from '../lib/resolvedModelName';
export const deleteWebsocketRequest = createFastMutation({ export const deleteWebsocketRequest = createFastMutation({
mutationKey: ['delete_websocket_request'], mutationKey: ['delete_websocket_request'],
@@ -14,7 +14,7 @@ export const deleteWebsocketRequest = createFastMutation({
title: 'Delete WebSocket Request', title: 'Delete WebSocket Request',
description: ( description: (
<> <>
Permanently delete <InlineCode>{fallbackRequestName(request)}</InlineCode>? Permanently delete <InlineCode>{resolvedModelName(request)}</InlineCode>?
</> </>
), ),
}); });

View File

@@ -28,7 +28,7 @@ import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest';
import { useSidebarHidden } from '../hooks/useSidebarHidden'; import { useSidebarHidden } from '../hooks/useSidebarHidden';
import { useWorkspaces } from '../hooks/useWorkspaces'; import { useWorkspaces } from '../hooks/useWorkspaces';
import { showDialog, toggleDialog } from '../lib/dialog'; import { showDialog, toggleDialog } from '../lib/dialog';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { resolvedModelNameWithFolders } from '../lib/resolvedModelName';
import { router } from '../lib/router'; import { router } from '../lib/router';
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams'; import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
import { CookieDialog } from './CookieDialog'; import { CookieDialog } from './CookieDialog';
@@ -270,11 +270,11 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
for (const r of sortedRequests) { for (const r of sortedRequests) {
requestGroup.items.push({ requestGroup.items.push({
key: `switch-request-${r.id}`, key: `switch-request-${r.id}`,
searchText: fallbackRequestName(r), searchText: resolvedModelNameWithFolders(r),
label: ( label: (
<HStack space={2}> <HStack space={2}>
<HttpMethodTag className="text-text-subtlest" request={r} /> <HttpMethodTag className="text-text-subtlest" request={r} />
<div className="truncate">{fallbackRequestName(r)}</div> <div className="truncate">{resolvedModelNameWithFolders(r)}</div>
</HStack> </HStack>
), ),
onSelect: async () => { onSelect: async () => {

View File

@@ -15,7 +15,7 @@ import { useActiveRequest } from '../hooks/useActiveRequest';
import { useFolders } from '../hooks/useFolders'; import { useFolders } from '../hooks/useFolders';
import { useHttpRequests } from '../hooks/useHttpRequests'; import { useHttpRequests } from '../hooks/useHttpRequests';
import { capitalize } from '../lib/capitalize'; import { capitalize } from '../lib/capitalize';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { resolvedModelName } from '../lib/resolvedModelName';
import { Banner } from './core/Banner'; import { Banner } from './core/Banner';
import { Checkbox } from './core/Checkbox'; import { Checkbox } from './core/Checkbox';
import { Editor } from './core/Editor/Editor'; import { Editor } from './core/Editor/Editor';
@@ -386,7 +386,7 @@ function buildRequestBreadcrumbs(request: HttpRequest, folders: Folder[]): strin
}; };
next(); next();
return ancestors.map((a) => (a.model === 'folder' ? a.name : fallbackRequestName(a))); return ancestors.map((a) => (a.model === 'folder' ? a.name : resolvedModelName(a)));
} }
function CheckboxArg({ function CheckboxArg({

View File

@@ -11,7 +11,7 @@ import type {
import classNames from 'classnames'; import classNames from 'classnames';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { resolvedModelName } from '../lib/resolvedModelName';
import { showErrorToast, showToast } from '../lib/toast'; import { showErrorToast, showToast } from '../lib/toast';
import { Banner } from './core/Banner'; import { Banner } from './core/Banner';
import { Button } from './core/Button'; import { Button } from './core/Button';
@@ -249,7 +249,7 @@ function TreeNodeChildren({
) : ( ) : (
<span aria-hidden /> <span aria-hidden />
)} )}
<div className="truncate">{fallbackRequestName(node.model)}</div> <div className="truncate">{resolvedModelName(node.model)}</div>
{node.status.status !== 'current' && ( {node.status.status !== 'current' && (
<InlineCode <InlineCode
className={classNames( className={classNames(

View File

@@ -9,7 +9,7 @@ import type { ReflectResponseService } from '../hooks/useGrpc';
import { useHttpAuthenticationSummaries } from '../hooks/useHttpAuthentication'; import { useHttpAuthenticationSummaries } from '../hooks/useHttpAuthentication';
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest'; import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { resolvedModelName } from '../lib/resolvedModelName';
import { Button } from './core/Button'; import { Button } from './core/Button';
import { CountBadge } from './core/CountBadge'; import { CountBadge } from './core/CountBadge';
import { Icon } from './core/Icon'; import { Icon } from './core/Icon';
@@ -343,7 +343,7 @@ export function GrpcConnectionSetupPane({
defaultValue={activeRequest.name} defaultValue={activeRequest.name}
className="font-sans !text-xl !px-0" className="font-sans !text-xl !px-0"
containerClassName="border-0" containerClassName="border-0"
placeholder={fallbackRequestName(activeRequest)} placeholder={resolvedModelName(activeRequest)}
onChange={(name) => updateRequest.mutate({ id: activeRequest.id, update: { name } })} onChange={(name) => updateRequest.mutate({ id: activeRequest.id, update: { name } })}
/> />
<MarkdownEditor <MarkdownEditor

View File

@@ -20,7 +20,7 @@ import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest';
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest'; import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
import { deepEqualAtom } from '../lib/atoms'; import { deepEqualAtom } from '../lib/atoms';
import { languageFromContentType } from '../lib/contentType'; import { languageFromContentType } from '../lib/contentType';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { resolvedModelName } from '../lib/resolvedModelName';
import { generateId } from '../lib/generateId'; import { generateId } from '../lib/generateId';
import { import {
BODY_TYPE_BINARY, BODY_TYPE_BINARY,
@@ -468,7 +468,7 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }:
defaultValue={activeRequest.name} defaultValue={activeRequest.name}
className="font-sans !text-xl !px-0" className="font-sans !text-xl !px-0"
containerClassName="border-0" containerClassName="border-0"
placeholder={fallbackRequestName(activeRequest)} placeholder={resolvedModelName(activeRequest)}
onChange={(name) => updateRequest({ id: activeRequestId, update: { name } })} onChange={(name) => updateRequest({ id: activeRequestId, update: { name } })}
/> />
<MarkdownEditor <MarkdownEditor

View File

@@ -4,7 +4,7 @@ import { upsertWebsocketRequest } from '../commands/upsertWebsocketRequest';
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest'; import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest'; import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
import { useWorkspaces } from '../hooks/useWorkspaces'; import { useWorkspaces } from '../hooks/useWorkspaces';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { resolvedModelName } from '../lib/resolvedModelName';
import { router } from '../lib/router'; import { router } from '../lib/router';
import { showToast } from '../lib/toast'; import { showToast } from '../lib/toast';
import { Button } from './core/Button'; import { Button } from './core/Button';
@@ -59,7 +59,7 @@ export function MoveToWorkspaceDialog({ onDone, request, activeWorkspaceId }: Pr
id: 'workspace-moved', id: 'workspace-moved',
message: ( message: (
<> <>
<InlineCode>{fallbackRequestName(request)}</InlineCode> moved to{' '} <InlineCode>{resolvedModelName(request)}</InlineCode> moved to{' '}
<InlineCode> <InlineCode>
{workspaces.find((w) => w.id === selectedWorkspaceId)?.name ?? 'unknown'} {workspaces.find((w) => w.id === selectedWorkspaceId)?.name ?? 'unknown'}
</InlineCode> </InlineCode>

View File

@@ -6,7 +6,7 @@ import { useHotKey } from '../hooks/useHotKey';
import { useKeyboardEvent } from '../hooks/useKeyboardEvent'; import { useKeyboardEvent } from '../hooks/useKeyboardEvent';
import { useRecentRequests } from '../hooks/useRecentRequests'; import { useRecentRequests } from '../hooks/useRecentRequests';
import { requestsAtom } from '../hooks/useRequests'; import { requestsAtom } from '../hooks/useRequests';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { resolvedModelName } from '../lib/resolvedModelName';
import { jotaiStore } from '../lib/jotai'; import { jotaiStore } from '../lib/jotai';
import { router } from '../lib/router'; import { router } from '../lib/router';
import { Button } from './core/Button'; import { Button } from './core/Button';
@@ -57,7 +57,7 @@ export function RecentRequestsDropdown({ className }: Props) {
if (request === undefined) continue; if (request === undefined) continue;
recentRequestItems.push({ recentRequestItems.push({
label: fallbackRequestName(request), label: resolvedModelName(request),
leftSlot: <HttpMethodTag request={request} />, leftSlot: <HttpMethodTag request={request} />,
onSelect: async () => { onSelect: async () => {
await router.navigate({ await router.navigate({
@@ -94,7 +94,7 @@ export function RecentRequestsDropdown({ className }: Props) {
activeRequest == null && 'text-text-subtlest italic', activeRequest == null && 'text-text-subtlest italic',
)} )}
> >
{fallbackRequestName(activeRequest)} {resolvedModelName(activeRequest)}
</Button> </Button>
</Dropdown> </Dropdown>
); );

View File

@@ -21,7 +21,7 @@ import { useLatestWebsocketConnection } from '../hooks/useWebsocketConnections';
import { trackEvent } from '../lib/analytics'; import { trackEvent } from '../lib/analytics';
import { deepEqualAtom } from '../lib/atoms'; import { deepEqualAtom } from '../lib/atoms';
import { languageFromContentType } from '../lib/contentType'; import { languageFromContentType } from '../lib/contentType';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { resolvedModelName } from '../lib/resolvedModelName';
import { generateId } from '../lib/generateId'; import { generateId } from '../lib/generateId';
import { CountBadge } from './core/CountBadge'; import { CountBadge } from './core/CountBadge';
import { Editor } from './core/Editor/Editor'; import { Editor } from './core/Editor/Editor';
@@ -303,7 +303,7 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque
defaultValue={activeRequest.name} defaultValue={activeRequest.name}
className="font-sans !text-xl !px-0" className="font-sans !text-xl !px-0"
containerClassName="border-0" containerClassName="border-0"
placeholder={fallbackRequestName(activeRequest)} placeholder={resolvedModelName(activeRequest)}
onChange={(name) => upsertWebsocketRequest.mutate({ ...activeRequest, name })} onChange={(name) => upsertWebsocketRequest.mutate({ ...activeRequest, name })}
/> />
<MarkdownEditor <MarkdownEditor

View File

@@ -6,7 +6,7 @@ import { activeWorkspaceAtom } from '../../hooks/useActiveWorkspace';
import { foldersAtom } from '../../hooks/useFolders'; import { foldersAtom } from '../../hooks/useFolders';
import { requestsAtom } from '../../hooks/useRequests'; import { requestsAtom } from '../../hooks/useRequests';
import { deepEqualAtom } from '../../lib/atoms'; import { deepEqualAtom } from '../../lib/atoms';
import { fallbackRequestName } from '../../lib/fallbackRequestName'; import { resolvedModelName } from '../../lib/resolvedModelName';
import type { SidebarTreeNode } from './Sidebar'; import type { SidebarTreeNode } from './Sidebar';
export const sidebarSelectedIdAtom = atom<string | null>(null); export const sidebarSelectedIdAtom = atom<string | null>(null);
@@ -18,7 +18,7 @@ const allPotentialChildrenAtom = atom((get) => {
id: v.id, id: v.id,
model: v.model, model: v.model,
folderId: v.folderId, folderId: v.folderId,
name: fallbackRequestName(v), name: resolvedModelName(v),
workspaceId: v.workspaceId, workspaceId: v.workspaceId,
sortPriority: v.sortPriority, sortPriority: v.sortPriority,
})); }));

View File

@@ -2,7 +2,7 @@ import type { GrpcRequest } from '@yaakapp-internal/models';
import { InlineCode } from '../components/core/InlineCode'; import { InlineCode } from '../components/core/InlineCode';
import { trackEvent } from '../lib/analytics'; import { trackEvent } from '../lib/analytics';
import { showConfirmDelete } from '../lib/confirm'; import { showConfirmDelete } from '../lib/confirm';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { resolvedModelName } from '../lib/resolvedModelName';
import { invokeCmd } from '../lib/tauri'; import { invokeCmd } from '../lib/tauri';
import { useFastMutation } from './useFastMutation'; import { useFastMutation } from './useFastMutation';
@@ -15,7 +15,7 @@ export function useDeleteAnyGrpcRequest() {
title: 'Delete Request', title: 'Delete Request',
description: ( description: (
<> <>
Permanently delete <InlineCode>{fallbackRequestName(request)}</InlineCode>? Permanently delete <InlineCode>{resolvedModelName(request)}</InlineCode>?
</> </>
), ),
}); });

View File

@@ -2,7 +2,7 @@ import type { HttpRequest } from '@yaakapp-internal/models';
import { InlineCode } from '../components/core/InlineCode'; import { InlineCode } from '../components/core/InlineCode';
import { trackEvent } from '../lib/analytics'; import { trackEvent } from '../lib/analytics';
import { showConfirmDelete } from '../lib/confirm'; import { showConfirmDelete } from '../lib/confirm';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { resolvedModelName } from '../lib/resolvedModelName';
import { invokeCmd } from '../lib/tauri'; import { invokeCmd } from '../lib/tauri';
import { useFastMutation } from './useFastMutation'; import { useFastMutation } from './useFastMutation';
@@ -15,7 +15,7 @@ export function useDeleteAnyHttpRequest() {
title: 'Delete Request', title: 'Delete Request',
description: ( description: (
<> <>
Permanently delete <InlineCode>{fallbackRequestName(request)}</InlineCode>? Permanently delete <InlineCode>{resolvedModelName(request)}</InlineCode>?
</> </>
), ),
}); });

View File

@@ -1,7 +1,7 @@
import { emit } from '@tauri-apps/api/event'; import { emit } 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 } from 'react';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { resolvedModelName } from '../lib/resolvedModelName';
import { useActiveEnvironment } from './useActiveEnvironment'; import { useActiveEnvironment } from './useActiveEnvironment';
import { getActiveRequest } from './useActiveRequest'; import { getActiveRequest } from './useActiveRequest';
import { useActiveWorkspace } from './useActiveWorkspace'; import { useActiveWorkspace } from './useActiveWorkspace';
@@ -26,7 +26,7 @@ export function useSyncWorkspaceRequestTitle() {
const activeRequest = getActiveRequest(); const activeRequest = getActiveRequest();
if (activeRequest) { if (activeRequest) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
newTitle += ` ${fallbackRequestName(activeRequest)}`; newTitle += ` ${resolvedModelName(activeRequest)}`;
} }
if (appInfo.isDev) { if (appInfo.isDev) {

View File

@@ -1,6 +1,8 @@
import type { AnyModel } from '@yaakapp-internal/models'; import type { AnyModel } from '@yaakapp-internal/models';
import { foldersAtom } from '../hooks/useFolders';
import { jotaiStore } from './jotai';
export function fallbackRequestName(r: AnyModel | null): string { export function resolvedModelName(r: AnyModel | null): string {
if (r == null) return ''; if (r == null) return '';
if (!('url' in r) || r.model === 'plugin') { if (!('url' in r) || r.model === 'plugin') {
@@ -33,3 +35,21 @@ export function fallbackRequestName(r: AnyModel | null): string {
return withoutProto; return withoutProto;
} }
export function resolvedModelNameWithFolders(model: AnyModel | null): string {
if (model == null) return '';
const folders = jotaiStore.get(foldersAtom) ?? [];
const getParents = (m: AnyModel, names: string[]) => {
names = [...names, resolvedModelName(m)];
if ('folderId' in m) {
const parent = folders.find((f) => f.id === m.folderId);
if (parent) {
names = [resolvedModelName(parent), ...names];
}
}
return names;
};
return getParents(model, []).join(' / ');
}