From 453c84f46f54b3fe10878620b44216c42930def8 Mon Sep 17 00:00:00 2001 From: Michael An <2331806369@qq.com> Date: Fri, 13 Sep 2024 20:28:20 +0800 Subject: [PATCH] [refactor] lib container view (#6766) * [refactor] lib container view * optimize codes --- .../{index.js => detail-container.js} | 6 +- .../dirent-detail/dirent-details/index.js | 6 +- .../{lib-details/index.js => lib-details.js} | 24 +- .../toolbar/selected-dirents-toolbar.js | 6 +- frontend/src/models/index.js | 6 + .../lib-content-view/lib-content-container.js | 370 ------------------ .../lib-content-view/lib-content-view.js | 363 +++++++++++------ 7 files changed, 266 insertions(+), 515 deletions(-) rename frontend/src/components/dirent-detail/{index.js => detail-container.js} (89%) rename frontend/src/components/dirent-detail/{lib-details/index.js => lib-details.js} (85%) create mode 100644 frontend/src/models/index.js delete mode 100644 frontend/src/pages/lib-content-view/lib-content-container.js diff --git a/frontend/src/components/dirent-detail/index.js b/frontend/src/components/dirent-detail/detail-container.js similarity index 89% rename from frontend/src/components/dirent-detail/index.js rename to frontend/src/components/dirent-detail/detail-container.js index bbfbbc07851..0b11fa2e5b9 100644 --- a/frontend/src/components/dirent-detail/index.js +++ b/frontend/src/components/dirent-detail/detail-container.js @@ -5,7 +5,7 @@ import DirentDetail from './dirent-details'; import ObjectUtils from '../../metadata/metadata-view/utils/object-utils'; import { MetadataContext } from '../../metadata'; -const Index = React.memo(({ repoID, path, dirent, currentRepoInfo, repoTags, fileTags, onClose, onFileTagChanged }) => { +const DetailContainer = React.memo(({ repoID, path, dirent, currentRepoInfo, repoTags, fileTags, onClose, onFileTagChanged }) => { useEffect(() => { // init context @@ -45,7 +45,7 @@ const Index = React.memo(({ repoID, path, dirent, currentRepoInfo, repoTags, fil return !isChanged; }); -Index.propTypes = { +DetailContainer.propTypes = { repoID: PropTypes.string, path: PropTypes.string, dirent: PropTypes.object, @@ -56,4 +56,4 @@ Index.propTypes = { onFileTagChanged: PropTypes.func, }; -export default Index; +export default DetailContainer; diff --git a/frontend/src/components/dirent-detail/dirent-details/index.js b/frontend/src/components/dirent-detail/dirent-details/index.js index 8fa53a5a81b..803b226a90e 100644 --- a/frontend/src/components/dirent-detail/dirent-details/index.js +++ b/frontend/src/components/dirent-detail/dirent-details/index.js @@ -101,7 +101,7 @@ class DirentDetails extends React.Component { {this.renderImage()} {dirent && direntDetail && (
- {dirent.type !== 'file' ? ( + {dirent.type !== 'file' ? - ) : ( + : - )} + }
)} diff --git a/frontend/src/components/dirent-detail/lib-details/index.js b/frontend/src/components/dirent-detail/lib-details.js similarity index 85% rename from frontend/src/components/dirent-detail/lib-details/index.js rename to frontend/src/components/dirent-detail/lib-details.js index 1539197d93a..4fd5e1868f6 100644 --- a/frontend/src/components/dirent-detail/lib-details/index.js +++ b/frontend/src/components/dirent-detail/lib-details.js @@ -1,15 +1,15 @@ import React, { useEffect, useMemo, useState } from 'react'; import PropTypes from 'prop-types'; import { Formatter } from '@seafile/sf-metadata-ui-component'; -import { Utils } from '../../../utils/utils'; -import { gettext } from '../../../utils/constants'; -import { seafileAPI } from '../../../utils/seafile-api'; -import toaster from '../../toast'; -import { Detail, Header, Body } from '../detail'; -import Repo from '../../../models/repo'; -import Loading from '../../loading'; -import DetailItem from '../detail-item'; -import { CellType } from '../../../metadata/metadata-view/_basic'; +import { Utils } from '../../utils/utils'; +import { gettext } from '../../utils/constants'; +import { seafileAPI } from '../../utils/seafile-api'; +import toaster from '../toast'; +import { Detail, Header, Body } from './detail'; +import Repo from '../../models/repo'; +import Loading from '../loading'; +import DetailItem from './detail-item'; +import { CellType } from '../../metadata/metadata-view/_basic'; const LibDetail = React.memo(({ currentRepoInfo, onClose }) => { const [isLoading, setLoading] = useState(true); @@ -36,9 +36,9 @@ const LibDetail = React.memo(({ currentRepoInfo, onClose }) => {
- {isLoading ? ( + {isLoading ?
- ) : ( + :
@@ -62,7 +62,7 @@ const LibDetail = React.memo(({ currentRepoInfo, onClose }) => {
- )} + } ); diff --git a/frontend/src/components/toolbar/selected-dirents-toolbar.js b/frontend/src/components/toolbar/selected-dirents-toolbar.js index c86f2a35775..8c4e527b3b8 100644 --- a/frontend/src/components/toolbar/selected-dirents-toolbar.js +++ b/frontend/src/components/toolbar/selected-dirents-toolbar.js @@ -42,7 +42,7 @@ const propTypes = { isGroupOwnedRepo: PropTypes.bool.isRequired, }; -class MultipleDirOperationToolbar extends React.Component { +class SelectedDirentsToolbar extends React.Component { constructor(props) { super(props); @@ -471,6 +471,6 @@ class MultipleDirOperationToolbar extends React.Component { } } -MultipleDirOperationToolbar.propTypes = propTypes; +SelectedDirentsToolbar.propTypes = propTypes; -export default MultipleDirOperationToolbar; +export default SelectedDirentsToolbar; diff --git a/frontend/src/models/index.js b/frontend/src/models/index.js new file mode 100644 index 00000000000..03085668bcc --- /dev/null +++ b/frontend/src/models/index.js @@ -0,0 +1,6 @@ +import Dirent from './dirent'; +import FileTag from './file-tag'; +import RepoTag from './repo-tag'; +import RepoInfo from './repo-info'; + +export { Dirent, FileTag, RepoTag, RepoInfo }; diff --git a/frontend/src/pages/lib-content-view/lib-content-container.js b/frontend/src/pages/lib-content-view/lib-content-container.js deleted file mode 100644 index 0281e57da91..00000000000 --- a/frontend/src/pages/lib-content-view/lib-content-container.js +++ /dev/null @@ -1,370 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import { Utils } from '../../utils/utils'; -import { gettext } from '../../utils/constants'; -import CurDirPath from '../../components/cur-dir-path'; -import DirTool from '../../components/cur-dir-path/dir-tool'; -import Detail from '../../components/dirent-detail'; -import DirColumnView from '../../components/dir-view-mode/dir-column-view'; -import ToolbarForSelectedDirents from '../../components/toolbar/selected-dirents-toolbar'; - -import '../../css/lib-content-view.css'; - -const propTypes = { - isSidePanelFolded: PropTypes.bool, - switchViewMode: PropTypes.func.isRequired, - isCustomPermission: PropTypes.bool, - pathPrefix: PropTypes.array.isRequired, - isTreePanelShown: PropTypes.bool.isRequired, - toggleTreePanel: PropTypes.func.isRequired, - currentMode: PropTypes.string.isRequired, - path: PropTypes.string.isRequired, - pathExist: PropTypes.bool.isRequired, - // repoinfo - repoEncrypted: PropTypes.bool.isRequired, - currentRepoInfo: PropTypes.object.isRequired, - repoID: PropTypes.string.isRequired, - enableDirPrivateShare: PropTypes.bool.isRequired, - isGroupOwnedRepo: PropTypes.bool.isRequired, - userPerm: PropTypes.string, - isRepoOwner: PropTypes.bool.isRequired, - // path func - onTabNavClick: PropTypes.func.isRequired, - onMainNavBarClick: PropTypes.func.isRequired, - // file - isViewFile: PropTypes.bool.isRequired, - fileTags: PropTypes.array.isRequired, - isFileLoading: PropTypes.bool.isRequired, - filePermission: PropTypes.string, - content: PropTypes.string, - viewId: PropTypes.string, - lastModified: PropTypes.string, - latestContributor: PropTypes.string, - currentDirent: PropTypes.object, - onLinkClick: PropTypes.func.isRequired, - onCloseMarkdownViewDialog: PropTypes.func, - // tree - isTreeDataLoading: PropTypes.bool.isRequired, - treeData: PropTypes.object.isRequired, - currentNode: PropTypes.object, - onNodeClick: PropTypes.func.isRequired, - onNodeCollapse: PropTypes.func.isRequired, - onNodeExpanded: PropTypes.func.isRequired, - onRenameNode: PropTypes.func.isRequired, - onDeleteNode: PropTypes.func.isRequired, - onAddFileNode: PropTypes.func.isRequired, - onAddFolderNode: PropTypes.func.isRequired, - // repo content - repoTags: PropTypes.array.isRequired, - usedRepoTags: PropTypes.array.isRequired, - updateUsedRepoTags: PropTypes.func.isRequired, - // list - isDirentListLoading: PropTypes.bool.isRequired, - direntList: PropTypes.array.isRequired, - sortBy: PropTypes.string.isRequired, - sortOrder: PropTypes.string.isRequired, - sortItems: PropTypes.func.isRequired, - updateDirent: PropTypes.func.isRequired, - onItemClick: PropTypes.func.isRequired, - onItemSelected: PropTypes.func.isRequired, - onItemDelete: PropTypes.func.isRequired, - onItemRename: PropTypes.func.isRequired, - onItemMove: PropTypes.func.isRequired, - onItemCopy: PropTypes.func.isRequired, - onAddFolder: PropTypes.func.isRequired, - onAddFile: PropTypes.func.isRequired, - onItemConvert: PropTypes.func.isRequired, - onFileTagChanged: PropTypes.func.isRequired, - isDirentSelected: PropTypes.bool.isRequired, - isAllDirentSelected: PropTypes.bool.isRequired, - onAllDirentSelected: PropTypes.func.isRequired, - isDirentDetailShow: PropTypes.bool.isRequired, - selectedDirent: PropTypes.object, - selectedDirentList: PropTypes.array.isRequired, - onSelectedDirentListUpdate: PropTypes.func.isRequired, - onItemsMove: PropTypes.func.isRequired, - onItemsCopy: PropTypes.func.isRequired, - onItemsDelete: PropTypes.func.isRequired, - closeDirentDetail: PropTypes.func.isRequired, - showDirentDetail: PropTypes.func.isRequired, - onDeleteRepoTag: PropTypes.func.isRequired, - updateDetail: PropTypes.bool.isRequired, - onListContainerScroll: PropTypes.func.isRequired, - onDirentClick: PropTypes.func.isRequired, - direntDetailPanelTab: PropTypes.string, - loadDirentList: PropTypes.func, - fullDirentList: PropTypes.array, - unSelectDirent: PropTypes.func, - onFilesTagChanged: PropTypes.func.isRequired, - showShareBtn: PropTypes.bool.isRequired, - onUploadFile: PropTypes.func.isRequired, - onUploadFolder: PropTypes.func.isRequired, - onToolbarFileTagChanged: PropTypes.func.isRequired, - eventBus: PropTypes.object, -}; - -class LibContentContainer extends React.Component { - - constructor(props) { - super(props); - this.state = { - currentDirent: null, - hasSelectedFile: false, - }; - - this.errMessage = (
{gettext('Folder does not exist.')}
); - } - - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.path !== this.props.path || nextProps.updateDetail !== this.props.updateDetail) { - this.setState({ currentDirent: null }); - } - - if (!this.state.hasSelectedFile) { - const { isDirentSelected } = nextProps; - if (isDirentSelected) { - this.setState({ hasSelectedFile: true }); - } - } - } - - onPathClick = (path) => { - this.props.onMainNavBarClick(path); - }; - - onItemClick = (dirent) => { - this.props.onItemClick(dirent); - }; - - onDirentClick = (dirent, event) => { - this.setState({ currentDirent: dirent && dirent.isActive ? null : dirent }); - this.props.onDirentClick(dirent, event); - }; - - onItemSelected = (dirent) => { - this.setState({ currentDirent: dirent && dirent.isActive ? null : dirent }); - this.props.onItemSelected(dirent); - }; - - onItemDelete = (dirent) => { - this.checkCurrentDirent(dirent); - this.props.onItemDelete(dirent); - }; - - onItemMove = (destRepo, dirent, selectedPath, currentPath) => { - this.checkCurrentDirent(dirent); - this.props.onItemMove(destRepo, dirent, selectedPath, currentPath); - }; - - checkCurrentDirent = (deletedDirent) => { - let { currentDirent } = this.state; - if (currentDirent && deletedDirent.name === currentDirent.name) { - this.setState({ currentDirent: null }); - } - }; - - onItemsScroll = (e) => { - let target = e.target; - - if (target.scrollTop === 0) { - return; - } - - if (target.scrollTop + target.clientHeight + 1 >= target.scrollHeight) { - this.props.onListContainerScroll(); - } - }; - - render() { - const isDesktop = Utils.isDesktop(); - const { path, repoID, usedRepoTags, isDirentSelected } = this.props; - const { hasSelectedFile } = this.state; - let isRepoInfoBarShow = false; - if (path === '/') { - if (isDesktop && usedRepoTags.length !== 0) { - isRepoInfoBarShow = true; - } - } - - return ( -
- {this.props.currentRepoInfo.status === 'read-only' && -
- {gettext('This library has been set to read-only by admin and cannot be updated.')} -
- } -
-
- {isDirentSelected ? ( - - ) : ( - - )} -
- {isDesktop && -
- -
- } -
-
- {!this.props.pathExist && this.errMessage} - {this.props.pathExist && ( - - )} - {this.props.isDirentDetailShow && ( - - )} -
-
- ); - } -} - -LibContentContainer.propTypes = propTypes; - -export default LibContentContainer; diff --git a/frontend/src/pages/lib-content-view/lib-content-view.js b/frontend/src/pages/lib-content-view/lib-content-view.js index b22ffc59d09..439bcdc2f4e 100644 --- a/frontend/src/pages/lib-content-view/lib-content-view.js +++ b/frontend/src/pages/lib-content-view/lib-content-view.js @@ -1,7 +1,8 @@ -import React, { Fragment } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import cookie from 'react-cookies'; import moment from 'moment'; +import classnames from 'classnames'; import MediaQuery from 'react-responsive'; import { Modal } from 'reactstrap'; import { navigate } from '@gatsbyjs/reach-router'; @@ -9,16 +10,12 @@ import { gettext, siteRoot, username, enableVideoThumbnail, enablePDFThumbnail, import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; import collabServer from '../../utils/collab-server'; -import Dirent from '../../models/dirent'; -import FileTag from '../../models/file-tag'; -import RepoTag from '../../models/repo-tag'; -import RepoInfo from '../../models/repo-info'; +import { Dirent, FileTag, RepoTag, RepoInfo } from '../../models'; import TreeNode from '../../components/tree-view/tree-node'; import treeHelper from '../../components/tree-view/tree-helper'; import toaster from '../../components/toast'; import ModalPortal from '../../components/modal-portal'; import LibDecryptDialog from '../../components/dialog/lib-decrypt-dialog'; -import LibContentContainer from './lib-content-container'; import FileUploader from '../../components/file-uploader/file-uploader'; import CopyMoveDirentProgressDialog from '../../components/dialog/copy-move-dirent-progress-dialog'; import DeleteFolderDialog from '../../components/dialog/delete-folder-dialog'; @@ -26,6 +23,13 @@ import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type'; import { PRIVATE_FILE_TYPE } from '../../constants'; import { MetadataProvider, CollaboratorsProvider } from '../../metadata/hooks'; import { LIST_MODE, METADATA_MODE } from '../../components/dir-view-mode/constants'; +import CurDirPath from '../../components/cur-dir-path'; +import DirTool from '../../components/cur-dir-path/dir-tool'; +import DetailContainer from '../../components/dirent-detail/detail-container'; +import DirColumnView from '../../components/dir-view-mode/dir-column-view'; +import SelectedDirentsToolbar from '../../components/toolbar/selected-dirents-toolbar'; + +import '../../css/lib-content-view.css'; const propTypes = { eventBus: PropTypes.object, @@ -80,7 +84,6 @@ class LibContentView extends React.Component { errorMsg: '', isDirentDetailShow: false, direntDetailPanelTab: '', - updateDetail: false, itemsShowLength: 100, isSessionExpired: false, isCopyMoveProgressDialogShow: false, @@ -104,6 +107,23 @@ class LibContentView extends React.Component { this.unsubscribeEventBus = null; } + updateCurrentDirent = (deletedDirent) => { + let { currentDirent } = this.state; + if (currentDirent && deletedDirent.name === currentDirent.name) { + this.setState({ currentDirent: null }); + } + }; + + onItemsScroll = (e) => { + let target = e.target; + if (target.scrollTop === 0) { + return; + } + if (target.scrollTop + target.clientHeight + 1 >= target.scrollHeight) { + this.onListContainerScroll(); + } + }; + showDirentDetail = (direntDetailPanelTab) => { if (direntDetailPanelTab) { this.setState({ direntDetailPanelTab: direntDetailPanelTab }, () => { @@ -241,7 +261,10 @@ class LibContentView extends React.Component { }); } - componentDidUpdate() { + componentDidUpdate(prevProps, prevState) { + if (prevState.path !== this.state.path) { + this.setState({ currentDirent: null }); + } this.lastModifyTime = new Date(); this.props.eventBus.dispatch(EVENT_BUS_TYPE.CURRENT_LIBRARY_CHANGED, { repoID: this.props.repoID, @@ -386,23 +409,15 @@ class LibContentView extends React.Component { }; handleMarkdownFile = (path) => { - seafileAPI.getFileInfo(this.props.repoID, path) - .then(() => { - /* - if (this.state.currentMode !== 'column') { - cookie.save('seafile_view_mode', 'column'); - this.setState({currentMode: 'column'}); - } - */ + seafileAPI.getFileInfo(this.props.repoID, path).then(() => { + this.loadSidePanel(path); + this.showFile(path); + }).catch(() => { + if (this.state.isTreePanelShown) { this.loadSidePanel(path); - this.showFile(path); - }) - .catch(() => { - if (this.state.isTreePanelShown) { - this.loadSidePanel(path); - } - this.showDir(path); - }); + } + this.showDir(path); + }); }; handleNonMarkdownFile = (path) => { @@ -950,7 +965,7 @@ class LibContentView extends React.Component { let direntPaths = this.getSelectedDirentPaths(); let dirNames = this.getSelectedDirentNames(); - this.setState({ updateDetail: !this.state.updateDetail }); + this.setState({ currentDirent: null }); seafileAPI.deleteMutipleDirents(repoID, this.state.path, dirNames).then(res => { if (this.state.isTreePanelShown) { this.deleteTreeNodes(direntPaths); @@ -1143,6 +1158,7 @@ class LibContentView extends React.Component { }; onMainPanelItemDelete = (dirent) => { + this.updateCurrentDirent(dirent); let path = Utils.joinPath(this.state.path, dirent.name); this.deleteItem(path, dirent.isDir()); }; @@ -1260,6 +1276,7 @@ class LibContentView extends React.Component { // list operations onMoveItem = (destRepo, dirent, moveToDirentPath, nodeParentPath) => { + this.updateCurrentDirent(dirent); let repoID = this.props.repoID; // just for view list state let dirName = dirent.name; @@ -1412,6 +1429,9 @@ class LibContentView extends React.Component { }; onDirentClick = (clickedDirent, event) => { + this.setState({ + currentDirent: clickedDirent && clickedDirent.isActive ? null : clickedDirent + }); const { direntList, selectedDirentList, lastSelectedIndex } = this.state; if (clickedDirent) { const clickedIndex = direntList.findIndex(dirent => dirent.name === clickedDirent.name); @@ -1527,6 +1547,9 @@ class LibContentView extends React.Component { }; onDirentSelected = (dirent) => { + this.setState({ + currentDirent: dirent && dirent.isActive ? null : dirent + }); let direntList = this.state.direntList.map(item => { if (item.name === dirent.name) { item.isSelected = !item.isSelected; @@ -2170,17 +2193,15 @@ class LibContentView extends React.Component { }; render() { + let { currentRepoInfo, userPerm, isCopyMoveProgressDialogShow, isDeleteFolderDialogOpen, currentDirent, + path, usedRepoTags } = this.state; if (this.state.libNeedDecrypt) { return ( - + ); } - if (this.state.libNeedDecryptWhenCopy || this.state.libNeedDecryptWhenMove) { return ( @@ -2191,22 +2212,19 @@ class LibContentView extends React.Component { ); } - if (this.state.errorMsg) { return ( - + <>

{this.state.errorMsg}

-
+ ); } - if (!this.state.currentRepoInfo) { return ''; } let enableDirPrivateShare = false; - let { currentRepoInfo, userPerm, isCopyMoveProgressDialogShow, isDeleteFolderDialogOpen, currentDirent } = this.state; let showShareBtn = Utils.isHasPermissionToShare(currentRepoInfo, userPerm); let isRepoOwner = currentRepoInfo.owner_email === username; let isVirtual = currentRepoInfo.is_virtual; @@ -2225,6 +2243,14 @@ class LibContentView extends React.Component { canUpload = upload; } + const isDesktop = Utils.isDesktop(); + let isRepoInfoBarShow = false; + if (path === '/') { + if (isDesktop && usedRepoTags.length !== 0) { + isRepoInfoBarShow = true; + } + } + return (
- +
+ {this.state.currentRepoInfo.status === 'read-only' && +
+ {gettext('This library has been set to read-only by admin and cannot be updated.')} +
+ } +
+
+ {this.state.isDirentSelected ? + + : + + } +
+ {isDesktop && +
+ +
+ } +
+
+ {this.state.pathExist ? + + : +
{gettext('Folder does not exist.')}
+ } + {this.state.isDirentDetailShow && ( + + )} +
+
{canUpload && this.state.pathExist && !this.state.isViewFile && this.state.currentMode !== METADATA_MODE && ( this.uploader = uploader}