1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-04 00:20:07 +00:00

Lib view mode (#2975)

* [update dir layout]rewrite dir layout

* add file menu handler

* optimized code

* add file load error message for file component

* repair pencil bug

* delete unnecessary file

* reapir file-chooser bug

* add file uploader module

* rename component name
This commit is contained in:
杨顺强
2019-02-21 17:37:04 +08:00
committed by Daniel Pan
parent a1c3186765
commit 0fccd9530b
19 changed files with 931 additions and 473 deletions

View File

@@ -54,7 +54,7 @@ class TagListItem extends React.Component {
{this.props.item.fileCount}{' '}{'files'}
</span>
</div>
<i className="tag-edit fa fa-pencil" onClick={this.onTagUpdate}></i>
<i className="tag-edit fa fa-pencil-alt" onClick={this.onTagUpdate}></i>
</li>
);
}

View File

@@ -39,7 +39,7 @@ class ReadmeDialog extends React.Component {
return (
<Modal isOpen={true} toggle={this.props.toggleCancel} className="readme-dialog" size="lg">
<ModalHeader>{this.props.fileName}
<a className="readme-dialog-edit" href={this.props.href} target='_blank'><i className="fa fa-pencil"></i></a>
<a className="readme-dialog-edit" href={this.props.href} target='_blank'><i className="fa fa-pencil-alt"></i></a>
</ModalHeader>
<ModalBody>
{this.state.isLoading ?

View File

@@ -0,0 +1,104 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Utils } from '../../utils/utils';
import { gettext, siteRoot } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import WikiMarkdownViewer from '../wiki-markdown-viewer';
const propTypes = {
path: PropTypes.string.isRequired,
repoID: PropTypes.string.isRequired,
hash: PropTypes.string,
isDraft: PropTypes.bool,
hasDraft: PropTypes.bool,
goDraftPage: PropTypes.func.isRequired,
reviewStatus: PropTypes.any,
goReviewPage: PropTypes.func.isRequired,
isFileLoading: PropTypes.bool.isRequired,
isFileLoadedErr: PropTypes.bool.isRequired,
filePermission: PropTypes.bool,
content: PropTypes.string,
lastModified: PropTypes.string,
latestContributor: PropTypes.string,
onLinkClick: PropTypes.func.isRequired,
};
class DirColumnFile extends React.Component {
componentDidMount() {
if (this.props.hash) {
let hash = this.props.hash;
setTimeout(function() {
window.location.hash = hash;
}, 500);
}
}
onEditClick = (e) => {
e.preventDefault();
let { path, repoID } = this.props;
let url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(path) + '?mode=edit';
window.open(url);
}
onNewDraft = (e) => {
e.preventDefault();
let { path, repoID } = this.props;
seafileAPI.createDraft(repoID, path).then(res => {
window.location.href = siteRoot + 'lib/' + res.data.origin_repo_id + '/file' + res.data.draft_file_path + '?mode=edit';
});
}
goDraftPage = (e) => {
e.preventDefault();
this.props.goDraftPage();
}
goReviewPage = (e) => {
e.preventDefault();
this.props.goReviewPage();
}
render() {
if (this.props.isFileLoadedErr) {
return (
<div className="message err-tip">{gettext('File does not exist.')}</div>
);
}
return (
<div className="cur-view-content">
<WikiMarkdownViewer
isTOCShow={false}
isFileLoading={this.props.isFileLoading}
markdownContent={this.props.content}
lastModified = {this.props.lastModified}
latestContributor={this.props.latestContributor}
onLinkClick={this.props.onLinkClick}
>
<Fragment>
{this.props.reviewStatus === 'open' &&
<div className='seafile-btn-view-review text-center'>
<div className='tag tag-green'>
{gettext('This file is in review stage')}
<span className="ml-2" onClick={this.goReviewPage}>{gettext('View Review')}</span>
</div>
</div>
}
{(this.props.reviewStatus !== 'open' && !this.props.isDraft && this.props.hasDraft) &&
<div className='seafile-btn-view-review text-center'>
<div className='tag tag-green'>
{gettext('This file is in draft stage.')}
<span className="ml-2" onClick={this.goDraftPage}>{gettext('Edit Draft')}</span>
</div>
</div>
}
</Fragment>
</WikiMarkdownViewer>
</div>
);
}
}
DirColumnFile.propTypes = propTypes;
export default DirColumnFile;

View File

@@ -0,0 +1,200 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import TreeView from '../../components/tree-view/tree-view';
import Loading from '../../components/loading';
import ModalPortal from '../../components/modal-portal';
import Delete from '../../components/dialog/delete-dialog';
import Rename from '../../components/dialog/rename-dialog';
import CreateFolder from '../../components/dialog/create-folder-dialog';
import CreateFile from '../../components/dialog/create-file-dialog';
const propTypes = {
currentPath: PropTypes.string.isRequired,
repoPermission: PropTypes.bool.isRequired,
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,
};
class DirColumnNav extends React.Component {
constructor(props) {
super(props);
this.state = {
opNode: null,
isDeleteDialogShow: false,
isAddFileDialogShow: false,
isAddFolderDialogShow: false,
isRenameDialogShow: false,
};
this.isNodeMenuShow = true;
}
componentWillReceiveProps(nextProps) {
this.setState({opNode: nextProps.currentNode});
}
onNodeClick = (node) => {
this.setState({opNode: node});
this.props.onNodeClick(node);
}
onMenuItemClick = (operation, node) => {
this.setState({opNode: node});
switch (operation) {
case 'New Folder':
this.onAddFolderToggle();
break;
case 'New File':
this.onAddFileToggle();
break;
case 'Rename':
this.onRenameToggle();
break;
case 'Delete':
this.onDeleteToggle();
break;
}
}
onAddFileToggle = (type) => {
if (type === 'root') {
let root = this.props.treeData.root;
this.setState({
isAddFileDialogShow: !this.state.isAddFileDialogShow,
opNode: root,
});
} else {
this.setState({isAddFileDialogShow: !this.state.isAddFileDialogShow});
}
}
onAddFolderToggle = (type) => {
if (type === 'root') {
let root = this.props.treeData.root;
this.setState({
isAddFolderDialogShow: !this.state.isAddFolderDialogShow,
opNode: root,
});
} else {
this.setState({isAddFolderDialogShow: !this.state.isAddFolderDialogShow});
}
}
onRenameToggle = () => {
this.setState({isRenameDialogShow: !this.state.isRenameDialogShow});
}
onDeleteToggle = () => {
this.setState({isDeleteDialogShow: !this.state.isDeleteDialogShow});
}
onAddFolderNode = (dirPath) => {
this.setState({isAddFolderDialogShow: !this.state.isAddFolderDialogShow});
this.props.onAddFolderNode(dirPath);
}
onAddFileNode = (filePath, isDraft) => {
this.setState({isAddFileDialogShow: !this.state.isAddFileDialogShow});
this.props.onAddFileNode(filePath, isDraft);
}
onRenameNode = (newName) => {
this.setState({isRenameDialogShow: !this.state.isRenameDialogShow});
let node = this.state.opNode;
this.props.onRenameNode(node, newName);
}
onDeleteNode = () => {
this.setState({isDeleteDialogShow: !this.state.isDeleteDialogShow});
let node = this.state.opNode;
this.props.onDeleteNode(node);
}
checkDuplicatedName = (newName) => {
let node = this.state.opNode;
// root node to new node conditions: parentNode is null,
let parentNode = node.parentNode ? node.parentNode : node;
let childrenObject = parentNode.children.map(item => {
return item.object;
});
let isDuplicated = childrenObject.some(object => {
return object.name === newName;
});
return isDuplicated;
}
render() {
return (
<Fragment>
<div className="dir-content-nav" role="navigation">
{this.props.isTreeDataLoading ?
(<Loading/>) :
(<TreeView
repoPermission={this.props.repoPermission}
isNodeMenuShow={this.isNodeMenuShow}
treeData={this.props.treeData}
currentPath={this.props.currentPath}
onNodeClick={this.onNodeClick}
onNodeExpanded={this.props.onNodeExpanded}
onNodeCollapse={this.props.onNodeCollapse}
onMenuItemClick={this.onMenuItemClick}
onFreezedItem={this.onFreezedItem}
onUnFreezedItem={this.onUnFreezedItem}
/>)
}
</div>
{this.state.isAddFolderDialogShow && (
<ModalPortal>
<CreateFolder
parentPath={this.state.opNode.path}
onAddFolder={this.onAddFolderNode}
checkDuplicatedName={this.checkDuplicatedName}
addFolderCancel={this.onAddFolderToggle}
/>
</ModalPortal>
)}
{this.state.isAddFileDialogShow && (
<ModalPortal>
<CreateFile
parentPath={this.state.opNode.path}
onAddFile={this.onAddFileNode}
checkDuplicatedName={this.checkDuplicatedName}
addFileCancel={this.onAddFileToggle}
/>
</ModalPortal>
)}
{this.state.isRenameDialogShow && (
<ModalPortal>
<Rename
currentNode={this.state.opNode}
onRename={this.onRenameNode}
checkDuplicatedName={this.checkDuplicatedName}
toggleCancel={this.onRenameToggle}
/>
</ModalPortal>
)}
{this.state.isDeleteDialogShow && (
<ModalPortal>
<Delete
currentNode={this.state.opNode}
handleSubmit={this.onDeleteNode}
toggleCancel={this.onDeleteToggle}
/>
</ModalPortal>
)}
</Fragment>
);
}
}
DirColumnNav.propTypes = propTypes;
export default DirColumnNav;

View File

@@ -0,0 +1,155 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import DirColumnNav from './dir-column-nav';
import DirColumnFile from './dir-column-file';
import DirListMode from './dir-list-view';
import '../../css/lib-content-view.css';
const propTypes = {
path: PropTypes.string.isRequired,
repoID: PropTypes.string.isRequired,
// repoinfo
currentRepoInfo: PropTypes.object.isRequired,
repoPermission: PropTypes.bool.isRequired,
repoEncrypted: PropTypes.bool.isRequired,
enableDirPrivateShare: PropTypes.bool.isRequired,
userPrem: PropTypes.bool,
isAdmin: PropTypes.bool.isRequired,
isRepoOwner: PropTypes.bool.isRequired,
isGroupOwnedRepo: PropTypes.bool.isRequired,
// 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,
// file
isViewFile: PropTypes.bool.isRequired,
isFileLoading: PropTypes.bool.isRequired,
isFileLoadedErr: PropTypes.bool.isRequired,
hash: PropTypes.string,
isDraft: PropTypes.bool.isRequired,
hasDraft: PropTypes.bool.isRequired,
goDraftPage: PropTypes.func.isRequired,
reviewStatus: PropTypes.string,
goReviewPage: PropTypes.func.isRequired,
filePermission: PropTypes.bool.isRequired,
content: PropTypes.string,
lastModified: PropTypes.string,
latestContributor: PropTypes.string,
onLinkClick: PropTypes.func.isRequired,
// repo content
isRepoInfoBarShow: PropTypes.bool.isRequired,
draftCounts: PropTypes.number.isRequired,
reviewCounts: PropTypes.number.isRequired,
usedRepoTags: PropTypes.array.isRequired,
readmeMarkdown: PropTypes.object,
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,
onAddFile: 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,
onItemDetails: PropTypes.func.isRequired,
onDirentClick: PropTypes.func.isRequired,
isAllItemSelected: PropTypes.bool.isRequired,
onAllItemSelected: PropTypes.func.isRequired,
};
class DirColumnView extends React.Component {
render() {
return (
<Fragment>
<DirColumnNav
currentPath={this.props.path}
repoPermission={this.props.repoPermission}
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.onAddFolderNode}
onAddFileNode={this.props.onAddFileNode}
onRenameNode={this.props.onRenameNode}
onDeleteNode={this.props.onDeleteNode}
/>
<div className="dir-content-main">
{this.props.isViewFile ? (
<DirColumnFile
path={this.props.path}
repoID={this.props.repoID}
hash={this.props.hash}
isDraft={this.props.isDraft}
hasDraft={this.props.hasDraft}
goDraftPage={this.props.goDraftPage}
reviewStatus={this.props.reviewStatus}
goReviewPage={this.props.goReviewPage}
isFileLoading={this.props.isFileLoading}
isFileLoadedErr={this.props.isFileLoadedErr}
filePermission={this.props.filePermission}
content={this.props.content}
lastModified={this.props.lastModified}
latestContributor={this.props.latestContributor}
onLinkClick={this.props.onLinkClick}
/>
) : (
<DirListMode
path={this.props.path}
repoID={this.props.repoID}
currentRepoInfo={this.props.currentRepoInfo}
repoEncrypted={this.props.repoEncrypted}
isRepoOwner={this.props.isRepoOwner}
isAdmin={this.props.isAdmin}
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
enableDirPrivateShare={this.props.enableDirPrivateShare}
isRepoInfoBarShow={this.props.isRepoInfoBarShow}
usedRepoTags={this.props.usedRepoTags}
readmeMarkdown={this.props.readmeMarkdown}
draftCounts={this.props.draftCounts}
reviewCounts={this.props.draftCounts}
updateUsedRepoTags={this.props.updateUsedRepoTags}
isDirentListLoading={this.props.isDirentListLoading}
direntList={this.props.direntList}
sortBy={this.props.sortBy}
sortOrder={this.props.sortOrder}
sortItems={this.props.sortItems}
onAddFile={this.props.onAddFile}
onItemClick={this.props.onItemClick}
onItemSelected={this.props.onItemSelected}
onItemDelete={this.props.onItemDelete}
onItemRename={this.props.onItemRename}
onItemMove={this.props.onItemMove}
onItemCopy={this.props.onItemCopy}
onDirentClick={this.props.onDirentClick}
onItemDetails={this.props.onItemDetails}
updateDirent={this.props.updateDirent}
isAllItemSelected={this.props.isAllItemSelected}
onAllItemSelected={this.props.onAllItemSelected}
/>
)}
</div>
</Fragment>
);
}
}
DirColumnView.propTypes = propTypes;
export default DirColumnView;

View File

@@ -0,0 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
const propTypes = {
};
class DirGridView extends React.Component {
render() {
return (
<div>表格布局</div>
);
}
}
DirGridView.propTypes = propTypes;
export default DirGridView;

View File

@@ -0,0 +1,90 @@
import React from 'react';
import PropTypes from 'prop-types';
import RepoInfoBar from '../../components/repo-info-bar';
import DirentListView from '../../components/dirent-list-view/dirent-list-view';
const propTypes = {
path: PropTypes.string.isRequired,
repoID: PropTypes.string.isRequired,
currentRepoInfo: PropTypes.object.isRequired,
repoEncrypted: PropTypes.bool.isRequired,
isRepoOwner: PropTypes.bool.isRequired,
isAdmin: PropTypes.bool.isRequired,
isGroupOwnedRepo: PropTypes.bool.isRequired,
enableDirPrivateShare: PropTypes.bool.isRequired,
isRepoInfoBarShow: PropTypes.bool.isRequired,
usedRepoTags: PropTypes.array.isRequired,
readmeMarkdown: PropTypes.object,
draftCounts: PropTypes.number,
reviewCounts: PropTypes.number,
updateUsedRepoTags: PropTypes.func.isRequired,
isDirentListLoading: PropTypes.bool.isRequired,
direntList: PropTypes.array.isRequired,
sortBy: PropTypes.string.isRequired,
sortOrder: PropTypes.string.isRequired,
sortItems: PropTypes.func.isRequired,
onAddFile: 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,
onDirentClick: PropTypes.func.isRequired,
onItemDetails: PropTypes.func.isRequired,
updateDirent: PropTypes.func.isRequired,
isAllItemSelected: PropTypes.bool.isRequired,
onAllItemSelected: PropTypes.func.isRequired,
};
class DirListView extends React.Component {
render() {
return (
<div className="main-panel">
{this.props.isRepoInfoBarShow && (
<RepoInfoBar
repoID={this.props.repoID}
currentPath={this.props.path}
readmeMarkdown={this.props.readmeMarkdown}
draftCounts={this.props.draftCounts}
reviewCounts={this.props.reviewCounts}
usedRepoTags={this.props.usedRepoTags}
updateUsedRepoTags={this.props.updateUsedRepoTags}
/>
)}
<DirentListView
path={this.props.path}
currentRepoInfo={this.props.currentRepoInfo}
repoID={this.props.repoID}
repoEncrypted={this.props.repoEncrypted}
isRepoOwner={this.props.isRepoOwner}
isAdmin={this.props.isAdmin}
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
enableDirPrivateShare={this.props.enableDirPrivateShare}
direntList={this.props.direntList}
sortBy={this.props.sortBy}
sortOrder={this.props.sortOrder}
sortItems={this.props.sortItems}
onAddFile={this.props.onAddFile}
onItemClick={this.props.onItemClick}
onItemSelected={this.props.onItemSelected}
onItemDelete={this.props.onItemDelete}
onItemRename={this.props.onItemRename}
onItemMove={this.props.onItemMove}
onItemCopy={this.props.onItemCopy}
onDirentClick={this.props.onDirentClick}
onItemDetails={this.props.onItemDetails}
isDirentListLoading={this.props.isDirentListLoading}
updateDirent={this.props.updateDirent}
isAllItemSelected={this.props.isAllItemSelected}
onAllItemSelected={this.props.onAllItemSelected}
/>
</div>
);
}
}
DirListView.propTypes = propTypes;
export default DirListView;

View File

@@ -112,7 +112,7 @@ class DetailListView extends React.Component {
);
})}
</ul>
<i className='fa fa-pencil attr-action-icon' onClick={this.onEditFileTagToggle}></i>
<i className='fa fa-pencil-alt attr-action-icon' onClick={this.onEditFileTagToggle}></i>
</td>
</tr>
<tr className="file-related-files">
@@ -128,7 +128,7 @@ class DetailListView extends React.Component {
);
})}
</ul>
<i className='fa fa-pencil attr-action-icon' onClick={this.onListRelatedFileToggle}></i>
<i className='fa fa-pencil-alt attr-action-icon' onClick={this.onListRelatedFileToggle}></i>
</td>
</tr>
</tbody>

