Add Delete Style Choice to Settings

This commit is contained in:
Arnab Chakraborty 2024-05-28 21:41:15 +03:00
parent d64b21357b
commit 711e830c94
7 changed files with 120 additions and 17 deletions

View file

@ -1,7 +1,7 @@
use crate::{
invalidate_query,
node::{
config::{NodeConfig, NodeConfigP2P, NodePreferences},
config::{DeletePreferences, DeletePromptOptions, NodeConfig, NodeConfigP2P, NodePreferences},
get_hardware_model_name, HardwareModel,
},
old_job::JobProgressEvent,
@ -94,6 +94,7 @@ pub struct SanitisedNodeConfig {
pub features: Vec<BackendFeature>,
pub preferences: NodePreferences,
pub image_labeler_version: Option<String>,
pub delete_prompt: DeletePreferences,
}
impl From<NodeConfig> for SanitisedNodeConfig {
@ -106,6 +107,7 @@ impl From<NodeConfig> for SanitisedNodeConfig {
features: value.features,
preferences: value.preferences,
image_labeler_version: value.image_labeler_version,
delete_prompt: value.delete_prompt,
}
}
}

View file

@ -1,6 +1,6 @@
use crate::{
invalidate_query,
node::config::{P2PDiscoveryState, Port},
node::config::{DeletePromptOptions, P2PDiscoveryState, Port},
};
use sd_prisma::prisma::{instance, location};
@ -25,6 +25,7 @@ pub(crate) fn mount() -> AlphaRouter<Ctx> {
pub p2p_discovery: Option<P2PDiscoveryState>,
pub p2p_remote_access: Option<bool>,
pub image_labeler_version: Option<String>,
pub delete_prompt: Option<DeletePromptOptions>,
}
R.mutation(|node, args: ChangeNodeNameArgs| async move {
if let Some(name) = &args.name {
@ -61,6 +62,10 @@ pub(crate) fn mount() -> AlphaRouter<Ctx> {
config.p2p.remote_access = remote_access;
};
if let Some(delete_prompt) = args.delete_prompt {
config.delete_prompt.set_option(delete_prompt);
}
#[cfg(feature = "ai")]
if let Some(version) = args.image_labeler_version {
if config

View file

@ -124,6 +124,9 @@ pub struct NodeConfig {
pub preferences: NodePreferences,
// Model version for the image labeler
pub image_labeler_version: Option<String>,
// Delete preferences
#[serde(default)]
pub delete_prompt: DeletePreferences,
version: NodeConfigVersion,
}
@ -152,6 +155,30 @@ mod identity_serde {
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Type, Default)]
pub enum DeletePromptOptions {
#[default]
ShowPrompt,
SendTrash,
DeleteInstantly,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Type, Default)]
pub struct DeletePreferences {
option: DeletePromptOptions,
}
impl DeletePreferences {
pub fn delete_prompt(&self) -> DeletePromptOptions {
self.option
}
pub fn set_option(&mut self, mut delete_prompt: DeletePromptOptions) -> &mut Self {
self.option = delete_prompt;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Type)]
pub struct NodePreferences {
pub thumbnailer: ThumbnailerPreferences,
@ -201,6 +228,7 @@ impl ManagedVersion<NodeConfigVersion> for NodeConfig {
sd_api_origin: None,
preferences: NodePreferences::default(),
image_labeler_version,
delete_prompt: DeletePreferences::default(),
})
}
}

View file

