From 9ccf148b31bfbf62ba187edbe7f40a5d9132f3a5 Mon Sep 17 00:00:00 2001 From: Shawn Yuan <32612643+ysh83737@users.noreply.github.com> Date: Sat, 7 Oct 2023 19:41:35 +0800 Subject: [PATCH] =?UTF-8?q?feature:=20=E4=B8=BA=E5=BC=B9=E7=AA=97=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E7=9A=84=E8=B7=AF=E5=BE=84=E9=80=89=E6=8B=A9=E5=99=A8?= =?UTF-8?q?PathSelector=E6=B7=BB=E5=8A=A0=E6=8E=92=E5=BA=8F=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=20(#164)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: extract component `Sort` * feat: add sorting feature to `PathSelector` * feat: replace sort action with compoent `Sort` * feat: move `sortMethodFuncs` to module `Sort` --- .../FileManager/Navigator/SubActions.js | 66 ++--------- src/component/FileManager/PathSelector.js | 88 +++++++++++---- src/component/FileManager/Sort.tsx | 105 ++++++++++++++++++ src/component/Viewer/Video.js | 2 +- src/redux/explorer/action.ts | 37 +----- 5 files changed, 182 insertions(+), 116 deletions(-) create mode 100644 src/component/FileManager/Sort.tsx diff --git a/src/component/FileManager/Navigator/SubActions.js b/src/component/FileManager/Navigator/SubActions.js index 4d648be..5890e91 100644 --- a/src/component/FileManager/Navigator/SubActions.js +++ b/src/component/FileManager/Navigator/SubActions.js @@ -3,7 +3,6 @@ import { IconButton, makeStyles, Menu, MenuItem } from "@material-ui/core"; import ViewListIcon from "@material-ui/icons/ViewList"; import ViewSmallIcon from "@material-ui/icons/ViewComfy"; import ViewModuleIcon from "@material-ui/icons/ViewModule"; -import TextTotateVerticalIcon from "@material-ui/icons/TextRotateVertical"; import DownloadIcon from "@material-ui/icons/CloudDownload"; import Avatar from "@material-ui/core/Avatar"; import { useDispatch, useSelector } from "react-redux"; @@ -14,6 +13,7 @@ import { FormatPageBreak } from "mdi-material-ui"; import pathHelper from "../../../utils/page"; import { changePageSize } from "../../../redux/viewUpdate/action"; import { useTranslation } from "react-i18next"; +import Sort from "../Sort"; const useStyles = makeStyles((theme) => ({ sideButton: { @@ -22,17 +22,6 @@ const useStyles = makeStyles((theme) => ({ }, })); -const sortOptions = [ - "A-Z", - "Z-A", - "oldestUploaded", - "newestUploaded", - "oldestModified", - "newestModified", - "smallest", - "largest", -]; - const paginationOption = ["50", "100", "200", "500", "1000"]; export default function SubActions({ isSmall, inherit }) { @@ -63,29 +52,14 @@ export default function SubActions({ isSmall, inherit }) { const ChangePageSize = useCallback((e) => dispatch(changePageSize(e)), [ dispatch, ]); - const [anchorSort, setAnchorSort] = useState(null); const [anchorPagination, setAnchorPagination] = useState(null); - const [selectedIndex, setSelectedIndex] = useState(0); - const showSortOptions = (e) => { - setAnchorSort(e.currentTarget); - }; const showPaginationOptions = (e) => { setAnchorPagination(e.currentTarget); }; - const handleMenuItemClick = (e, index) => { - setSelectedIndex(index); - const optionsTable = { - 0: "namePos", - 1: "nameRev", - 2: "timePos", - 3: "timeRev", - 4: "modifyTimePos", - 5: "modifyTimeRev", - 6: "sizePos", - 7: "sizeRes", - }; - ChangeSortMethod(optionsTable[index]); - setAnchorSort(null); + + /** change sort */ + const onChangeSort = (value) => { + ChangeSortMethod(value); }; const handlePaginationChange = (s) => { ChangePageSize(s); @@ -181,32 +155,12 @@ export default function SubActions({ isSmall, inherit }) { - - - - setAnchorSort(null)} - > - {sortOptions.map((option, index) => ( - handleMenuItemClick(event, index)} - > - {t("sortMethods." + option)} - - ))} - + onChange={onChangeSort} + /> {share && ( ({ maxHeight: "330px", overflowY: " auto", }, + sortWrapper: { + textAlign: "right", + paddingRight: "30px", + }, + sortButton: { + padding: "0", + }, }); - class PathSelectorCompoment extends Component { state = { presentPath: "/", + sortBy: '', dirList: [], selectedTarget: null, }; + /** + * the source dir list from api `/directory` + * + * `state.dirList` is a sorted copy of it + */ + sourceDirList = [] componentDidMount = () => { const toBeLoad = this.props.presentPath; @@ -93,31 +107,11 @@ class PathSelectorCompoment extends Component { dirList.forEach((value) => { value.displayName = value.name; }); - if (toBeLoad === "/") { - dirList.unshift({ name: "/", path: "", displayName: "/" }); - } else { - let path = toBeLoad; - let name = toBeLoad; - const displayNames = ["fileManager.currentFolder", "fileManager.backToParentFolder"]; - for (let i = 0; i < 2; i++) { - const paths = path.split("/"); - name = paths.pop(); - name = name === "" ? "/" : name; - path = paths.join("/"); - dirList.unshift({ - name: name, - path: path, - displayName: this.props.t( - displayNames[i] - ), - }); - } - } + this.sourceDirList = dirList this.setState({ presentPath: toBeLoad, - dirList: dirList, selectedTarget: null, - }); + }, this.updateDirList); }) .catch((error) => { this.props.toggleSnackbar( @@ -134,6 +128,51 @@ class PathSelectorCompoment extends Component { this.props.onSelect(this.state.dirList[index]); }; + + /** + * change sort type + * @param {Event} event + */ + onChangeSort = (sortBy) => { + this.setState({ sortBy }, this.updateDirList) + }; + + /** + * sort dir list, and handle parent dirs + */ + updateDirList = () => { + const { state, sourceDirList } = this + const { sortBy, presentPath } = state + + // copy + const dirList = [...sourceDirList] + // sort + const sortMethod = sortMethodFuncs[sortBy] + if (sortMethod) dirList.sort(sortMethod) + + // add root/parent dirs to top + if (presentPath === "/") { + dirList.unshift({ name: "/", path: "", displayName: "/" }); + } else { + let path = presentPath; + let name = presentPath; + const displayNames = ["fileManager.currentFolder", "fileManager.backToParentFolder"]; + for (let i = 0; i < 2; i++) { + const paths = path.split("/"); + name = paths.pop(); + name = name === "" ? "/" : name; + path = paths.join("/"); + dirList.unshift({ + name: name, + path: path, + displayName: this.props.t( + displayNames[i] + ), + }); + } + } + this.setState({ dirList }) + } render() { const { classes, t } = this.props; @@ -157,6 +196,9 @@ class PathSelectorCompoment extends Component { return (
+
+ +
{this.state.dirList.map((value, index) => ( (null); + const showSortOptions: MouseEventHandler = (e) => { + setAnchorSort(e.currentTarget); + } + + const [sortBy, setSortBy] = useState(value || '') + function onChangeSort(value: SortMethod) { + setSortBy(value) + onChange(value) + setAnchorSort(null); + } + return ( + <> + + + + setAnchorSort(null)} + > + { + SORT_OPTIONS.map((option, index) => ( + onChangeSort(option.value)} + > + {t(option.label)} + + )) + } + + + ) +} + + +type SortFunc = (a: CloudreveFile, b: CloudreveFile) => number; + +export const sortMethodFuncs: Record = { + sizePos: (a: CloudreveFile, b: CloudreveFile) => { + return a.size - b.size; + }, + sizeRes: (a: CloudreveFile, b: CloudreveFile) => { + return b.size - a.size; + }, + namePos: (a: CloudreveFile, b: CloudreveFile) => { + return a.name.localeCompare( + b.name, + navigator.languages[0] || navigator.language, + { numeric: true, ignorePunctuation: true } + ); + }, + nameRev: (a: CloudreveFile, b: CloudreveFile) => { + return b.name.localeCompare( + a.name, + navigator.languages[0] || navigator.language, + { numeric: true, ignorePunctuation: true } + ); + }, + timePos: (a: CloudreveFile, b: CloudreveFile) => { + return Date.parse(a.create_date) - Date.parse(b.create_date); + }, + timeRev: (a: CloudreveFile, b: CloudreveFile) => { + return Date.parse(b.create_date) - Date.parse(a.create_date); + }, + modifyTimePos: (a: CloudreveFile, b: CloudreveFile) => { + return Date.parse(a.date) - Date.parse(b.date); + }, + modifyTimeRev: (a: CloudreveFile, b: CloudreveFile) => { + return Date.parse(b.date) - Date.parse(a.date); + }, +}; diff --git a/src/component/Viewer/Video.js b/src/component/Viewer/Video.js index 353360d..98e8391 100644 --- a/src/component/Viewer/Video.js +++ b/src/component/Viewer/Video.js @@ -17,7 +17,7 @@ import { Launch, PlaylistPlay, Subtitles } from "@material-ui/icons"; import TextLoading from "../Placeholder/TextLoading"; import SelectMenu from "./SelectMenu"; import { getDownloadURL } from "../../services/file"; -import { sortMethodFuncs } from "../../redux/explorer/action"; +import { sortMethodFuncs } from "../FileManager/Sort"; import { useTranslation } from "react-i18next"; const Artplayer = React.lazy(() => diff --git a/src/redux/explorer/action.ts b/src/redux/explorer/action.ts index a4f1353..28c6f1f 100644 --- a/src/redux/explorer/action.ts +++ b/src/redux/explorer/action.ts @@ -33,6 +33,7 @@ import { saveFileToFileSystemDirectory, verifyFileSystemRWPermission, } from "../../utils/filesystem"; +import { sortMethodFuncs } from "../../component/FileManager/Sort"; export interface ActionSetFileList extends AnyAction { type: "SET_FILE_LIST"; @@ -113,42 +114,6 @@ export const setShiftSelectedIds = (shiftSelectedIds: any) => { }; }; -type SortFunc = (a: CloudreveFile, b: CloudreveFile) => number; -export const sortMethodFuncs: Record = { - sizePos: (a: CloudreveFile, b: CloudreveFile) => { - return a.size - b.size; - }, - sizeRes: (a: CloudreveFile, b: CloudreveFile) => { - return b.size - a.size; - }, - namePos: (a: CloudreveFile, b: CloudreveFile) => { - return a.name.localeCompare( - b.name, - navigator.languages[0] || navigator.language, - { numeric: true, ignorePunctuation: true } - ); - }, - nameRev: (a: CloudreveFile, b: CloudreveFile) => { - return b.name.localeCompare( - a.name, - navigator.languages[0] || navigator.language, - { numeric: true, ignorePunctuation: true } - ); - }, - timePos: (a: CloudreveFile, b: CloudreveFile) => { - return Date.parse(a.create_date) - Date.parse(b.create_date); - }, - timeRev: (a: CloudreveFile, b: CloudreveFile) => { - return Date.parse(b.create_date) - Date.parse(a.create_date); - }, - modifyTimePos: (a: CloudreveFile, b: CloudreveFile) => { - return Date.parse(a.date) - Date.parse(b.date); - }, - modifyTimeRev: (a: CloudreveFile, b: CloudreveFile) => { - return Date.parse(b.date) - Date.parse(a.date); - }, -}; - export const selectAll = (): ThunkAction => { return (dispatch, getState): void => { const state = getState();