1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-05 00:43:53 +00:00

[refactor] lib container view (#6766)

* [refactor] lib container view

* optimize codes
This commit is contained in:
Michael An
2024-09-13 20:28:20 +08:00
committed by GitHub
parent 3dae590dfc
commit 453c84f46f
7 changed files with 266 additions and 515 deletions

View File

@@ -5,7 +5,7 @@ import DirentDetail from './dirent-details';
import ObjectUtils from '../../metadata/metadata-view/utils/object-utils'; import ObjectUtils from '../../metadata/metadata-view/utils/object-utils';
import { MetadataContext } from '../../metadata'; 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(() => { useEffect(() => {
// init context // init context
@@ -45,7 +45,7 @@ const Index = React.memo(({ repoID, path, dirent, currentRepoInfo, repoTags, fil
return !isChanged; return !isChanged;
}); });
Index.propTypes = { DetailContainer.propTypes = {
repoID: PropTypes.string, repoID: PropTypes.string,
path: PropTypes.string, path: PropTypes.string,
dirent: PropTypes.object, dirent: PropTypes.object,
@@ -56,4 +56,4 @@ Index.propTypes = {
onFileTagChanged: PropTypes.func, onFileTagChanged: PropTypes.func,
}; };
export default Index; export default DetailContainer;

View File

@@ -101,7 +101,7 @@ class DirentDetails extends React.Component {
{this.renderImage()} {this.renderImage()}
{dirent && direntDetail && ( {dirent && direntDetail && (
<div className="detail-content"> <div className="detail-content">
{dirent.type !== 'file' ? ( {dirent.type !== 'file' ?
<DirDetails <DirDetails
repoID={repoID} repoID={repoID}
repoInfo={this.props.currentRepoInfo} repoInfo={this.props.currentRepoInfo}
@@ -109,7 +109,7 @@ class DirentDetails extends React.Component {
direntDetail={direntDetail} direntDetail={direntDetail}
path={this.props.dirent ? path + '/' + dirent.name : path} path={this.props.dirent ? path + '/' + dirent.name : path}
/> />
) : ( :
<FileDetails <FileDetails
repoID={repoID} repoID={repoID}
repoInfo={this.props.currentRepoInfo} repoInfo={this.props.currentRepoInfo}
@@ -120,7 +120,7 @@ class DirentDetails extends React.Component {
fileTagList={dirent ? dirent.file_tags : fileTags} fileTagList={dirent ? dirent.file_tags : fileTags}
onFileTagChanged={this.props.onFileTagChanged} onFileTagChanged={this.props.onFileTagChanged}
/> />
)} }
</div> </div>
)} )}
</Body> </Body>

View File

@@ -1,15 +1,15 @@
import React, { useEffect, useMemo, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Formatter } from '@seafile/sf-metadata-ui-component'; import { Formatter } from '@seafile/sf-metadata-ui-component';
import { Utils } from '../../../utils/utils'; import { Utils } from '../../utils/utils';
import { gettext } from '../../../utils/constants'; import { gettext } from '../../utils/constants';
import { seafileAPI } from '../../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import toaster from '../../toast'; import toaster from '../toast';
import { Detail, Header, Body } from '../detail'; import { Detail, Header, Body } from './detail';
import Repo from '../../../models/repo'; import Repo from '../../models/repo';
import Loading from '../../loading'; import Loading from '../loading';
import DetailItem from '../detail-item'; import DetailItem from './detail-item';
import { CellType } from '../../../metadata/metadata-view/_basic'; import { CellType } from '../../metadata/metadata-view/_basic';
const LibDetail = React.memo(({ currentRepoInfo, onClose }) => { const LibDetail = React.memo(({ currentRepoInfo, onClose }) => {
const [isLoading, setLoading] = useState(true); const [isLoading, setLoading] = useState(true);
@@ -36,9 +36,9 @@ const LibDetail = React.memo(({ currentRepoInfo, onClose }) => {
<Detail> <Detail>
<Header title={currentRepoInfo.repo_name} icon={smallIconUrl} onClose={onClose} /> <Header title={currentRepoInfo.repo_name} icon={smallIconUrl} onClose={onClose} />
<Body> <Body>
{isLoading ? ( {isLoading ?
<div className="w-100 h-100 d-flex algin-items-center justify-content-center"><Loading /></div> <div className="w-100 h-100 d-flex algin-items-center justify-content-center"><Loading /></div>
) : ( :
<div className="detail-content"> <div className="detail-content">
<DetailItem field={filesField} value={repo.file_count || 0} className="sf-metadata-property-detail-formatter"> <DetailItem field={filesField} value={repo.file_count || 0} className="sf-metadata-property-detail-formatter">
<Formatter field={filesField} value={repo.file_count || 0} /> <Formatter field={filesField} value={repo.file_count || 0} />
@@ -62,7 +62,7 @@ const LibDetail = React.memo(({ currentRepoInfo, onClose }) => {
<Formatter field={mtimeField} value={repo.last_modified} /> <Formatter field={mtimeField} value={repo.last_modified} />
</DetailItem> </DetailItem>
</div> </div>
)} }
</Body> </Body>
</Detail> </Detail>
); );

View File

@@ -42,7 +42,7 @@ const propTypes = {
isGroupOwnedRepo: PropTypes.bool.isRequired, isGroupOwnedRepo: PropTypes.bool.isRequired,
}; };
class MultipleDirOperationToolbar extends React.Component { class SelectedDirentsToolbar extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
@@ -471,6 +471,6 @@ class MultipleDirOperationToolbar extends React.Component {
} }
} }
MultipleDirOperationToolbar.propTypes = propTypes; SelectedDirentsToolbar.propTypes = propTypes;
export default MultipleDirOperationToolbar; export default SelectedDirentsToolbar;

View File

@@ -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 };

View File

@@ -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 = (<div className="message err-tip">{gettext('Folder does not exist.')}</div>);
}
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 (
<div className="cur-view-container">
{this.props.currentRepoInfo.status === 'read-only' &&
<div className="readonly-tip-message">
{gettext('This library has been set to read-only by admin and cannot be updated.')}
</div>
}
<div className="cur-view-path lib-cur-view-path">
<div className={classnames('cur-view-path-left', { 'w-100': !isDesktop, 'animation-children': hasSelectedFile })}>
{isDirentSelected ? (
<ToolbarForSelectedDirents
repoID={this.props.repoID}
path={this.props.path}
userPerm={this.props.userPerm}
repoEncrypted={this.props.repoEncrypted}
repoTags={this.props.repoTags}
selectedDirentList={this.props.selectedDirentList}
direntList={this.props.direntList}
onItemsMove={this.props.onItemsMove}
onItemsCopy={this.props.onItemsCopy}
onItemsDelete={this.props.onItemsDelete}
onItemRename={this.props.onItemRename}
isRepoOwner={this.props.isRepoOwner}
currentRepoInfo={this.props.currentRepoInfo}
enableDirPrivateShare={this.props.enableDirPrivateShare}
updateDirent={this.props.updateDirent}
unSelectDirent={this.props.unSelectDirent}
onFilesTagChanged={this.props.onFilesTagChanged}
showShareBtn={this.props.showShareBtn}
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
showDirentDetail={this.props.showDirentDetail}
currentMode={this.props.currentMode}
switchViewMode={this.props.switchViewMode}
/>
) : (
<CurDirPath
currentRepoInfo={this.props.currentRepoInfo}
repoID={repoID}
repoName={this.props.currentRepoInfo.repo_name}
repoEncrypted={this.props.repoEncrypted}
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
pathPrefix={this.props.pathPrefix}
currentPath={this.props.path}
userPerm={this.props.userPerm}
onTabNavClick={this.props.onTabNavClick}
onPathClick={this.onPathClick}
fileTags={this.props.fileTags}
direntList={this.props.direntList}
sortBy={this.props.sortBy}
sortOrder={this.props.sortOrder}
sortItems={this.props.sortItems}
toggleTreePanel={this.props.toggleTreePanel}
enableDirPrivateShare={this.props.enableDirPrivateShare}
showShareBtn={this.props.showShareBtn}
onAddFolder={this.props.onAddFolder}
onAddFile={this.props.onAddFile}
onUploadFile={this.props.onUploadFile}
onUploadFolder={this.props.onUploadFolder}
fullDirentList={this.props.fullDirentList}
filePermission={this.props.filePermission}
onFileTagChanged={this.props.onToolbarFileTagChanged}
repoTags={this.props.repoTags}
onItemMove={this.props.onItemMove}
isDesktop={isDesktop}
loadDirentList={this.props.loadDirentList}
/>
)}
</div>
{isDesktop &&
<div className="cur-view-path-right py-1">
<DirTool
repoID={repoID}
repoName={this.props.currentRepoInfo.repo_name}
userPerm={this.props.userPerm}
currentPath={path}
updateUsedRepoTags={this.props.updateUsedRepoTags}
onDeleteRepoTag={this.props.onDeleteRepoTag}
currentMode={this.props.currentMode}
switchViewMode={this.props.switchViewMode}
isCustomPermission={this.props.isCustomPermission}
sortBy={this.props.sortBy}
sortOrder={this.props.sortOrder}
sortItems={this.props.sortItems}
viewId={this.props.viewId}
viewType={this.props.viewType}
/>
</div>
}
</div>
<div className='cur-view-content lib-content-container' onScroll={this.onItemsScroll}>
{!this.props.pathExist && this.errMessage}
{this.props.pathExist && (
<DirColumnView
isSidePanelFolded={this.props.isSidePanelFolded}
isTreePanelShown={this.props.isTreePanelShown}
currentMode={this.props.currentMode}
currentDirent={this.props.currentDirent}
path={this.props.path}
repoID={repoID}
currentRepoInfo={this.props.currentRepoInfo}
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
userPerm={this.props.userPerm}
enableDirPrivateShare={this.props.enableDirPrivateShare}
isTreeDataLoading={this.props.isTreeDataLoading}
treeData={this.props.treeData}
currentNode={this.props.currentNode}
onNodeClick={this.props.onNodeClick}
onNodeCollapse={this.props.onNodeCollapse}
onNodeExpanded={this.props.onNodeExpanded}
onAddFolderNode={this.props.onAddFolder}
onAddFileNode={this.props.onAddFile}
onRenameNode={this.props.onRenameNode}
onDeleteNode={this.props.onDeleteNode}
isViewFile={this.props.isViewFile}
isFileLoading={this.props.isFileLoading}
filePermission={this.props.filePermission}
content={this.props.content}
viewId={this.props.viewId}
lastModified={this.props.lastModified}
latestContributor={this.props.latestContributor}
onLinkClick={this.props.onLinkClick}
isRepoInfoBarShow={isRepoInfoBarShow}
repoTags={this.props.repoTags}
usedRepoTags={this.props.usedRepoTags}
updateUsedRepoTags={this.props.updateUsedRepoTags}
isDirentListLoading={this.props.isDirentListLoading}
direntList={this.props.direntList}
fullDirentList={this.props.fullDirentList}
sortBy={this.props.sortBy}
sortOrder={this.props.sortOrder}
sortItems={this.props.sortItems}
onAddFolder={this.props.onAddFolder}
onAddFile={this.props.onAddFile}
onItemClick={this.onItemClick}
onItemSelected={this.onItemSelected}
onItemDelete={this.onItemDelete}
onItemRename={this.props.onItemRename}
onItemMove={this.onItemMove}
onItemCopy={this.props.onItemCopy}
onItemConvert={this.props.onItemConvert}
onDirentClick={this.onDirentClick}
updateDirent={this.props.updateDirent}
isAllItemSelected={this.props.isAllDirentSelected}
onAllItemSelected={this.props.onAllDirentSelected}
selectedDirentList={this.props.selectedDirentList}
onSelectedDirentListUpdate={this.props.onSelectedDirentListUpdate}
onItemsMove={this.props.onItemsMove}
onItemsCopy={this.props.onItemsCopy}
onItemsDelete={this.props.onItemsDelete}
onFileTagChanged={this.props.onFileTagChanged}
showDirentDetail={this.props.showDirentDetail}
onItemsScroll={this.onItemsScroll}
isDirentDetailShow={this.props.isDirentDetailShow}
eventBus={this.props.eventBus}
onCloseMarkdownViewDialog={this.props.onCloseMarkdownViewDialog}
getMarkDownFilePath={this.props.getMarkDownFilePath}
getMarkDownFileName={this.props.getMarkDownFileName}
openMarkdownFile={this.props.openMarkdownFile}
/>
)}
{this.props.isDirentDetailShow && (
<Detail
path={path}
repoID={repoID}
currentRepoInfo={this.props.currentRepoInfo}
dirent={this.state.currentDirent}
repoTags={this.props.repoTags}
fileTags={this.props.isViewFile ? this.props.fileTags : []}
onFileTagChanged={this.props.onFileTagChanged}
onClose={this.props.closeDirentDetail}
/>
)}
</div>
</div>
);
}
}
LibContentContainer.propTypes = propTypes;
export default LibContentContainer;

View File

@@ -1,7 +1,8 @@
import React, { Fragment } from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import cookie from 'react-cookies'; import cookie from 'react-cookies';
import moment from 'moment'; import moment from 'moment';
import classnames from 'classnames';
import MediaQuery from 'react-responsive'; import MediaQuery from 'react-responsive';
import { Modal } from 'reactstrap'; import { Modal } from 'reactstrap';
import { navigate } from '@gatsbyjs/reach-router'; import { navigate } from '@gatsbyjs/reach-router';
@@ -9,16 +10,12 @@ import { gettext, siteRoot, username, enableVideoThumbnail, enablePDFThumbnail,
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import collabServer from '../../utils/collab-server'; import collabServer from '../../utils/collab-server';
import Dirent from '../../models/dirent'; import { Dirent, FileTag, RepoTag, RepoInfo } from '../../models';
import FileTag from '../../models/file-tag';
import RepoTag from '../../models/repo-tag';
import RepoInfo from '../../models/repo-info';
import TreeNode from '../../components/tree-view/tree-node'; import TreeNode from '../../components/tree-view/tree-node';
import treeHelper from '../../components/tree-view/tree-helper'; import treeHelper from '../../components/tree-view/tree-helper';
import toaster from '../../components/toast'; import toaster from '../../components/toast';
import ModalPortal from '../../components/modal-portal'; import ModalPortal from '../../components/modal-portal';
import LibDecryptDialog from '../../components/dialog/lib-decrypt-dialog'; import LibDecryptDialog from '../../components/dialog/lib-decrypt-dialog';
import LibContentContainer from './lib-content-container';
import FileUploader from '../../components/file-uploader/file-uploader'; import FileUploader from '../../components/file-uploader/file-uploader';
import CopyMoveDirentProgressDialog from '../../components/dialog/copy-move-dirent-progress-dialog'; import CopyMoveDirentProgressDialog from '../../components/dialog/copy-move-dirent-progress-dialog';
import DeleteFolderDialog from '../../components/dialog/delete-folder-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 { PRIVATE_FILE_TYPE } from '../../constants';
import { MetadataProvider, CollaboratorsProvider } from '../../metadata/hooks'; import { MetadataProvider, CollaboratorsProvider } from '../../metadata/hooks';
import { LIST_MODE, METADATA_MODE } from '../../components/dir-view-mode/constants'; 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 = { const propTypes = {
eventBus: PropTypes.object, eventBus: PropTypes.object,
@@ -80,7 +84,6 @@ class LibContentView extends React.Component {
errorMsg: '', errorMsg: '',
isDirentDetailShow: false, isDirentDetailShow: false,
direntDetailPanelTab: '', direntDetailPanelTab: '',
updateDetail: false,
itemsShowLength: 100, itemsShowLength: 100,
isSessionExpired: false, isSessionExpired: false,
isCopyMoveProgressDialogShow: false, isCopyMoveProgressDialogShow: false,
@@ -104,6 +107,23 @@ class LibContentView extends React.Component {
this.unsubscribeEventBus = null; 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) => { showDirentDetail = (direntDetailPanelTab) => {
if (direntDetailPanelTab) { if (direntDetailPanelTab) {
this.setState({ direntDetailPanelTab: 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.lastModifyTime = new Date();
this.props.eventBus.dispatch(EVENT_BUS_TYPE.CURRENT_LIBRARY_CHANGED, { this.props.eventBus.dispatch(EVENT_BUS_TYPE.CURRENT_LIBRARY_CHANGED, {
repoID: this.props.repoID, repoID: this.props.repoID,
@@ -386,23 +409,15 @@ class LibContentView extends React.Component {
}; };
handleMarkdownFile = (path) => { handleMarkdownFile = (path) => {
seafileAPI.getFileInfo(this.props.repoID, path) seafileAPI.getFileInfo(this.props.repoID, path).then(() => {
.then(() => { this.loadSidePanel(path);
/* this.showFile(path);
if (this.state.currentMode !== 'column') { }).catch(() => {
cookie.save('seafile_view_mode', 'column'); if (this.state.isTreePanelShown) {
this.setState({currentMode: 'column'});
}
*/
this.loadSidePanel(path); this.loadSidePanel(path);
this.showFile(path); }
}) this.showDir(path);
.catch(() => { });
if (this.state.isTreePanelShown) {
this.loadSidePanel(path);
}
this.showDir(path);
});
}; };
handleNonMarkdownFile = (path) => { handleNonMarkdownFile = (path) => {
@@ -950,7 +965,7 @@ class LibContentView extends React.Component {
let direntPaths = this.getSelectedDirentPaths(); let direntPaths = this.getSelectedDirentPaths();
let dirNames = this.getSelectedDirentNames(); let dirNames = this.getSelectedDirentNames();
this.setState({ updateDetail: !this.state.updateDetail }); this.setState({ currentDirent: null });
seafileAPI.deleteMutipleDirents(repoID, this.state.path, dirNames).then(res => { seafileAPI.deleteMutipleDirents(repoID, this.state.path, dirNames).then(res => {
if (this.state.isTreePanelShown) { if (this.state.isTreePanelShown) {
this.deleteTreeNodes(direntPaths); this.deleteTreeNodes(direntPaths);
@@ -1143,6 +1158,7 @@ class LibContentView extends React.Component {
}; };
onMainPanelItemDelete = (dirent) => { onMainPanelItemDelete = (dirent) => {
this.updateCurrentDirent(dirent);
let path = Utils.joinPath(this.state.path, dirent.name); let path = Utils.joinPath(this.state.path, dirent.name);
this.deleteItem(path, dirent.isDir()); this.deleteItem(path, dirent.isDir());
}; };
@@ -1260,6 +1276,7 @@ class LibContentView extends React.Component {
// list operations // list operations
onMoveItem = (destRepo, dirent, moveToDirentPath, nodeParentPath) => { onMoveItem = (destRepo, dirent, moveToDirentPath, nodeParentPath) => {
this.updateCurrentDirent(dirent);
let repoID = this.props.repoID; let repoID = this.props.repoID;
// just for view list state // just for view list state
let dirName = dirent.name; let dirName = dirent.name;
@@ -1412,6 +1429,9 @@ class LibContentView extends React.Component {
}; };
onDirentClick = (clickedDirent, event) => { onDirentClick = (clickedDirent, event) => {
this.setState({
currentDirent: clickedDirent && clickedDirent.isActive ? null : clickedDirent
});
const { direntList, selectedDirentList, lastSelectedIndex } = this.state; const { direntList, selectedDirentList, lastSelectedIndex } = this.state;
if (clickedDirent) { if (clickedDirent) {
const clickedIndex = direntList.findIndex(dirent => dirent.name === clickedDirent.name); const clickedIndex = direntList.findIndex(dirent => dirent.name === clickedDirent.name);
@@ -1527,6 +1547,9 @@ class LibContentView extends React.Component {
}; };
onDirentSelected = (dirent) => { onDirentSelected = (dirent) => {
this.setState({
currentDirent: dirent && dirent.isActive ? null : dirent
});
let direntList = this.state.direntList.map(item => { let direntList = this.state.direntList.map(item => {
if (item.name === dirent.name) { if (item.name === dirent.name) {
item.isSelected = !item.isSelected; item.isSelected = !item.isSelected;
@@ -2170,17 +2193,15 @@ class LibContentView extends React.Component {
}; };
render() { render() {
let { currentRepoInfo, userPerm, isCopyMoveProgressDialogShow, isDeleteFolderDialogOpen, currentDirent,
path, usedRepoTags } = this.state;
if (this.state.libNeedDecrypt) { if (this.state.libNeedDecrypt) {
return ( return (
<ModalPortal> <ModalPortal>
<LibDecryptDialog <LibDecryptDialog repoID={this.props.repoID} onLibDecryptDialog={this.onLibDecryptDialog} />
repoID={this.props.repoID}
onLibDecryptDialog={this.onLibDecryptDialog}
/>
</ModalPortal> </ModalPortal>
); );
} }
if (this.state.libNeedDecryptWhenCopy || this.state.libNeedDecryptWhenMove) { if (this.state.libNeedDecryptWhenCopy || this.state.libNeedDecryptWhenMove) {
return ( return (
<ModalPortal> <ModalPortal>
@@ -2191,22 +2212,19 @@ class LibContentView extends React.Component {
</ModalPortal> </ModalPortal>
); );
} }
if (this.state.errorMsg) { if (this.state.errorMsg) {
return ( return (
<Fragment> <>
<p className="error mt-6 text-center">{this.state.errorMsg}</p> <p className="error mt-6 text-center">{this.state.errorMsg}</p>
<button type="submit" className="btn btn-primary submit" onClick={this.handleSubmit}>{gettext('Leave Share')}</button> <button type="submit" className="btn btn-primary submit" onClick={this.handleSubmit}>{gettext('Leave Share')}</button>
</Fragment> </>
); );
} }
if (!this.state.currentRepoInfo) { if (!this.state.currentRepoInfo) {
return ''; return '';
} }
let enableDirPrivateShare = false; let enableDirPrivateShare = false;
let { currentRepoInfo, userPerm, isCopyMoveProgressDialogShow, isDeleteFolderDialogOpen, currentDirent } = this.state;
let showShareBtn = Utils.isHasPermissionToShare(currentRepoInfo, userPerm); let showShareBtn = Utils.isHasPermissionToShare(currentRepoInfo, userPerm);
let isRepoOwner = currentRepoInfo.owner_email === username; let isRepoOwner = currentRepoInfo.owner_email === username;
let isVirtual = currentRepoInfo.is_virtual; let isVirtual = currentRepoInfo.is_virtual;
@@ -2225,6 +2243,14 @@ class LibContentView extends React.Component {
canUpload = upload; canUpload = upload;
} }
const isDesktop = Utils.isDesktop();
let isRepoInfoBarShow = false;
if (path === '/') {
if (isDesktop && usedRepoTags.length !== 0) {
isRepoInfoBarShow = true;
}
}
return ( return (
<MetadataProvider <MetadataProvider
repoID={this.props.repoID} repoID={this.props.repoID}
@@ -2233,95 +2259,184 @@ class LibContentView extends React.Component {
> >
<CollaboratorsProvider repoID={this.props.repoID}> <CollaboratorsProvider repoID={this.props.repoID}>
<div className="main-panel-center flex-row"> <div className="main-panel-center flex-row">
<LibContentContainer <div className="cur-view-container">
isSidePanelFolded={this.props.isSidePanelFolded} {this.state.currentRepoInfo.status === 'read-only' &&
repoEncrypted={this.state.repoEncrypted} <div className="readonly-tip-message">
isRepoOwner={isRepoOwner} {gettext('This library has been set to read-only by admin and cannot be updated.')}
onFilesTagChanged={this.onFileTagChanged} </div>
unSelectDirent={this.unSelectDirent} }
pathPrefix={this.props.pathPrefix} <div className="cur-view-path lib-cur-view-path">
isTreePanelShown={this.state.isTreePanelShown} <div className={classnames(
toggleTreePanel={this.toggleTreePanel} 'cur-view-path-left', {
currentMode={this.state.currentMode} 'w-100': !isDesktop,
switchViewMode={this.switchViewMode} 'animation-children': this.state.isDirentSelected
isCustomPermission={isCustomPermission} })}>
path={this.state.path} {this.state.isDirentSelected ?
pathExist={this.state.pathExist} <SelectedDirentsToolbar
currentRepoInfo={this.state.currentRepoInfo} repoID={this.props.repoID}
repoID={this.props.repoID} path={this.state.path}
enableDirPrivateShare={enableDirPrivateShare} userPerm={userPerm}
userPerm={userPerm} repoEncrypted={this.state.repoEncrypted}
isGroupOwnedRepo={this.state.isGroupOwnedRepo} repoTags={this.state.repoTags}
onTabNavClick={this.props.onTabNavClick} selectedDirentList={this.state.selectedDirentList}
onMainNavBarClick={this.onMainNavBarClick} direntList={direntItemsList}
isViewFile={this.state.isViewFile} onItemsMove={this.onMoveItems}
fileTags={this.state.fileTags} onItemsCopy={this.onCopyItems}
isFileLoading={this.state.isFileLoading} onItemsDelete={this.onDeleteItems}
filePermission={this.state.filePermission} onItemRename={this.onMainPanelItemRename}
content={this.state.content} isRepoOwner={isRepoOwner}
viewId={this.state.viewId} currentRepoInfo={this.state.currentRepoInfo}
lastModified={this.state.lastModified} enableDirPrivateShare={enableDirPrivateShare}
latestContributor={this.state.latestContributor} updateDirent={this.updateDirent}
onLinkClick={this.onLinkClick} unSelectDirent={this.unSelectDirent}
isTreeDataLoading={this.state.isTreeDataLoading} onFilesTagChanged={this.onFileTagChanged}
treeData={this.state.treeData} showShareBtn={showShareBtn}
currentNode={this.state.currentNode} isGroupOwnedRepo={this.state.isGroupOwnedRepo}
onNodeClick={this.onTreeNodeClick} showDirentDetail={this.showDirentDetail}
onNodeCollapse={this.onTreeNodeCollapse} currentMode={this.state.currentMode}
onNodeExpanded={this.onTreeNodeExpanded} switchViewMode={this.switchViewMode}
onAddFolderNode={this.onAddFolder} />
onAddFileNode={this.onAddFile} :
onRenameNode={this.onRenameTreeNode} <CurDirPath
onDeleteNode={this.onDeleteTreeNode} currentRepoInfo={this.state.currentRepoInfo}
repoTags={this.state.repoTags} repoID={this.props.repoID}
usedRepoTags={this.state.usedRepoTags} repoName={this.state.currentRepoInfo.repo_name}
updateUsedRepoTags={this.updateUsedRepoTags} repoEncrypted={this.state.repoEncrypted}
isDirentListLoading={this.state.isDirentListLoading} isGroupOwnedRepo={this.state.isGroupOwnedRepo}
direntList={direntItemsList} pathPrefix={this.props.pathPrefix}
fullDirentList={this.state.direntList} currentPath={this.state.path}
sortBy={this.state.sortBy} userPerm={userPerm}
sortOrder={this.state.sortOrder} onTabNavClick={this.props.onTabNavClick}
currentDirent={currentDirent} onPathClick={this.onMainNavBarClick}
sortItems={this.sortItems} fileTags={this.state.fileTags}
updateDirent={this.updateDirent} direntList={direntItemsList}
onDirentClick={this.onDirentClick} sortBy={this.state.sortBy}
onItemClick={this.onItemClick} sortOrder={this.state.sortOrder}
onItemSelected={this.onDirentSelected} sortItems={this.sortItems}
onItemDelete={this.onMainPanelItemDelete} toggleTreePanel={this.toggleTreePanel}
onItemRename={this.onMainPanelItemRename} enableDirPrivateShare={enableDirPrivateShare}
onItemMove={this.onMoveItem} showShareBtn={showShareBtn}
onItemCopy={this.onCopyItem} onAddFolder={this.onAddFolder}
onItemConvert={this.onConvertItem} onAddFile={this.onAddFile}
onAddFolder={this.onAddFolder} onUploadFile={this.onUploadFile}
onAddFile={this.onAddFile} onUploadFolder={this.onUploadFolder}
onFileTagChanged={this.onFileTagChanged} fullDirentList={this.state.direntList}
isDirentSelected={this.state.isDirentSelected} filePermission={this.state.filePermission}
isAllDirentSelected={this.state.isAllDirentSelected} onFileTagChanged={this.onToolbarFileTagChanged}
onAllDirentSelected={this.onAllDirentSelected} repoTags={this.state.repoTags}
isDirentDetailShow={this.state.isDirentDetailShow} onItemMove={this.onMoveItem}
selectedDirent={this.state.selectedDirentList && this.state.selectedDirentList[0]} isDesktop={isDesktop}
selectedDirentList={this.state.selectedDirentList} loadDirentList={this.loadDirentList}
onSelectedDirentListUpdate={this.onSelectedDirentListUpdate} />
onItemsMove={this.onMoveItems} }
onItemsCopy={this.onCopyItems} </div>
onItemsDelete={this.onDeleteItems} {isDesktop &&
closeDirentDetail={this.closeDirentDetail} <div className="cur-view-path-right py-1">
showDirentDetail={this.showDirentDetail} <DirTool
direntDetailPanelTab={this.state.direntDetailPanelTab} repoID={this.props.repoID}
onDeleteRepoTag={this.onDeleteRepoTag} repoName={this.state.currentRepoInfo.repo_name}
onToolbarFileTagChanged={this.onToolbarFileTagChanged} userPerm={userPerm}
updateDetail={this.state.updateDetail} currentPath={path}
onListContainerScroll={this.onListContainerScroll} updateUsedRepoTags={this.updateUsedRepoTags}
loadDirentList={this.loadDirentList} onDeleteRepoTag={this.onDeleteRepoTag}
showShareBtn={showShareBtn} currentMode={this.state.currentMode}
onUploadFile={this.onUploadFile} switchViewMode={this.switchViewMode}
onUploadFolder={this.onUploadFolder} isCustomPermission={isCustomPermission}
eventBus={this.props.eventBus} sortBy={this.state.sortBy}
onCloseMarkdownViewDialog={this.onCloseMarkdownViewDialog} sortOrder={this.state.sortOrder}
getMarkDownFilePath={this.getMarkDownFilePath} sortItems={this.sortItems}
getMarkDownFileName={this.getMarkDownFileName} viewId={this.state.viewId}
openMarkdownFile={this.openMarkdownFile} viewType={this.props.viewType}
/> />
</div>
}
</div>
<div className='cur-view-content lib-content-container' onScroll={this.onItemsScroll}>
{this.state.pathExist ?
<DirColumnView
isSidePanelFolded={this.props.isSidePanelFolded}
isTreePanelShown={this.state.isTreePanelShown}
currentMode={this.state.currentMode}
currentDirent={currentDirent}
path={this.state.path}
repoID={this.props.repoID}
currentRepoInfo={this.state.currentRepoInfo}
isGroupOwnedRepo={this.state.isGroupOwnedRepo}
userPerm={userPerm}
enableDirPrivateShare={enableDirPrivateShare}
isTreeDataLoading={this.state.isTreeDataLoading}
treeData={this.state.treeData}
currentNode={this.state.currentNode}
onNodeClick={this.onTreeNodeClick}
onNodeCollapse={this.onTreeNodeCollapse}
onNodeExpanded={this.onTreeNodeExpanded}
onAddFolderNode={this.onAddFolder}
onAddFileNode={this.onAddFile}
onRenameNode={this.onRenameTreeNode}
onDeleteNode={this.onDeleteTreeNode}
isViewFile={this.state.isViewFile}
isFileLoading={this.state.isFileLoading}
filePermission={this.state.filePermission}
content={this.state.content}
viewId={this.state.viewId}
lastModified={this.state.lastModified}
latestContributor={this.state.latestContributor}
onLinkClick={this.onLinkClick}
isRepoInfoBarShow={isRepoInfoBarShow}
repoTags={this.state.repoTags}
usedRepoTags={this.state.usedRepoTags}
updateUsedRepoTags={this.updateUsedRepoTags}
isDirentListLoading={this.state.isDirentListLoading}
direntList={direntItemsList}
fullDirentList={this.state.direntList}
sortBy={this.state.sortBy}
sortOrder={this.state.sortOrder}
sortItems={this.sortItems}
onAddFolder={this.onAddFolder}
onAddFile={this.onAddFile}
onItemClick={this.onItemClick}
onItemSelected={this.onDirentSelected}
onItemDelete={this.onMainPanelItemDelete}
onItemRename={this.onMainPanelItemRename}
onItemMove={this.onMoveItem}
onItemCopy={this.onCopyItem}
onItemConvert={this.onConvertItem}
onDirentClick={this.onDirentClick}
updateDirent={this.updateDirent}
isAllItemSelected={this.state.isAllDirentSelected}
onAllItemSelected={this.onAllDirentSelected}
selectedDirentList={this.state.selectedDirentList}
onSelectedDirentListUpdate={this.onSelectedDirentListUpdate}
onItemsMove={this.onMoveItems}
onItemsCopy={this.onCopyItems}
onItemsDelete={this.onDeleteItems}
onFileTagChanged={this.onFileTagChanged}
showDirentDetail={this.showDirentDetail}
onItemsScroll={this.onItemsScroll}
isDirentDetailShow={this.state.isDirentDetailShow}
eventBus={this.props.eventBus}
onCloseMarkdownViewDialog={this.onCloseMarkdownViewDialog}
getMarkDownFilePath={this.getMarkDownFilePath}
getMarkDownFileName={this.getMarkDownFileName}
openMarkdownFile={this.openMarkdownFile}
/>
:
<div className="message err-tip">{gettext('Folder does not exist.')}</div>
}
{this.state.isDirentDetailShow && (
<DetailContainer
path={this.state.path}
repoID={this.props.repoID}
currentRepoInfo={this.state.currentRepoInfo}
dirent={this.state.currentDirent}
repoTags={this.state.repoTags}
fileTags={this.state.isViewFile ? this.state.fileTags : []}
onFileTagChanged={this.onFileTagChanged}
onClose={this.closeDirentDetail}
/>
)}
</div>
</div>
{canUpload && this.state.pathExist && !this.state.isViewFile && this.state.currentMode !== METADATA_MODE && ( {canUpload && this.state.pathExist && !this.state.isViewFile && this.state.currentMode !== METADATA_MODE && (
<FileUploader <FileUploader
ref={uploader => this.uploader = uploader} ref={uploader => this.uploader = uploader}