From 0eb69d777d188c8cc016c39992f3ea0cb9e79688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Thu, 23 Mar 2023 09:52:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(file):=E5=A2=9E=E5=8A=A0=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=90=8D=E7=A7=B0=E7=9A=84=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/commands/files.rs | 19 ++++++++++- src-tauri/src/main.rs | 3 +- src/components/DirTree.tsx | 1 + src/components/FileList.tsx | 60 ++++++++++++++++++++++++++++++--- 4 files changed, 77 insertions(+), 6 deletions(-) diff --git a/src-tauri/src/commands/files.rs b/src-tauri/src/commands/files.rs index 7ce6d67..f222879 100644 --- a/src-tauri/src/commands/files.rs +++ b/src-tauri/src/commands/files.rs @@ -1,4 +1,7 @@ -use std::{fs::DirEntry, path::Path}; +use std::{ + fs::{self, DirEntry}, + path::{Path, PathBuf}, +}; use mountpoints::mountinfos; use serde::Serialize; @@ -193,3 +196,17 @@ pub async fn scan_for_child_dirs( child_dirs.sort_by(|a, b| a.dirname.partial_cmp(&b.dirname).unwrap()); Ok(child_dirs) } + +#[tauri::command] +pub async fn rename_file( + _app: tauri::AppHandle, + _window: tauri::Window, + store_path: String, + origin_name: String, + new_name: String, +) -> Result<(), String> { + let origin_file = PathBuf::from(store_path.clone()).join(origin_name); + let new_file = PathBuf::from(store_path).join(new_name); + fs::rename(origin_file, new_file).map_err(|e| format!("重命名问文件失败,{}", e))?; + Ok(()) +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 2c8d224..de79ae7 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -15,7 +15,8 @@ fn main() { .invoke_handler(tauri::generate_handler![ commands::prelude::scan_directory, commands::prelude::show_drives, - commands::prelude::scan_for_child_dirs + commands::prelude::scan_for_child_dirs, + commands::prelude::rename_file ]) .setup(|app| { let main_window = app.get_window("main").unwrap(); diff --git a/src/components/DirTree.tsx b/src/components/DirTree.tsx index 7358434..93ab159 100644 --- a/src/components/DirTree.tsx +++ b/src/components/DirTree.tsx @@ -123,6 +123,7 @@ export const DirTree: FC = () => { const roots = useDirTreeStore(currentRootsSelector()); const { focused, focus, unfocus, selected, foldDir } = useDirTreeStore(); const [viewRef, { width }] = useMeasure(); + const storeFiles = useFileListStore.use.updateFiles(); const ebus = useContext(EventBusContext); const handleFocusAction = useCallback(() => { diff --git a/src/components/FileList.tsx b/src/components/FileList.tsx index df0461d..648f41e 100644 --- a/src/components/FileList.tsx +++ b/src/components/FileList.tsx @@ -1,18 +1,34 @@ //@ts-nocheck -import { Box, Center, Text, Tooltip } from '@mantine/core'; +import { Box, Center, Group, Text, TextInput, Tooltip } from '@mantine/core'; +import { notifications } from '@mantine/notifications'; +import { invoke } from '@tauri-apps/api'; import EventEmitter from 'events'; -import { head, includes, indexOf, isEmpty, length, not, pluck } from 'ramda'; -import { FC, useCallback, useContext, useLayoutEffect, useMemo, useRef } from 'react'; +import { equals, find, head, includes, indexOf, isEmpty, length, not, pluck, propEq } from 'ramda'; +import { + FC, + KeyboardEvent, + useCallback, + useContext, + useLayoutEffect, + useMemo, + useRef, + useState +} from 'react'; import { useMeasure } from 'react-use'; import { Virtuoso, VirtuosoHandle } from 'react-virtuoso'; import { EventBusContext } from '../EventBus'; +import { useDirTreeStore } from '../states/dirs'; import { sortedFilesSelector, useFileListStore } from '../states/files'; export const FileList: FC = () => { const files = useFileListStore(sortedFilesSelector()); + const storeFiles = useFileListStore.use.updateFiles(); const activeFiles = useFileListStore.use.actives(); + const cwd = useDirTreeStore.use.selected(); + const directories = useDirTreeStore.use.directories(); const ebus = useContext(EventBusContext); const filesCount = useMemo(() => length(files), [files]); + const [editingFile, setEditing] = useState(null); const [parentRef, { height: parentHeight }] = useMeasure(); const listRef = useRef(null); const handleFileSelectAction = useCallback( @@ -21,6 +37,30 @@ export const FileList: FC = () => { }, [ebus] ); + const handleFileRenameAction = useCallback( + async (fileId: string, event: KeyboardEvent) => { + if (equals(event.key, 'Enter')) { + const newFileName = event.target.value; + const originalFile = find(propEq('id', fileId), files); + const storeDirectory = find(propEq('id', cwd), directories); + try { + await invoke('rename_file', { + storePath: storeDirectory?.path, + originName: originalFile?.filename, + newName: newFileName + }); + const refreshedFiles = await invoke('scan_directory', { target: storeDirectory?.path }); + storeFiles(refreshedFiles); + ebus.emit('reset_views'); + } catch (e) { + console.error('[debug]重命名文件:', e); + notifications.show({ message: `重命名文件失败,${e}`, color: 'red' }); + } + setEditing(null); + } + }, + [files, cwd, directories] + ); useLayoutEffect(() => { let firstActived = head(activeFiles); @@ -51,6 +91,7 @@ export const FileList: FC = () => { px={4} py={2} onClick={() => handleFileSelectAction(files[index].filename)} + onDoubleClick={() => setEditing(files[index].id)} sx={theme => ({ cursor: 'pointer', '&:hover': { @@ -59,7 +100,18 @@ export const FileList: FC = () => { })} > - {files[index].filename} + {propEq('id', editingFile)(files[index]) ? ( + + handleFileRenameAction(files[index].id, event)} + /> + + ) : ( + {files[index].filename} + )} )}