Add the ability to duplicate folders (#144)

This commit is contained in:
Gregory Schier
2024-12-19 13:06:08 -08:00
committed by GitHub
parent 833dc7d3f7
commit 42bf016e90
8 changed files with 108 additions and 8 deletions

View File

@@ -55,9 +55,9 @@ use yaak_models::queries::{
delete_all_http_responses_for_request, delete_all_http_responses_for_workspace,
delete_cookie_jar, delete_environment, delete_folder, delete_grpc_connection,
delete_grpc_request, delete_http_request, delete_http_response, delete_plugin,
delete_workspace, duplicate_grpc_request, duplicate_http_request, generate_id,
generate_model_id, get_cookie_jar, get_environment, get_folder, get_grpc_connection,
get_grpc_request, get_http_request, get_http_response, get_key_value_raw,
delete_workspace, duplicate_folder, duplicate_grpc_request, duplicate_http_request,
generate_id, generate_model_id, get_cookie_jar, get_environment, get_folder,
get_grpc_connection, get_grpc_request, get_http_request, get_http_response, get_key_value_raw,
get_or_create_settings, get_plugin, get_workspace, list_cookie_jars, list_environments,
list_folders, list_grpc_connections_for_workspace, list_grpc_events, list_grpc_requests,
list_http_requests, list_http_responses_for_request, list_http_responses_for_workspace,
@@ -1242,6 +1242,15 @@ async fn cmd_duplicate_grpc_request(id: &str, w: WebviewWindow) -> Result<GrpcRe
duplicate_grpc_request(&w, id).await.map_err(|e| e.to_string())
}
#[tauri::command]
async fn cmd_duplicate_folder<R: Runtime>(
window: WebviewWindow<R>,
id: &str,
) -> Result<(), String> {
let folder = get_folder(&window, id).await.map_err(|e| e.to_string())?;
duplicate_folder(&window, &folder).await.map_err(|e| e.to_string())
}
#[tauri::command]
async fn cmd_create_http_request(
request: HttpRequest,
@@ -1738,6 +1747,7 @@ pub fn run() {
cmd_delete_send_history,
cmd_delete_workspace,
cmd_dismiss_notification,
cmd_duplicate_folder,
cmd_duplicate_grpc_request,
cmd_duplicate_http_request,
cmd_export_data,

View File

@@ -306,6 +306,7 @@ pub async fn duplicate_grpc_request<R: Runtime>(
return Err(ModelNotFound(id.to_string()));
}
};
request.sort_priority = request.sort_priority + 0.001;
request.id = "".to_string();
upsert_grpc_request(window, request).await
}
@@ -1108,9 +1109,79 @@ pub async fn duplicate_http_request<R: Runtime>(
Some(r) => r,
};
request.id = "".to_string();
request.sort_priority = request.sort_priority + 0.001;
upsert_http_request(window, request).await
}
pub async fn duplicate_folder<R: Runtime>(
window: &WebviewWindow<R>,
src_folder: &Folder,
) -> Result<()> {
let workspace_id = src_folder.workspace_id.as_str();
let http_requests = list_http_requests(window, workspace_id)
.await?
.into_iter()
.filter(|m| m.folder_id.as_ref() == Some(&src_folder.id));
let grpc_requests = list_grpc_requests(window, workspace_id)
.await?
.into_iter()
.filter(|m| m.folder_id.as_ref() == Some(&src_folder.id));
let folders = list_folders(window, workspace_id)
.await?
.into_iter()
.filter(|m| m.folder_id.as_ref() == Some(&src_folder.id));
let new_folder = upsert_folder(
window,
Folder {
id: "".into(),
sort_priority: src_folder.sort_priority + 0.001,
..src_folder.clone()
},
)
.await?;
for m in http_requests {
upsert_http_request(
window,
HttpRequest {
id: "".into(),
folder_id: Some(new_folder.id.clone()),
sort_priority: m.sort_priority + 0.001,
..m
},
)
.await?;
}
for m in grpc_requests {
upsert_grpc_request(
window,
GrpcRequest {
id: "".into(),
folder_id: Some(new_folder.id.clone()),
sort_priority: m.sort_priority + 0.001,
..m
},
)
.await?;
}
for m in folders {
// Recurse down
Box::pin(duplicate_folder(
window,
&Folder {
folder_id: Some(new_folder.id.clone()),
..m
},
))
.await?;
}
Ok(())
}
pub async fn upsert_http_request<R: Runtime>(
window: &WebviewWindow<R>,
r: HttpRequest,

View File

@@ -64,8 +64,7 @@ export function ExportDataDialog({
<tr>
<th className="w-6 min-w-0 py-2 text-left pl-1">
<Checkbox
checked={allSelected}
indeterminate={!allSelected && !noneSelected}
checked={!allSelected && !noneSelected ? 'indeterminate' : allSelected}
hideLabel
title="All workspaces"
onChange={handleToggleAll}

View File

@@ -138,7 +138,7 @@ export function GrpcConnectionSetupPane({
value: TAB_DESCRIPTION,
label: (
<div className="flex items-center">
Docs
Info
{activeRequest.description && <CountBadge count={true} />}
</div>
),

View File

@@ -129,7 +129,7 @@ export const RequestPane = memo(function RequestPane({
value: TAB_DESCRIPTION,
label: (
<div className="flex items-center">
Docs
Info
{activeRequest.description && <CountBadge count={true} />}
</div>
),

View File

@@ -22,6 +22,7 @@ import { useAppRoutes } from '../hooks/useAppRoutes';
import { useCreateDropdownItems } from '../hooks/useCreateDropdownItems';
import { useDeleteFolder } from '../hooks/useDeleteFolder';
import { useDeleteRequest } from '../hooks/useDeleteRequest';
import { useDuplicateFolder } from '../hooks/useDuplicateFolder';
import { useDuplicateGrpcRequest } from '../hooks/useDuplicateGrpcRequest';
import { useDuplicateHttpRequest } from '../hooks/useDuplicateHttpRequest';
import { useFolders } from '../hooks/useFolders';
@@ -699,6 +700,7 @@ function SidebarItem({
const deleteFolder = useDeleteFolder(itemId);
const deleteRequest = useDeleteRequest(itemId);
const renameRequest = useRenameRequest(itemId);
const duplicateFolder = useDuplicateFolder(itemId);
const duplicateHttpRequest = useDuplicateHttpRequest({ id: itemId, navigateAfter: true });
const duplicateGrpcRequest = useDuplicateGrpcRequest({ id: itemId, navigateAfter: true });
const sendRequest = useSendAnyHttpRequest();
@@ -802,6 +804,12 @@ function SidebarItem({
render: () => <FolderSettingsDialog folderId={itemId} />,
}),
},
{
key: 'duplicateFolder',
label: 'Duplicate',
leftSlot: <Icon icon="copy" />,
onSelect: () => duplicateFolder.mutate(),
},
{
key: 'delete-folder',
label: 'Delete',
@@ -877,7 +885,7 @@ function SidebarItem({
createDropdownItems,
deleteFolder,
deleteRequest,
dialog,
duplicateFolder,
duplicateGrpcRequest,
duplicateHttpRequest,
httpRequestActions,

View File

@@ -0,0 +1,11 @@
import { useMutation } from '@tanstack/react-query';
import { trackEvent } from '../lib/analytics';
import { invokeCmd } from '../lib/tauri';
export function useDuplicateFolder(id: string) {
return useMutation<void, string>({
mutationKey: ['duplicate_folder', id],
mutationFn: () => invokeCmd('cmd_duplicate_folder', { id }),
onSettled: () => trackEvent('folder', 'duplicate'),
});
}

View File

@@ -24,6 +24,7 @@ type TauriCmd =
| 'cmd_delete_http_response'
| 'cmd_delete_workspace'
| 'cmd_dismiss_notification'
| 'cmd_duplicate_folder'
| 'cmd_duplicate_grpc_request'
| 'cmd_duplicate_http_request'
| 'cmd_export_data'