View File

@@ -88,6 +88,7 @@ class DirentListItem extends React.Component {
filePath={this.state.filePath}
onItemClick={this.onItemClick}
selectedPath={this.props.selectedPath}
selectedRepo={this.props.selectedRepo}
onDirentItemClick={this.props.onDirentItemClick}
isShowFile={this.props.isShowFile}
/>
@@ -98,10 +99,7 @@ class DirentListItem extends React.Component {
}
render() {
let isCurrentRepo = false;
if (this.props.selectedRepo) {
isCurrentRepo = this.props.selectedRepo.repo_id === this.props.repo.repo_id;
}
let isCurrentRepo = this.props.selectedRepo.repo_id === this.props.repo.repo_id;
let isCurrentPath = this.props.selectedPath === this.state.filePath;
return (

View File

@@ -39,6 +39,7 @@ class FileChooser extends React.Component {
let repoInfo = new RepoInfo(res.data);
this.setState({
currentRepoInfo: repoInfo,
selectedRepo: repoInfo
});
});
}
@@ -100,6 +101,9 @@ class FileChooser extends React.Component {
}
render() {
if (!this.state.selectedRepo) {
return '';
}
const mode = this.props.mode;
let libName = mode === 'current_repo_and_other_repos' ? gettext('Other Libraries') : gettext('Libraries');
return (

View File

@@ -88,7 +88,7 @@ class SelectEditor extends React.Component {
{this.props.isEditIconShow && (
<span
title={gettext('Edit')}
className="fa fa-pencil attr-action-icon"
className="fa fa-pencil-alt attr-action-icon"
onClick={this.onEditPermission}>
</span>
)}

View File

@@ -10,6 +10,12 @@ import ShareDialog from '../../components/dialog/share-dialog';
const propTypes = {
path: PropTypes.string.isRequired,
repoID: PropTypes.string.isRequired,
repoName: PropTypes.string.isRequired,
repoEncrypted: PropTypes.bool.isRequired,
enableDirPrivateShare: PropTypes.bool.isRequired,
userPerm: PropTypes.string.isRequired,
isAdmin: PropTypes.bool.isRequired,
isGroupOwnedRepo: PropTypes.bool.isRequired,
showShareBtn: PropTypes.bool.isRequired,
onAddFile: PropTypes.func.isRequired,
onAddFolder: PropTypes.func.isRequired,
@@ -139,14 +145,6 @@ class DirOperationToolbar extends React.Component {
this.props.onAddFolder(dirPath);
}
onViewReview = () => {
this.props.goReviewPage();
}
onViewDraft = () => {
this.props.goDraftPage();
}
checkDuplicatedName = (newName) => {
let direntList = this.props.direntList;
let isDuplicated = direntList.some(object => {

View File

@@ -79,8 +79,9 @@ class TreeNode {
this.path = this.generatePath(this.parentNode);
if (this.isExpanded) {
this.updateChildrenPath(this);
} else {
this.isLoaded = false;
}
// this.isLoaded = false;
}
updateChildrenPath(node) {