mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-06-30 11:23:33 +00:00
[ENG-1365] Move to Trash (#2318)
* Working System Trash Connection * small changes and prettier --------- Co-authored-by: Utku Bakir <74243531+utkubakir@users.noreply.github.com>
This commit is contained in:
parent
9f5396133b
commit
66063d22c2
|
@ -172,6 +172,7 @@ Also ensure that Rosetta is installed, as a few of our dependencies require it.
|
|||
#### `ModuleNotFoundError: No module named 'distutils'`
|
||||
|
||||
If you run into this issue, or some other error involving `node-gyp`:
|
||||
|
||||
```
|
||||
File "pnpm@8.15.6/node_modules/pnpm/dist/node_modules/node-gyp/gyp/gyp_main.py", line 42, in <module>
|
||||
import gyp # noqa: E402
|
||||
|
|
26
Cargo.lock
generated
26
Cargo.lock
generated
|
@ -8276,6 +8276,7 @@ dependencies = [
|
|||
"tracing-appender",
|
||||
"tracing-subscriber",
|
||||
"tracing-test",
|
||||
"trash",
|
||||
"uuid",
|
||||
"webp",
|
||||
]
|
||||
|
@ -10531,6 +10532,22 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trash"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a1a7a9a17d3b004898be42be29a4c18d5a4cf008b5cdf72d69b1945dfcb158a"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"libc",
|
||||
"log",
|
||||
"objc",
|
||||
"once_cell",
|
||||
"scopeguard",
|
||||
"url",
|
||||
"windows 0.44.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "treediff"
|
||||
version = "4.0.2"
|
||||
|
@ -11267,6 +11284,15 @@ dependencies = [
|
|||
"windows_x86_64_msvc 0.39.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b"
|
||||
dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
|
|
|
@ -132,6 +132,7 @@ static_assertions = "1.1.0"
|
|||
sysinfo = "0.29.10"
|
||||
tar = "0.4.40"
|
||||
tower-service = "0.3.2"
|
||||
trash = "4.1.0"
|
||||
|
||||
# Override features of transitive dependencies
|
||||
[dependencies.openssl]
|
||||
|
|
|
@ -27,6 +27,7 @@ use specta::Type;
|
|||
use tokio::{fs, io};
|
||||
use tokio_stream::{wrappers::ReadDirStream, StreamExt};
|
||||
use tracing::{error, warn};
|
||||
use trash;
|
||||
|
||||
use super::{
|
||||
files::{create_directory, FromPattern},
|
||||
|
@ -147,6 +148,35 @@ pub(crate) fn mount() -> AlphaRouter<Ctx> {
|
|||
Ok(())
|
||||
})
|
||||
})
|
||||
.procedure("moveToTrash", {
|
||||
R.with2(library())
|
||||
.mutation(|(_, library), paths: Vec<PathBuf>| async move {
|
||||
paths
|
||||
.into_iter()
|
||||
.map(|path| async move {
|
||||
match fs::metadata(&path).await {
|
||||
Ok(_) => {
|
||||
trash::delete(&path).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(()),
|
||||
Err(e) => Err(FileIOError::from((
|
||||
path,
|
||||
e,
|
||||
"Failed to get file metadata for deletion",
|
||||
))),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.try_join()
|
||||
.await?;
|
||||
|
||||
invalidate_query!(library, "search.ephemeralPaths");
|
||||
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
.procedure("copyFiles", {
|
||||
R.with2(library())
|
||||
.mutation(|(_, library), args: EphemeralFileSystemOps| async move {
|
||||
|
|
|
@ -44,6 +44,7 @@ use serde::{Deserialize, Serialize};
|
|||
use specta::Type;
|
||||
use tokio::{fs, io, task::spawn_blocking};
|
||||
use tracing::{error, warn};
|
||||
use trash;
|
||||
|
||||
use super::{Ctx, R};
|
||||
|
||||
|
@ -515,6 +516,53 @@ pub(crate) fn mount() -> AlphaRouter<Ctx> {
|
|||
}
|
||||
})
|
||||
})
|
||||
.procedure("moveToTrash", {
|
||||
R.with2(library())
|
||||
.mutation(|(node, library), args: OldFileDeleterJobInit| async move {
|
||||
match args.file_path_ids.len() {
|
||||
0 => Ok(()),
|
||||
1 => {
|
||||
let (maybe_location, maybe_file_path) = library
|
||||
.db
|
||||
._batch((
|
||||
library
|
||||
.db
|
||||
.location()
|
||||
.find_unique(location::id::equals(args.location_id))
|
||||
.select(location::select!({ path })),
|
||||
library
|
||||
.db
|
||||
.file_path()
|
||||
.find_unique(file_path::id::equals(args.file_path_ids[0]))
|
||||
.select(file_path_to_isolate::select()),
|
||||
))
|
||||
.await?;
|
||||
|
||||
let location_path = maybe_location
|
||||
.ok_or(LocationError::IdNotFound(args.location_id))?
|
||||
.path
|
||||
.ok_or(LocationError::MissingPath(args.location_id))?;
|
||||
|
||||
let file_path = maybe_file_path.ok_or(LocationError::FilePath(
|
||||
FilePathError::IdNotFound(args.file_path_ids[0]),
|
||||
))?;
|
||||
|
||||
let full_path = Path::new(&location_path).join(
|
||||
IsolatedFilePathData::try_from(&file_path)
|
||||
.map_err(LocationError::MissingField)?,
|
||||
);
|
||||
|
||||
trash::delete(&full_path).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
_ => Job::new(args)
|
||||
.spawn(&node, &library)
|
||||
.await
|
||||
.map_err(Into::into),
|
||||
}
|
||||
})
|
||||
})
|
||||
.procedure("convertImage", {
|
||||
#[derive(Type, Deserialize)]
|
||||
struct ConvertImageArgs {
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import { AppWindow, ArrowSquareOut, CaretRight, ClipboardText } from '@phosphor-icons/react';
|
||||
import clsx from 'clsx';
|
||||
import { memo, useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { createSearchParams } from 'react-router-dom';
|
||||
import {
|
||||
getExplorerItemData,
|
||||
getIndexedItemFilePath,
|
||||
|
@ -6,13 +10,9 @@ import {
|
|||
useLibraryQuery
|
||||
} from '@sd/client';
|
||||
import { ContextMenu } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
import { memo, useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { createSearchParams } from 'react-router-dom';
|
||||
import { useTabsContext } from '~/TabsContext';
|
||||
import { Icon } from '~/components';
|
||||
import { useIsDark, useLocale, useOperatingSystem } from '~/hooks';
|
||||
import { useTabsContext } from '~/TabsContext';
|
||||
import { usePlatform } from '~/util/Platform';
|
||||
|
||||
import { useExplorerContext } from './Context';
|
||||
|
@ -240,7 +240,7 @@ const Path = ({ path, onClick, disabled, locationPath }: PathProps) => {
|
|||
<ContextMenu.Item
|
||||
onClick={() => navigator.clipboard.writeText(osPath)}
|
||||
icon={ClipboardText}
|
||||
label={t("copy_as_path")}
|
||||
label={t('copy_as_path')}
|
||||
/>
|
||||
</ContextMenu.Root>
|
||||
);
|
||||
|
|
|
@ -47,6 +47,8 @@ export default (props: Props) => {
|
|||
const { t } = useLocale();
|
||||
const deleteFile = useLibraryMutation('files.deleteFiles');
|
||||
const deleteEphemeralFile = useLibraryMutation('ephemeralFiles.deleteFiles');
|
||||
const moveToTrashFile = useLibraryMutation('files.moveToTrash');
|
||||
const moveToTrashEphemeralFile = useLibraryMutation('ephemeralFiles.moveToTrash');
|
||||
|
||||
const form = useZodForm();
|
||||
const { dirCount = 0, fileCount = 0, indexedArgs, ephemeralArgs } = props;
|
||||
|
@ -76,23 +78,46 @@ export default (props: Props) => {
|
|||
await deleteEphemeralFile.mutateAsync(paths);
|
||||
}
|
||||
})}
|
||||
onSubmitSecond={form.handleSubmit(async () => {
|
||||
if (indexedArgs != undefined) {
|
||||
console.log(
|
||||
'DEBUG: DeleteDialog.tsx: onSubmitSecond (Move to Trash) -> Indexed Files'
|
||||
);
|
||||
const { locationId, rescan, pathIds } = indexedArgs;
|
||||
await moveToTrashFile.mutateAsync({
|
||||
location_id: locationId,
|
||||
file_path_ids: pathIds
|
||||
});
|
||||
|
||||
rescan?.();
|
||||
}
|
||||
|
||||
if (ephemeralArgs != undefined) {
|
||||
console.log(
|
||||
'DEBUG: DeleteDialog.tsx: onSubmitSecond (Move to Trash) -> Ephemeral Files'
|
||||
);
|
||||
const { paths } = ephemeralArgs;
|
||||
await moveToTrashEphemeralFile.mutateAsync(paths);
|
||||
}
|
||||
})}
|
||||
icon={<Icon theme="light" name={icon} size={28} />}
|
||||
dialog={useDialog(props)}
|
||||
title={t('delete_dialog_title', { prefix, type })}
|
||||
description={description}
|
||||
loading={deleteFile.isLoading}
|
||||
ctaLabel={t('delete')}
|
||||
ctaLabel={t('delete_forever')}
|
||||
ctaSecondLabel={t('move_to_trash')}
|
||||
ctaDanger
|
||||
className="w-[200px]"
|
||||
>
|
||||
<Tooltip label={t('coming_soon')}>
|
||||
{/* <Tooltip label={t('coming_soon')}>
|
||||
<div className="flex items-center pt-2 opacity-50">
|
||||
<CheckBox disabled className="!mt-0" />
|
||||
<p className="text-sm text-ink-dull">
|
||||
Delete all matching {type.endsWith('s') ? type : type + 's'}
|
||||
</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</Tooltip> */}
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
"delete_rule": "Выдаліць правіла",
|
||||
"delete_tag": "Выдаліць тэг",
|
||||
"delete_tag_description": "Вы ўпэўнены, што хочаце выдаліць гэты тэг? Гэта дзеянне няможна скасаваць, і тэгнутые файлы будуць адлучаны.",
|
||||
"delete_warning": "Папярэджанне: гэта выдаліць ваш {{type}} назаўжды, у нас пакуль няма сметніцы.",
|
||||
"delete_warning": "гэта выдаліць ваш {{type}} назаўжды, у нас пакуль няма сметніцы.",
|
||||
"description": "Апісанне",
|
||||
"deselect": "Скасаваць выбар",
|
||||
"details": "Падрабязней",
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
"delete_rule": "Regel löschen",
|
||||
"delete_tag": "Tag löschen",
|
||||
"delete_tag_description": "Sind Sie sicher, dass Sie diesen Tag löschen möchten? Dies kann nicht rückgängig gemacht werden, und getaggte Dateien werden nicht mehr verlinkt.",
|
||||
"delete_warning": "Warnung: Dies wird Ihre {{type}} für immer löschen, wir haben noch keinen Papierkorb...",
|
||||
"delete_warning": "Dies wird Ihre {{type}} für immer löschen, wir haben noch keinen Papierkorb...",
|
||||
"description": "Beschreibung",
|
||||
"deselect": "Abwählen",
|
||||
"details": "Details",
|
||||
|
|
|
@ -64,6 +64,10 @@
|
|||
"copy_path_to_clipboard": "Copy path to clipboard",
|
||||
"copy_success": "Items copied",
|
||||
"create": "Create",
|
||||
"create_file_error": "Error creating file",
|
||||
"create_file_success": "Created new file: {{name}}",
|
||||
"create_folder_error": "Error creating folder",
|
||||
"create_folder_success": "Created new folder: {{name}}",
|
||||
"create_library": "Create a Library",
|
||||
"create_library_description": "Libraries are a secure, on-device database. Your files remain where they are, the Library catalogs them and stores all Spacedrive related data.",
|
||||
"create_new_library": "Create new library",
|
||||
|
@ -82,11 +86,16 @@
|
|||
"cut_object": "Cut object",
|
||||
"cut_success": "Items cut",
|
||||
"data_folder": "Data Folder",
|
||||
"date_accessed": "Date Accessed",
|
||||
"date_created": "Date Created",
|
||||
"date_indexed": "Date Indexed",
|
||||
"date_modified": "Date Modified",
|
||||
"debug_mode": "Debug mode",
|
||||
"debug_mode_description": "Enable extra debugging features within the app.",
|
||||
"default": "Default",
|
||||
"delete": "Delete",
|
||||
"delete_dialog_title": "Delete {{prefix}} {{type}}",
|
||||
"delete_forever": "Delete Forever",
|
||||
"delete_info": "This will not delete the actual folder on disk. Preview media will be deleted.",
|
||||
"delete_library": "Delete Library",
|
||||
"delete_library_description": "This is permanent, your files will not be deleted, only the Spacedrive library.",
|
||||
|
@ -96,7 +105,7 @@
|
|||
"delete_rule": "Delete rule",
|
||||
"delete_tag": "Delete Tag",
|
||||
"delete_tag_description": "Are you sure you want to delete this tag? This cannot be undone and tagged files will be unlinked.",
|
||||
"delete_warning": "Warning: This will delete your {{type}} forever, we don't have a trash can yet...",
|
||||
"delete_warning": "This will delete your {{type}}. This action cannot be undone as of right now. If you Move to Trash, you can restore it later. If you Delete Forever, it will be gone forever.",
|
||||
"description": "Description",
|
||||
"deselect": "Deselect",
|
||||
"details": "Details",
|
||||
|
@ -121,7 +130,10 @@
|
|||
"edit": "Edit",
|
||||
"edit_library": "Edit Library",
|
||||
"edit_location": "Edit Location",
|
||||
"empty_file": "Empty file",
|
||||
"enable_networking": "Enable Networking",
|
||||
"enable_networking_description": "Allow your node to communicate with other Spacedrive nodes around you.",
|
||||
"enable_networking_description_required": "Required for library sync or Spacedrop!",
|
||||
"encrypt": "Encrypt",
|
||||
"encrypt_library": "Encrypt Library",
|
||||
"encrypt_library_coming_soon": "Library encryption coming soon",
|
||||
|
@ -200,6 +212,7 @@
|
|||
"hide_in_sidebar_description": "Prevent this tag from showing in the sidebar of the app.",
|
||||
"hide_location_from_view": "Hide location and contents from view",
|
||||
"home": "Home",
|
||||
"icon_size": "Icon size",
|
||||
"image_labeler_ai_model": "Image label recognition AI model",
|
||||
"image_labeler_ai_model_description": "The model used to recognize objects in images. Larger models are more accurate but slower.",
|
||||
"import": "Import",
|
||||
|
@ -211,8 +224,6 @@
|
|||
"install_update": "Install Update",
|
||||
"installed": "Installed",
|
||||
"item_size": "Item size",
|
||||
"icon_size": "Icon size",
|
||||
"text_size": "Text size",
|
||||
"item_with_count_one": "{{count}} item",
|
||||
"item_with_count_other": "{{count}} items",
|
||||
"job_has_been_canceled": "Job has been canceled.",
|
||||
|
@ -250,6 +261,7 @@
|
|||
"location_connected_tooltip": "Location is being watched for changes",
|
||||
"location_disconnected_tooltip": "Location is not being watched for changes",
|
||||
"location_display_name_info": "The name of this Location, this is what will be displayed in the sidebar. Will not rename the actual folder on disk.",
|
||||
"location_empty_notice_message": "No files found here",
|
||||
"location_is_already_linked": "Location is already linked",
|
||||
"location_path_info": "The path to this Location, this is where the files will be stored on disk.",
|
||||
"location_type": "Location Type",
|
||||
|
@ -259,9 +271,11 @@
|
|||
"locations": "Locations",
|
||||
"locations_description": "Manage your storage locations.",
|
||||
"lock": "Lock",
|
||||
"log_in": "Log in",
|
||||
"log_in_with_browser": "Log in with browser",
|
||||
"log_out": "Log out",
|
||||
"logged_in_as": "Logged in as {{email}}",
|
||||
"logging_in": "Logging in...",
|
||||
"logout": "Logout",
|
||||
"manage_library": "Manage Library",
|
||||
"managed": "Managed",
|
||||
|
@ -279,6 +293,7 @@
|
|||
"move_back_within_quick_preview": "Move back within quick preview",
|
||||
"move_files": "Move Files",
|
||||
"move_forward_within_quick_preview": "Move forward within quick preview",
|
||||
"move_to_trash": "Move to Trash",
|
||||
"name": "Name",
|
||||
"navigate_back": "Navigate back",
|
||||
"navigate_backwards": "Navigate backwards",
|
||||
|
@ -296,19 +311,14 @@
|
|||
"networking_port_description": "The port for Spacedrive's Peer-to-peer networking to communicate on. You should leave this disabled unless you have a restrictive firewall. Do not expose to the internet!",
|
||||
"new": "New",
|
||||
"new_folder": "Folder",
|
||||
"text_file": "Text File",
|
||||
"empty_file": "Empty file",
|
||||
"create_folder_error": "Error creating folder",
|
||||
"create_file_error": "Error creating file",
|
||||
"create_folder_success": "Created new folder: {{name}}",
|
||||
"create_file_success": "Created new file: {{name}}",
|
||||
"new_library": "New library",
|
||||
"new_location": "New location",
|
||||
"new_location_web_description": "As you are using the browser version of Spacedrive you will (for now) need to specify an absolute URL of a directory local to the remote node.",
|
||||
"new_tab": "New Tab",
|
||||
"new_tag": "New tag",
|
||||
"new_update_available": "New Update Available!",
|
||||
"location_empty_notice_message": "No files found here",
|
||||
"no_favorite_items": "No favorite items",
|
||||
"no_items_found": "No items found",
|
||||
"no_jobs": "No jobs.",
|
||||
"no_labels": "No labels",
|
||||
"no_nodes_found": "No Spacedrive nodes were found.",
|
||||
|
@ -326,6 +336,7 @@
|
|||
"online": "Online",
|
||||
"open": "Open",
|
||||
"open_file": "Open File",
|
||||
"open_in_new_tab": "Open in new tab",
|
||||
"open_new_location_once_added": "Open new location once added",
|
||||
"open_new_tab": "Open new tab",
|
||||
"open_object": "Open object",
|
||||
|
@ -347,12 +358,14 @@
|
|||
"pause": "Pause",
|
||||
"peers": "Peers",
|
||||
"people": "People",
|
||||
"pin": "Pin",
|
||||
"privacy": "Privacy",
|
||||
"privacy_description": "Spacedrive is built for privacy, that's why we're open source and local first. So we'll make it very clear what data is shared with us.",
|
||||
"quick_preview": "Quick Preview",
|
||||
"quick_view": "Quick view",
|
||||
"recent_jobs": "Recent Jobs",
|
||||
"recents": "Recents",
|
||||
"recents_notice_message": "Recents are created when you open a file.",
|
||||
"regen_labels": "Regen Labels",
|
||||
"regen_thumbnails": "Regen Thumbnails",
|
||||
"regenerate_thumbs": "Regenerate Thumbs",
|
||||
|
@ -364,6 +377,7 @@
|
|||
"rename": "Rename",
|
||||
"rename_object": "Rename object",
|
||||
"replica": "Replica",
|
||||
"rescan": "Rescan",
|
||||
"rescan_directory": "Rescan Directory",
|
||||
"rescan_location": "Rescan Location",
|
||||
"reset": "Reset",
|
||||
|
@ -377,6 +391,7 @@
|
|||
"save": "Save",
|
||||
"save_changes": "Save Changes",
|
||||
"saved_searches": "Saved Searches",
|
||||
"search": "Search",
|
||||
"search_extensions": "Search extensions",
|
||||
"search_for_files_and_actions": "Search for files and actions...",
|
||||
"secure_delete": "Secure delete",
|
||||
|
@ -399,9 +414,9 @@
|
|||
"show_slider": "Show slider",
|
||||
"size": "Size",
|
||||
"size_b": "B",
|
||||
"size_gb": "GB",
|
||||
"size_kb": "kB",
|
||||
"size_mb": "MB",
|
||||
"size_gb": "GB",
|
||||
"size_tb": "TB",
|
||||
"skip_login": "Skip login",
|
||||
"sort_by": "Sort by",
|
||||
|
@ -429,9 +444,12 @@
|
|||
"sync_with_library_description": "If enabled, your keybinds will be synced with library, otherwise they will apply only to this client.",
|
||||
"tags": "Tags",
|
||||
"tags_description": "Manage your tags.",
|
||||
"tags_notice_message": "No items assigned to this tag.",
|
||||
"telemetry_description": "Toggle ON to provide developers with detailed usage and telemetry data to enhance the app. Toggle OFF to send only basic data: your activity status, app version, core version, and platform (e.g., mobile, web, or desktop).",
|
||||
"telemetry_title": "Share Additional Telemetry and Usage Data",
|
||||
"temperature": "Temperature",
|
||||
"text_file": "Text File",
|
||||
"text_size": "Text size",
|
||||
"thank_you_for_your_feedback": "Thanks for your feedback!",
|
||||
"thumbnailer_cpu_usage": "Thumbnailer CPU usage",
|
||||
"thumbnailer_cpu_usage_description": "Limit how much CPU the thumbnailer can use for background processing.",
|
||||
|
@ -462,21 +480,5 @@
|
|||
"your_account": "Your account",
|
||||
"your_account_description": "Spacedrive account and information.",
|
||||
"your_local_network": "Your Local Network",
|
||||
"your_privacy": "Your Privacy",
|
||||
"pin": "Pin",
|
||||
"rescan": "Rescan",
|
||||
"open_in_new_tab": "Open in new tab",
|
||||
"enable_networking_description": "Allow your node to communicate with other Spacedrive nodes around you.",
|
||||
"enable_networking_description_required": "Required for library sync or Spacedrop!",
|
||||
"log_in": "Log in",
|
||||
"logging_in": "Logging in...",
|
||||
"no_favorite_items": "No favorite items",
|
||||
"tags_notice_message": "No items assigned to this tag.",
|
||||
"recents_notice_message": "Recents are created when you open a file.",
|
||||
"no_items_found": "No items found",
|
||||
"search": "Search",
|
||||
"date_created": "Date Created",
|
||||
"date_modified": "Date Modified",
|
||||
"date_indexed": "Date Indexed",
|
||||
"date_accessed": "Date Accessed"
|
||||
}
|
||||
"your_privacy": "Your Privacy"
|
||||
}
|
|
@ -104,7 +104,7 @@
|
|||
"delete_rule": "Eliminar regla",
|
||||
"delete_tag": "Eliminar Etiqueta",
|
||||
"delete_tag_description": "¿Estás seguro de que quieres eliminar esta etiqueta? Esto no se puede deshacer y los archivos etiquetados serán desvinculados.",
|
||||
"delete_warning": "Advertencia: Esto eliminará tu {{type}} para siempre, aún no tenemos papelera...",
|
||||
"delete_warning": "Esto eliminará tu {{type}} para siempre, aún no tenemos papelera...",
|
||||
"description": "Descripción",
|
||||
"deselect": "Deseleccionar",
|
||||
"details": "Detalles",
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
"delete_rule": "Supprimer la règle",
|
||||
"delete_tag": "Supprimer l'étiquette",
|
||||
"delete_tag_description": "Êtes-vous sûr de vouloir supprimer cette étiquette ? Cela ne peut pas être annulé et les fichiers étiquetés seront dissociés.",
|
||||
"delete_warning": "Attention : Ceci supprimera votre {{type}} pour toujours, nous n'avons pas encore de corbeille...",
|
||||
"delete_warning": "Ceci supprimera votre {{type}} pour toujours, nous n'avons pas encore de corbeille...",
|
||||
"description": "Description",
|
||||
"deselect": "Désélectionner",
|
||||
"details": "Détails",
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
"delete_rule": "Elimina regola",
|
||||
"delete_tag": "Elimina Tag",
|
||||
"delete_tag_description": "Sei sicuro di voler cancellare questa etichetta? Questa operazione non può essere annullata e i file etichettati verranno scollegati.",
|
||||
"delete_warning": "Attenzione: stai per eliminare il tuo {{type}} per sempre, non abbiamo ancora un cestino...",
|
||||
"delete_warning": "stai per eliminare il tuo {{type}} per sempre, non abbiamo ancora un cestino...",
|
||||
"description": "Descrizione",
|
||||
"deselect": "Deseleziona",
|
||||
"details": "Dettagli",
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
"delete_rule": "ルールを削除",
|
||||
"delete_tag": "タグを削除",
|
||||
"delete_tag_description": "本当にこのタグを削除しますか?これを元に戻すことはできず、タグ付けされたファイル間の結びつきは失われます。",
|
||||
"delete_warning": "【警告】これはあなたの {{type}} を完全に削除します。",
|
||||
"delete_warning": "これはあなたの {{type}} を完全に削除します。",
|
||||
"description": "説明",
|
||||
"deselect": "クリップボードを空にする",
|
||||
"details": "詳細",
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
"delete_rule": "Verwijder regel",
|
||||
"delete_tag": "Verwijder Tag",
|
||||
"delete_tag_description": "Weet je zeker dat je deze tag wilt verwijderen? Dit kan niet ongedaan worden gemaakt en ge-tagde bestanden worden ontkoppeld.",
|
||||
"delete_warning": "Waarschuwing: hiermee wordt je {{type}} permanent verwijderd, we hebben nog geen prullenbak...",
|
||||
"delete_warning": "hiermee wordt je {{type}} permanent verwijderd, we hebben nog geen prullenbak...",
|
||||
"description": "Omschrijving",
|
||||
"deselect": "Deselecteer",
|
||||
"details": "Details",
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
"delete_rule": "Удалить правило",
|
||||
"delete_tag": "Удалить тег",
|
||||
"delete_tag_description": "Вы уверены, что хотите удалить этот тег? Это действие нельзя отменить, и тегнутые файлы будут отсоединены.",
|
||||
"delete_warning": "Предупреждение: это удалит ваш {{type}} навсегда, у нас пока нет мусорной корзины.",
|
||||
"delete_warning": "это удалит ваш {{type}} навсегда, у нас пока нет мусорной корзины.",
|
||||
"description": "Описание",
|
||||
"deselect": "Отменить выбор",
|
||||
"details": "Подробности",
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
"delete_rule": "Kuralı Sil",
|
||||
"delete_tag": "Etiketi Sil",
|
||||
"delete_tag_description": "Bu etiketi silmek istediğinizden emin misiniz? Bu geri alınamaz ve etiketli dosyalar bağlantısız kalacak.",
|
||||
"delete_warning": "Uyarı: Bu, {{type}}'ınızı sonsuza dek silecek, henüz çöp kutumuz yok...",
|
||||
"delete_warning": "Bu, {{type}}'ınızı sonsuza dek silecek, henüz çöp kutumuz yok...",
|
||||
"description": "Açıklama",
|
||||
"deselect": "Seçimi Kaldır",
|
||||
"details": "Detaylar",
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
"delete_rule": "删除规则",
|
||||
"delete_tag": "删除标签",
|
||||
"delete_tag_description": "您确定要删除这个标签吗?这不能被撤销,打过标签的文件将会被取消链接。",
|
||||
"delete_warning": "警告:这将永久删除您的{{type}},我们目前还没有回收站…",
|
||||
"delete_warning": "这将永久删除您的{{type}},我们目前还没有回收站…",
|
||||
"description": "描述",
|
||||
"deselect": "取消选择",
|
||||
"details": "详情",
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
"delete_rule": "刪除規則",
|
||||
"delete_tag": "刪除標籤",
|
||||
"delete_tag_description": "您確定要刪除這個標籤嗎?這不能撤銷,並且帶有標籤的文件將被取消鏈接。",
|
||||
"delete_warning": "警告:這將永遠刪除您的{{type}},我們還沒有垃圾箱...",
|
||||
"delete_warning": "這將永遠刪除您的{{type}},我們還沒有垃圾箱...",
|
||||
"description": "描述",
|
||||
"deselect": "取消選擇",
|
||||
"details": "詳情",
|
||||
|
|
|
@ -73,6 +73,7 @@ export type Procedures = {
|
|||
{ key: "ephemeralFiles.createFolder", input: LibraryArgs<CreateEphemeralFolderArgs>, result: string } |
|
||||
{ key: "ephemeralFiles.cutFiles", input: LibraryArgs<EphemeralFileSystemOps>, result: null } |
|
||||
{ key: "ephemeralFiles.deleteFiles", input: LibraryArgs<string[]>, result: null } |
|
||||
{ key: "ephemeralFiles.moveToTrash", input: LibraryArgs<string[]>, result: null } |
|
||||
{ key: "ephemeralFiles.renameFile", input: LibraryArgs<EphemeralRenameFileArgs>, result: null } |
|
||||
{ key: "files.convertImage", input: LibraryArgs<ConvertImageArgs>, result: null } |
|
||||
{ key: "files.copyFiles", input: LibraryArgs<OldFileCopierJobInit>, result: null } |
|
||||
|
@ -81,6 +82,7 @@ export type Procedures = {
|
|||
{ key: "files.cutFiles", input: LibraryArgs<OldFileCutterJobInit>, result: null } |
|
||||
{ key: "files.deleteFiles", input: LibraryArgs<OldFileDeleterJobInit>, result: null } |
|
||||
{ key: "files.eraseFiles", input: LibraryArgs<OldFileEraserJobInit>, result: null } |
|
||||
{ key: "files.moveToTrash", input: LibraryArgs<OldFileDeleterJobInit>, result: null } |
|
||||
{ key: "files.removeAccessTime", input: LibraryArgs<number[]>, result: null } |
|
||||
{ key: "files.renameFile", input: LibraryArgs<RenameFileArgs>, result: null } |
|
||||
{ key: "files.setFavorite", input: LibraryArgs<SetFavoriteArgs>, result: null } |
|
||||
|
|
|
@ -121,7 +121,9 @@ export interface DialogProps<S extends FieldValues>
|
|||
loading?: boolean;
|
||||
trigger?: ReactNode;
|
||||
ctaLabel?: string;
|
||||
ctaSecondLabel?: string;
|
||||
onSubmit?: ReturnType<UseFormHandleSubmit<S>>;
|
||||
onSubmitSecond?: ReturnType<UseFormHandleSubmit<S>>;
|
||||
children?: ReactNode;
|
||||
ctaDanger?: boolean;
|
||||
closeLabel?: string;
|
||||
|
@ -141,6 +143,7 @@ export function Dialog<S extends FieldValues>({
|
|||
form,
|
||||
dialog,
|
||||
onSubmit,
|
||||
onSubmitSecond,
|
||||
onCancelled = true,
|
||||
invertButtonFocus,
|
||||
...props
|
||||
|
@ -190,7 +193,7 @@ export function Dialog<S extends FieldValues>({
|
|||
)
|
||||
: !form.formState.isValid;
|
||||
|
||||
const submitButton = (
|
||||
const submitButton = !props.ctaSecondLabel ? (
|
||||
<Button
|
||||
type="submit"
|
||||
size="sm"
|
||||
|
@ -200,9 +203,54 @@ export function Dialog<S extends FieldValues>({
|
|||
props.ctaDanger &&
|
||||
'border-red-500 bg-red-500 focus:ring-1 focus:ring-red-500 focus:ring-offset-2 focus:ring-offset-app-selected'
|
||||
)}
|
||||
onClick={async (e: React.MouseEvent<HTMLElement>) => {
|
||||
e.preventDefault();
|
||||
await onSubmit?.(e);
|
||||
dialog.onSubmit?.();
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
{props.ctaLabel}
|
||||
</Button>
|
||||
) : (
|
||||
<div className="flex flex-row gap-x-2">
|
||||
<Button
|
||||
type="submit"
|
||||
size="sm"
|
||||
disabled={form.formState.isSubmitting || props.submitDisabled || disableCheck}
|
||||
variant={props.ctaDanger ? 'colored' : 'accent'}
|
||||
className={clsx(
|
||||
props.ctaDanger &&
|
||||
'border-red-500 bg-red-500 focus:ring-1 focus:ring-red-500 focus:ring-offset-2 focus:ring-offset-app-selected'
|
||||
)}
|
||||
onClick={async (e: React.MouseEvent<HTMLElement>) => {
|
||||
e.preventDefault();
|
||||
await onSubmit?.(e);
|
||||
dialog.onSubmit?.();
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
{props.ctaLabel}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
size="sm"
|
||||
disabled={form.formState.isSubmitting || props.submitDisabled || disableCheck}
|
||||
variant={props.ctaDanger ? 'colored' : 'accent'}
|
||||
className={clsx(
|
||||
props.ctaDanger &&
|
||||
'border-primary-500 bg-primary-500 focus:ring-1 focus:ring-primary-500 focus:ring-offset-2 focus:ring-offset-app-selected'
|
||||
)}
|
||||
onClick={async (e: React.MouseEvent<HTMLElement>) => {
|
||||
e.preventDefault();
|
||||
await onSubmitSecond?.(e);
|
||||
dialog.onSubmit?.();
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
{props.ctaSecondLabel}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -226,8 +274,6 @@ export function Dialog<S extends FieldValues>({
|
|||
form={form}
|
||||
onSubmit={async (e) => {
|
||||
e?.preventDefault();
|
||||
await onSubmit?.(e);
|
||||
dialog.onSubmit?.();
|
||||
setOpen(false);
|
||||
}}
|
||||
className={clsx(
|
||||
|
|
Loading…
Reference in a new issue