Compare commits

...

3 Commits

5 changed files with 80 additions and 11 deletions

View File

@ -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<R: Runtime>(
child_dirs.sort_by(|a, b| a.dirname.partial_cmp(&b.dirname).unwrap());
Ok(child_dirs)
}
#[tauri::command]
pub async fn rename_file<R: Runtime>(
_app: tauri::AppHandle<R>,
_window: tauri::Window<R>,
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(())
}

View File

@ -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();

View File

@ -25,7 +25,7 @@ export const ContinuationView: FC = () => {
useEffect(() => {
ebus?.addListener('navigate_offset', ({ filename }) => {
let index = indexOf(filename, pluck('filename', files));
virtualListRef.current?.scrollToIndex({ index, align: 'start', behavior: 'smooth' });
virtualListRef.current?.scrollToIndex({ index, align: 'start' });
});
ebus?.addListener('reset_views', () => {
virtualListRef.current?.scrollTo({ top: 0 });

View File

@ -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<EventEmitter>(EventBusContext);
const handleFocusAction = useCallback(() => {

View File

@ -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<EventEmitter>(EventBusContext);
const filesCount = useMemo(() => length(files), [files]);
const [editingFile, setEditing] = useState<string | null>(null);
const [parentRef, { height: parentHeight }] = useMeasure();
const listRef = useRef<VirtuosoHandle | null>(null);
const handleFileSelectAction = useCallback(
@ -21,28 +37,50 @@ export const FileList: FC = () => {
},
[ebus]
);
const handleFileRenameAction = useCallback(
async (fileId: string, event: KeyboardEvent<HTMLInputElement>) => {
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);
let firstActivedIndex = indexOf(firstActived, pluck('filename', files));
listRef.current?.scrollToIndex({
index: firstActivedIndex,
align: 'center',
behavior: 'auto'
align: 'center'
});
}, [activeFiles]);
return (
<Box
w="100%"
h="100%"
pl={4}
sx={{ flexGrow: 1, overflowY: 'auto', contain: 'strict', overflowX: 'hidden' }}
ref={parentRef}
>
{!isEmpty(files) && (
<Virtuoso
style={{ height: parentHeight }}
style={{ height: parentHeight - 36 }}
totalCount={filesCount}
ref={listRef}
itemContent={index => (
@ -52,6 +90,7 @@ export const FileList: FC = () => {
px={4}
py={2}
onClick={() => handleFileSelectAction(files[index].filename)}
onDoubleClick={() => setEditing(files[index].id)}
sx={theme => ({
cursor: 'pointer',
'&:hover': {
@ -60,7 +99,18 @@ export const FileList: FC = () => {
})}
>
<Tooltip label={files[index].filename} zIndex={999}>
<Text truncate>{files[index].filename}</Text>
{propEq('id', editingFile)(files[index]) ? (
<Group>
<TextInput
defaultValue={files[index].filename}
size="xs"
variant="unstyled"
onKeyDown={event => handleFileRenameAction(files[index].id, event)}
/>
</Group>
) : (
<Text truncate>{files[index].filename}</Text>
)}
</Tooltip>
</Box>
)}