@ -32,6 +32,9 @@ export const Component = () => {
const { t } = useLocale();
console.log(node.data);
// console.log(node.data?.delete_prompt);
const form = useZodForm({
schema: z
.object({
@ -57,7 +60,12 @@ export const Component = () => {
})
.int()
.nonnegative()
.lte(100)
.lte(100),
delete_prompt: z.union([
z.literal('ShowPrompt'),
z.literal('SendTrash'),
z.literal('DeleteInstantly')
])
})
.strict(),
reValidateMode: 'onChange',
@ -70,7 +78,8 @@ export const Component = () => {
p2p_remote_access: node.data?.p2p.remote_access || false,
image_labeler_version: node.data?.image_labeler_version ?? undefined,
background_processing_percentage:
node.data?.preferences.thumbnailer.background_processing_percentage || 50
node.data?.preferences.thumbnailer.background_processing_percentage || 50,
delete_prompt: node.data?.delete_prompt.option || 'ShowPrompt'
}
});
const p2p_port = form.watch('p2p_port');
@ -87,7 +96,8 @@ export const Component = () => {
p2p_ipv6_enabled: value.p2p_ipv6_enabled ?? null,
p2p_discovery: value.p2p_discovery ?? null,
p2p_remote_access: value.p2p_remote_access ?? null,
image_labeler_version: value.image_labeler_version ?? null
image_labeler_version: value.image_labeler_version ?? null,
delete_prompt: value.delete_prompt ?? "ShowPrompt"
});
if (value.background_processing_percentage != undefined) {
@ -406,6 +416,30 @@ export const Component = () => {
</>
) : null}
</div>
<div className="flex flex-col gap-4">
<h1 className="mb-3 text-lg font-bold text-ink">{t('delete_settings')}</h1>
<Setting
mini
title={t('delete_show_prompt')}
description={
<p className="text-sm text-gray-400">
{t('delete_show_prompt_description')}
</p>
}
>
<Select
value={form.watch('delete_prompt') || 'ShowPrompt'}
containerClassName="h-[30px]"
className="h-full"
onChange={(type) => form.setValue('delete_prompt', type)}
>
<SelectOption value="ShowPrompt">{'Show Prompt'}</SelectOption>
<SelectOption value="SendTrash">{'Send to Trash'}</SelectOption>
<SelectOption value="DeleteInstantly">{'Delete Instantly'}</SelectOption>
</Select>
</Setting>
</div>
</FormProvider>
);
};

View file

@ -1,4 +1,10 @@
import { useItemsAsEphemeralPaths, useItemsAsFilePaths, type ExplorerItem } from '@sd/client';
import {
useBridgeQuery,
useItemsAsEphemeralPaths,
useItemsAsFilePaths,
useLibraryMutation,
type ExplorerItem
} from '@sd/client';
import { dialogManager } from '@sd/ui';
import DeleteDialog from '~/app/$libraryId/Explorer/FilePath/DeleteDialog';
import { isNonEmpty } from '~/util';
@ -8,6 +14,11 @@ import { useShortcut } from './useShortcut';
export const useKeyDeleteFile = (selectedItems: Set<ExplorerItem>, locationId?: number | null) => {
const filePaths = useItemsAsFilePaths([...selectedItems]);
const ephemeralPaths = useItemsAsEphemeralPaths([...selectedItems]);
const node = useBridgeQuery(['nodeState']);
const deleteFile = useLibraryMutation('files.deleteFiles');
const deleteEphemeralFile = useLibraryMutation('ephemeralFiles.deleteFiles');
const moveToTrashFile = useLibraryMutation('files.moveToTrash');
const moveToTrashEphemeralFile = useLibraryMutation('ephemeralFiles.moveToTrash');
const deleteHandler = (e: KeyboardEvent) => {
e.preventDefault();
@ -30,15 +41,31 @@ export const useKeyDeleteFile = (selectedItems: Set<ExplorerItem>, locationId?:
fileCount += entry.is_dir ? 0 : 1;
}
dialogManager.create((dp) => (
<DeleteDialog
{...dp}
indexedArgs={indexedArgs}
ephemeralArgs={ephemeralArgs}
dirCount={dirCount}
fileCount={fileCount}
/>
));
if (node.data?.delete_prompt.option === 'ShowPrompt') {
dialogManager.create((dp) => (
<DeleteDialog
{...dp}
indexedArgs={indexedArgs}
ephemeralArgs={ephemeralArgs}
dirCount={dirCount}
fileCount={fileCount}
/>
));
} else if (node.data?.delete_prompt.option === 'SendTrash') {
if (locationId != null && isNonEmpty(filePaths)) {
moveToTrashFile.mutate({ location_id: locationId, file_path_ids: filePaths.map((p) => p.id) });
} else if (isNonEmpty(ephemeralPaths)) {
const { paths } = ephemeralArgs!;
moveToTrashEphemeralFile.mutate(paths);
}
} else {
if (locationId != null && isNonEmpty(filePaths)) {
deleteFile.mutate({ location_id: locationId, file_path_ids: filePaths.map((p) => p.id) });
} else if (isNonEmpty(ephemeralPaths)) {
const { paths } = ephemeralArgs!;
deleteEphemeralFile.mutate(paths);
}
}
};
useShortcut('delItem', deleteHandler);

View file

@ -142,6 +142,9 @@
"default": "Default",
"default_settings": "Default Settings",
"delete": "Delete",
"delete_settings": "Delete Functionality",
"delete_show_prompt": "Delete Prompt",
"delete_show_prompt_description": "Choose if you would like to automatically send files to the trash, delete them forever, or show a prompt before choosing an option.",
"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.",

View file

@ -168,7 +168,7 @@ export type CRDTOperationData = { c: { [key in string]: JsonValue } } | { u: { f
export type CameraData = { device_make: string | null; device_model: string | null; color_space: string | null; color_profile: ColorProfile | null; focal_length: number | null; shutter_speed: number | null; flash: Flash | null; orientation: Orientation; lens_make: string | null; lens_model: string | null; bit_depth: number | null; zoom: number | null; iso: number | null; software: string | null; serial_number: string | null; lens_serial_number: string | null; contrast: number | null; saturation: number | null; sharpness: number | null; composite: Composite | null }
export type ChangeNodeNameArgs = { name: string | null; p2p_port: Port | null; p2p_ipv4_enabled: boolean | null; p2p_ipv6_enabled: boolean | null; p2p_discovery: P2PDiscoveryState | null; p2p_remote_access: boolean | null; image_labeler_version: string | null }
export type ChangeNodeNameArgs = { name: string | null; p2p_port: Port | null; p2p_ipv4_enabled: boolean | null; p2p_ipv6_enabled: boolean | null; p2p_discovery: P2PDiscoveryState | null; p2p_remote_access: boolean | null; image_labeler_version: string | null; delete_prompt: DeletePromptOptions | null }
export type Chapter = { id: number; start: [number, number]; end: [number, number]; time_base_den: number; time_base_num: number; metadata: Metadata }
@ -224,6 +224,10 @@ export type CursorOrderItem<T> = { order: SortOrder; data: T }
export type DefaultLocations = { desktop: boolean; documents: boolean; downloads: boolean; pictures: boolean; music: boolean; videos: boolean }
export type DeletePreferences = { option: DeletePromptOptions }
export type DeletePromptOptions = "ShowPrompt" | "SendTrash" | "DeleteInstantly"
/**
* The method used for the discovery of this peer.
* *Technically* you can have multiple under the hood but this simplifies things for the UX.
@ -484,7 +488,7 @@ id: string;
/**
* name is the display name of the current node. This is set by the user and is shown in the UI. // TODO: Length validation so it can fit in DNS record
*/
name: string; identity: RemoteIdentity; p2p: NodeConfigP2P; features: BackendFeature[]; preferences: NodePreferences; image_labeler_version: string | null }) & { data_path: string; device_model: string | null }
name: string; identity: RemoteIdentity; p2p: NodeConfigP2P; features: BackendFeature[]; preferences: NodePreferences; image_labeler_version: string | null; delete_prompt: DeletePreferences }) & { data_path: string; device_model: string | null }
export type NonIndexedPathItem = { path: string; name: string; extension: string; kind: number; is_dir: boolean; date_created: string; date_modified: string; size_in_bytes_bytes: number[]; hidden: boolean }