1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-09 02:42:47 +00:00

Fix create thumbnail error (#6798)

* fix create thumbnail in dirent grid item

* fix create thumbnail in dirent list item

* optimise codes

* change thumbnail start

* change 2 thumbnail centers
This commit is contained in:
Michael An
2024-09-20 14:29:37 +08:00
committed by GitHub
parent 6746e5ab1d
commit f1a180b23d
8 changed files with 346 additions and 174 deletions

View File

@@ -1,9 +1,12 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classnames from 'classnames';
import MD5 from 'MD5'; import MD5 from 'MD5';
import { UncontrolledTooltip } from 'reactstrap'; import { UncontrolledTooltip } from 'reactstrap';
import { gettext, siteRoot, mediaUrl } from '../../utils/constants'; import { gettext, siteRoot, mediaUrl, enableVideoThumbnail, enablePDFThumbnail } from '../../utils/constants';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import { imageThumbnailCenter, videoThumbnailCenter } from '../../utils/thumbnail-center';
import Dirent from '../../models/dirent';
const propTypes = { const propTypes = {
path: PropTypes.string.isRequired, path: PropTypes.string.isRequired,
@@ -13,26 +16,24 @@ const propTypes = {
showImagePopup: PropTypes.func.isRequired, showImagePopup: PropTypes.func.isRequired,
onGridItemContextMenu: PropTypes.func.isRequired, onGridItemContextMenu: PropTypes.func.isRequired,
onGridItemClick: PropTypes.func.isRequired, onGridItemClick: PropTypes.func.isRequired,
activeDirent: PropTypes.object,
onGridItemMouseDown: PropTypes.func, onGridItemMouseDown: PropTypes.func,
currentRepoInfo: PropTypes.object, currentRepoInfo: PropTypes.object,
onItemMove: PropTypes.func.isRequired, onItemMove: PropTypes.func.isRequired,
onItemsMove: PropTypes.func.isRequired, onItemsMove: PropTypes.func.isRequired,
selectedDirentList: PropTypes.array.isRequired, selectedDirentList: PropTypes.array.isRequired,
repoEncrypted: PropTypes.bool.isRequired,
}; };
class DirentGridItem extends React.Component { class DirentGridItem extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
let dirent = props.dirent;
this.state = { this.state = {
dirent,
isGridDropTipShow: false, isGridDropTipShow: false,
}; };
const { dirent } = this.props;
const { isCustomPermission, customPermission } = Utils.getUserPermission(dirent.permission); const { isCustomPermission, customPermission } = Utils.getUserPermission(dirent.permission);
this.isCustomPermission = isCustomPermission;
this.customPermission = customPermission;
this.canPreview = true; this.canPreview = true;
this.canDrag = dirent.permission === 'rw'; this.canDrag = dirent.permission === 'rw';
if (isCustomPermission) { if (isCustomPermission) {
@@ -42,19 +43,64 @@ class DirentGridItem extends React.Component {
} }
this.clickTimeout = null; this.clickTimeout = null;
this.isGeneratingThumbnail = false;
this.thumbnailCenter = null;
}
checkGenerateThumbnail = (dirent) => {
if (this.props.repoEncrypted || dirent.encoded_thumbnail_src) {
return false;
}
if (enableVideoThumbnail && Utils.videoCheck(dirent.name)) {
this.thumbnailCenter = videoThumbnailCenter;
return true;
}
if (Utils.imageCheck(dirent.name) || (enablePDFThumbnail && Utils.pdfCheck(dirent.name))) {
this.thumbnailCenter = imageThumbnailCenter;
return true;
}
return false;
};
componentDidMount() {
const { repoID, path } = this.props;
const { dirent } = this.state;
if (this.checkGenerateThumbnail(dirent)) {
this.isGeneratingThumbnail = true;
this.thumbnailCenter.createThumbnail({
repoID,
path: [path, dirent.name].join('/'),
callback: this.updateDirentThumbnail,
});
}
} }
componentWillUnmount() { componentWillUnmount() {
if (this.clickTimeout) { if (this.clickTimeout) {
clearTimeout(this.clickTimeout); clearTimeout(this.clickTimeout);
} }
if (this.isGeneratingThumbnail) {
const { dirent } = this.state;
const { repoID, path } = this.props;
this.thumbnailCenter.cancelThumbnail({
repoID,
path: [path, dirent.name].join('/'),
});
this.thumbnailCenter = null;
} }
this.setState = () => {};
}
updateDirentThumbnail = (encoded_thumbnail_src) => {
this.isGeneratingThumbnail = false;
this.setState({ dirent: new Dirent(Object.assign({}, this.state.dirent, { encoded_thumbnail_src })) });
};
onItemClick = (e) => { onItemClick = (e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const { dirent } = this.props; const { dirent } = this.state;
if (this.clickTimeout) { if (this.clickTimeout) {
clearTimeout(this.clickTimeout); clearTimeout(this.clickTimeout);
@@ -91,7 +137,7 @@ class DirentGridItem extends React.Component {
onItemLinkClick = (e) => { onItemLinkClick = (e) => {
e.preventDefault(); e.preventDefault();
const dirent = this.props.dirent; const { dirent } = this.state;
if (dirent.isDir()) { if (dirent.isDir()) {
this.props.onItemClick(dirent); this.props.onItemClick(dirent);
@@ -115,7 +161,7 @@ class DirentGridItem extends React.Component {
const dragStartItemsData = selectedDirentList.length > 0 ? selectedDirentList.map(dirent => ({ const dragStartItemsData = selectedDirentList.length > 0 ? selectedDirentList.map(dirent => ({
nodeDirent: dirent, nodeDirent: dirent,
nodeParentPath: path nodeParentPath: path
})) : { nodeDirent: this.props.dirent, nodeParentPath: this.props.path }; })) : { nodeDirent: this.state.dirent, nodeParentPath: this.props.path };
const serializedData = JSON.stringify(dragStartItemsData); const serializedData = JSON.stringify(dragStartItemsData);
@@ -127,7 +173,7 @@ class DirentGridItem extends React.Component {
if (Utils.isIEBrowser() || !this.canDrag) { if (Utils.isIEBrowser() || !this.canDrag) {
return false; return false;
} }
if (this.props.dirent.type === 'dir') { if (this.state.dirent.type === 'dir') {
this.setState({ isGridDropTipShow: true }); this.setState({ isGridDropTipShow: true });
} }
}; };
@@ -156,13 +202,13 @@ class DirentGridItem extends React.Component {
return; return;
} }
let selectedPath = Utils.joinPath(this.props.path, this.props.dirent.name); let selectedPath = Utils.joinPath(this.props.path, this.state.dirent.name);
let dragStartItemsData = e.dataTransfer.getData('application/drag-item-info'); let dragStartItemsData = e.dataTransfer.getData('application/drag-item-info');
dragStartItemsData = JSON.parse(dragStartItemsData); dragStartItemsData = JSON.parse(dragStartItemsData);
if (!Array.isArray(dragStartItemsData)) { if (!Array.isArray(dragStartItemsData)) {
let { nodeDirent, nodeParentPath } = dragStartItemsData; let { nodeDirent, nodeParentPath } = dragStartItemsData;
let dropItemData = this.props.dirent; let dropItemData = this.state.dirent;
if (nodeDirent.name === dropItemData.name) { if (nodeDirent.name === dropItemData.name) {
return; return;
} }
@@ -172,7 +218,7 @@ class DirentGridItem extends React.Component {
this.props.onItemMove(this.props.currentRepoInfo, nodeDirent, selectedPath, nodeParentPath); this.props.onItemMove(this.props.currentRepoInfo, nodeDirent, selectedPath, nodeParentPath);
} else { } else {
// if current dirent is a dir and selected list include it, return // if current dirent is a dir and selected list include it, return
if (dragStartItemsData.some(item => item.nodeDirent.name === this.props.dirent.name)) { if (dragStartItemsData.some(item => item.nodeDirent.name === this.state.dirent.name)) {
return; return;
} }
this.props.onItemsMove(this.props.currentRepoInfo, selectedPath); this.props.onItemsMove(this.props.currentRepoInfo, selectedPath);
@@ -184,7 +230,7 @@ class DirentGridItem extends React.Component {
}; };
onGridItemContextMenu = (event) => { onGridItemContextMenu = (event) => {
this.props.onGridItemContextMenu(event, this.props.dirent); this.props.onGridItemContextMenu(event, this.state.dirent);
}; };
getTextRenderWidth = (text, font) => { getTextRenderWidth = (text, font) => {
@@ -231,35 +277,32 @@ class DirentGridItem extends React.Component {
return showName; return showName;
}; };
render() { getDirentLink = (dirent) => {
let { dirent, path, repoID } = this.props; let { path, repoID, currentRepoInfo } = this.props;
let direntPath = Utils.joinPath(path, dirent.name); let direntPath = Utils.joinPath(path, dirent.name);
let iconUrl = Utils.getDirentIcon(dirent, true); let link = '';
if (dirent.type === 'dir') {
if (currentRepoInfo) {
link = siteRoot + 'library/' + repoID + '/' + currentRepoInfo.repo_name + Utils.encodePath(direntPath);
}
} else {
link = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(direntPath);
if (dirent.is_sdoc_revision && dirent.revision_id) {
link = siteRoot + 'lib/' + repoID + '/revisions/' + dirent.revision_id + '/';
}
}
return link;
};
render() {
let { dirent } = this.state;
let { is_freezed, is_locked, lock_owner_name, file_tags } = dirent;
let toolTipID = ''; let toolTipID = '';
let tagTitle = ''; let tagTitle = '';
if (dirent.file_tags && dirent.file_tags.length > 0) { if (file_tags && file_tags.length > 0) {
toolTipID = MD5(dirent.name).slice(0, 7); toolTipID = MD5(dirent.name).slice(0, 7);
tagTitle = dirent.file_tags.map(item => item.name).join(' '); tagTitle = file_tags.map(item => item.name).join(' ');
} }
let dirHref = '';
if (this.props.currentRepoInfo) {
dirHref = siteRoot + 'library/' + repoID + '/' + this.props.currentRepoInfo.repo_name + Utils.encodePath(direntPath);
}
let fileHref = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(direntPath);
if (dirent.is_sdoc_revision && dirent.revision_id) {
fileHref = siteRoot + 'lib/' + repoID + '/revisions/' + dirent.revision_id + '/';
}
let gridClass = 'grid-file-img-link cursor-pointer';
gridClass += this.state.isGridDropTipShow ? ' grid-drop-show' : ' ';
let lockedInfo = dirent.is_freezed ? gettext('Frozen by {name}') : gettext('locked by {name}');
lockedInfo = lockedInfo.replace('{name}', dirent.lock_owner_name);
const lockedImageUrl = `${mediaUrl}img/file-${dirent.is_freezed ? 'freezed-32.svg' : 'locked-32.png'}`;
const lockedMessage = dirent.is_freezed ? gettext('freezed') : gettext('locked');
const showName = this.getRenderedText(dirent); const showName = this.getRenderedText(dirent);
return ( return (
<Fragment> <Fragment>
@@ -268,7 +311,7 @@ class DirentGridItem extends React.Component {
onContextMenu={this.onGridItemContextMenu} onContextMenu={this.onGridItemContextMenu}
onMouseDown={this.onGridItemMouseDown}> onMouseDown={this.onGridItemMouseDown}>
<div <div
className={gridClass} className={classnames('grid-file-img-link cursor-pointer', { 'grid-drop-show': this.state.isGridDropTipShow })}
draggable={this.canDrag} draggable={this.canDrag}
onClick={this.onItemClick} onClick={this.onItemClick}
onDragStart={this.onGridItemDragStart} onDragStart={this.onGridItemDragStart}
@@ -279,16 +322,23 @@ class DirentGridItem extends React.Component {
> >
{(this.canPreview && dirent.encoded_thumbnail_src) ? {(this.canPreview && dirent.encoded_thumbnail_src) ?
<img src={`${siteRoot}${dirent.encoded_thumbnail_src || ''}`} className="thumbnail" onClick={this.onItemClick} alt=""/> : <img src={`${siteRoot}${dirent.encoded_thumbnail_src || ''}`} className="thumbnail" onClick={this.onItemClick} alt=""/> :
<img src={iconUrl} width="96" alt='' /> <img src={Utils.getDirentIcon(dirent, true)} width="96" alt='' />
}
{is_locked &&
<img
className="grid-file-locked-icon"
src={`${mediaUrl}img/file-${is_freezed ? 'freezed-32.svg' : 'locked-32.png'}`}
alt={is_freezed ? gettext('freezed') : gettext('locked')}
title={(is_freezed ? gettext('Frozen by {name}') : gettext('locked by {name}')).replace('{name}', lock_owner_name)}
/>
} }
{dirent.is_locked && <img className="grid-file-locked-icon" src={lockedImageUrl} alt={lockedMessage} title={lockedInfo}/>}
</div> </div>
<div className="grid-file-name" onDragStart={this.onGridItemDragStart} draggable={this.canDrag} > <div className="grid-file-name" onDragStart={this.onGridItemDragStart} draggable={this.canDrag} >
{(dirent.type !== 'dir' && dirent.file_tags && dirent.file_tags.length > 0) && ( {(dirent.type !== 'dir' && file_tags && file_tags.length > 0) && (
<Fragment> <Fragment>
<div id={`tag-list-title-${toolTipID}`} className="dirent-item tag-list tag-list-stacked d-inline-block align-middle"> <div id={`tag-list-title-${toolTipID}`} className="dirent-item tag-list tag-list-stacked d-inline-block align-middle">
{dirent.file_tags.map((fileTag, index) => { {file_tags.map((fileTag, index) => {
let length = dirent.file_tags.length; let length = file_tags.length;
return ( return (
<span className="file-tag" key={fileTag.id} style={{ zIndex: length - index, backgroundColor: fileTag.color }}></span> <span className="file-tag" key={fileTag.id} style={{ zIndex: length - index, backgroundColor: fileTag.color }}></span>
); );
@@ -308,7 +358,7 @@ class DirentGridItem extends React.Component {
</a> : </a> :
<a <a
className="grid-file-name-link" className="grid-file-name-link"
href={dirent.type === 'dir' ? dirHref : fileHref} href={this.getDirentLink(dirent)}
onClick={this.onItemClick} onClick={this.onItemClick}
title={dirent.name} title={dirent.name}
>{showName} >{showName}

View File

@@ -852,8 +852,8 @@ class DirentGridView extends React.Component {
onItemsMove={this.props.onItemsMove} onItemsMove={this.props.onItemsMove}
onGridItemMouseDown={this.onGridItemMouseDown} onGridItemMouseDown={this.onGridItemMouseDown}
onGridItemClick={this.onGridItemClick} onGridItemClick={this.onGridItemClick}
activeDirent={this.state.activeDirent}
selectedDirentList={selectedDirentList} selectedDirentList={selectedDirentList}
repoEncrypted={this.props.currentRepoInfo.encrypted}
/> />
); );
})} })}

View File

@@ -1,13 +1,15 @@
import React, { Fragment } from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classnames from 'classnames';
import MediaQuery from 'react-responsive'; import MediaQuery from 'react-responsive';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import moment from 'moment'; import moment from 'moment';
import { Dropdown, DropdownToggle, DropdownItem } from 'reactstrap'; import { Dropdown, DropdownToggle, DropdownItem } from 'reactstrap';
import { gettext, siteRoot, mediaUrl, username, useGoFileserver, fileServerRoot } from '../../utils/constants'; import { gettext, siteRoot, mediaUrl, username, useGoFileserver, fileServerRoot, enableVideoThumbnail, enablePDFThumbnail } from '../../utils/constants';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import URLDecorator from '../../utils/url-decorator'; import URLDecorator from '../../utils/url-decorator';
import { imageThumbnailCenter, videoThumbnailCenter } from '../../utils/thumbnail-center';
import ItemDropdownMenu from '../dropdown-menu/item-dropdown-menu'; import ItemDropdownMenu from '../dropdown-menu/item-dropdown-menu';
import Rename from '../rename'; import Rename from '../rename';
import ModalPortal from '../modal-portal'; import ModalPortal from '../modal-portal';
@@ -21,6 +23,7 @@ import LibSubFolderPermissionDialog from '../dialog/lib-sub-folder-permission-di
import FileAccessLog from '../dialog/file-access-log'; import FileAccessLog from '../dialog/file-access-log';
import toaster from '../toast'; import toaster from '../toast';
import FileTag from './file-tag'; import FileTag from './file-tag';
import Dirent from '../../models/dirent';
import '../../css/dirent-list-item.css'; import '../../css/dirent-list-item.css';
@@ -66,7 +69,7 @@ class DirentListItem extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
const { dirent } = this.props; let { dirent } = this.props;
const { isCustomPermission, customPermission } = Utils.getUserPermission(dirent.permission); const { isCustomPermission, customPermission } = Utils.getUserPermission(dirent.permission);
this.isCustomPermission = isCustomPermission; this.isCustomPermission = isCustomPermission;
this.customPermission = customPermission; this.customPermission = customPermission;
@@ -79,6 +82,7 @@ class DirentListItem extends React.Component {
} }
this.state = { this.state = {
dirent,
isOperationShow: false, isOperationShow: false,
highlight: false, highlight: false,
isZipDialogOpen: false, isZipDialogOpen: false,
@@ -96,6 +100,39 @@ class DirentListItem extends React.Component {
isOpMenuOpen: false // for mobile isOpMenuOpen: false // for mobile
}; };
this.tagListTitleID = `tag-list-title-${uuidv4()}`; this.tagListTitleID = `tag-list-title-${uuidv4()}`;
this.isGeneratingThumbnail = false;
this.thumbnailCenter = null;
}
componentDidMount() {
const { repoID, path } = this.props;
const { dirent } = this.state;
if (this.checkGenerateThumbnail(dirent)) {
this.isGeneratingThumbnail = true;
this.thumbnailCenter.createThumbnail({
repoID,
path: [path, dirent.name].join('/'),
callback: this.updateDirentThumbnail,
});
}
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.dirent && this.state.dirent && nextProps.dirent.name !== this.state.dirent.name) {
this.setState({
dirent: nextProps.dirent,
}, () => {
if (this.checkGenerateThumbnail(nextProps.dirent)) {
const { repoID, path } = nextProps;
this.isGeneratingThumbnail = true;
this.thumbnailCenter.createThumbnail({
repoID,
path: [path, nextProps.dirent.name].join('/'),
callback: this.updateDirentThumbnail,
});
}
});
}
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
@@ -109,6 +146,39 @@ class DirentListItem extends React.Component {
} }
} }
componentWillUnmount() {
if (this.isGeneratingThumbnail) {
const { dirent } = this.state;
const { repoID, path } = this.props;
this.thumbnailCenter.cancelThumbnail({
repoID,
path: [path, dirent.name].join('/'),
});
this.thumbnailCenter = null;
}
this.setState = () => {};
}
checkGenerateThumbnail = (dirent) => {
if (this.props.repoEncrypted || dirent.encoded_thumbnail_src) {
return false;
}
if (enableVideoThumbnail && Utils.videoCheck(dirent.name)) {
this.thumbnailCenter = videoThumbnailCenter;
return true;
}
if (Utils.imageCheck(dirent.name) || (enablePDFThumbnail && Utils.pdfCheck(dirent.name))) {
this.thumbnailCenter = imageThumbnailCenter;
return true;
}
return false;
};
updateDirentThumbnail = (encoded_thumbnail_src) => {
this.isGeneratingThumbnail = false;
this.setState({ dirent: new Dirent(Object.assign({}, this.state.dirent, { encoded_thumbnail_src })) });
};
toggleOpMenu = () => { toggleOpMenu = () => {
this.setState({ this.setState({
isOpMenuOpen: !this.state.isOpMenuOpen isOpMenuOpen: !this.state.isOpMenuOpen
@@ -160,24 +230,24 @@ class DirentListItem extends React.Component {
// buiness handler // buiness handler
onItemSelected = () => { onItemSelected = () => {
this.props.onItemSelected(this.props.dirent); this.props.onItemSelected(this.state.dirent);
}; };
onItemStarred = (e) => { onItemStarred = (e) => {
let dirent = this.props.dirent; let dirent = this.state.dirent;
let repoID = this.props.repoID; let repoID = this.props.repoID;
let filePath = this.getDirentPath(dirent); let filePath = this.getDirentPath(dirent);
if (dirent.starred) { if (dirent.starred) {
seafileAPI.unstarItem(repoID, filePath).then(() => { seafileAPI.unstarItem(repoID, filePath).then(() => {
this.props.updateDirent(this.props.dirent, 'starred', false); this.props.updateDirent(this.state.dirent, 'starred', false);
}).catch(error => { }).catch(error => {
let errMessage = Utils.getErrorMsg(error); let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage); toaster.danger(errMessage);
}); });
} else { } else {
seafileAPI.starItem(repoID, filePath).then(() => { seafileAPI.starItem(repoID, filePath).then(() => {
this.props.updateDirent(this.props.dirent, 'starred', true); this.props.updateDirent(this.state.dirent, 'starred', true);
}).catch(error => { }).catch(error => {
let errMessage = Utils.getErrorMsg(error); let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage); toaster.danger(errMessage);
@@ -190,13 +260,13 @@ class DirentListItem extends React.Component {
// '<td>' is clicked // '<td>' is clicked
e.stopPropagation(); e.stopPropagation();
if (e.target.tagName == 'TD') { if (e.target.tagName == 'TD') {
this.props.onDirentClick(this.props.dirent, e); this.props.onDirentClick(this.state.dirent, e);
} }
}; };
onItemClick = (e) => { onItemClick = (e) => {
e.preventDefault(); e.preventDefault();
const dirent = this.props.dirent; const dirent = this.state.dirent;
if (this.state.isRenameing) { if (this.state.isRenameing) {
return; return;
} }
@@ -220,7 +290,7 @@ class DirentListItem extends React.Component {
onItemDelete = (e) => { onItemDelete = (e) => {
e.preventDefault(); e.preventDefault();
e.nativeEvent.stopImmediatePropagation(); // for document event e.nativeEvent.stopImmediatePropagation(); // for document event
this.props.onItemDelete(this.props.dirent); this.props.onItemDelete(this.state.dirent);
}; };
onItemShare = (e) => { onItemShare = (e) => {
@@ -232,7 +302,7 @@ class DirentListItem extends React.Component {
exportDocx = () => { exportDocx = () => {
const serviceUrl = window.app.config.serviceURL; const serviceUrl = window.app.config.serviceURL;
let repoID = this.props.repoID; let repoID = this.props.repoID;
let filePath = this.getDirentPath(this.props.dirent); let filePath = this.getDirentPath(this.state.dirent);
let exportToDocxUrl = serviceUrl + '/repo/sdoc_export_to_docx/' + repoID + '/?file_path=' + filePath; let exportToDocxUrl = serviceUrl + '/repo/sdoc_export_to_docx/' + repoID + '/?file_path=' + filePath;
window.location.href = exportToDocxUrl; window.location.href = exportToDocxUrl;
}; };
@@ -240,7 +310,7 @@ class DirentListItem extends React.Component {
exportSdoc = () => { exportSdoc = () => {
const serviceUrl = window.app.config.serviceURL; const serviceUrl = window.app.config.serviceURL;
let repoID = this.props.repoID; let repoID = this.props.repoID;
let filePath = this.getDirentPath(this.props.dirent); let filePath = this.getDirentPath(this.state.dirent);
let exportToSdocUrl = serviceUrl + '/lib/' + repoID + '/file/' + filePath + '?dl=1'; let exportToSdocUrl = serviceUrl + '/lib/' + repoID + '/file/' + filePath + '?dl=1';
window.location.href = exportToSdocUrl; window.location.href = exportToSdocUrl;
}; };
@@ -314,7 +384,7 @@ class DirentListItem extends React.Component {
this.toggleFileAccessLogDialog(); this.toggleFileAccessLogDialog();
break; break;
case 'Properties': case 'Properties':
this.props.onDirentClick(this.props.dirent); this.props.onDirentClick(this.state.dirent);
this.props.showDirentDetail('info'); this.props.showDirentDetail('info');
break; break;
case 'Open via Client': case 'Open via Client':
@@ -331,7 +401,7 @@ class DirentListItem extends React.Component {
onItemConvert = (e, dstType) => { onItemConvert = (e, dstType) => {
e.preventDefault(); e.preventDefault();
e.nativeEvent.stopImmediatePropagation(); // for document event e.nativeEvent.stopImmediatePropagation(); // for document event
this.props.onItemConvert(this.props.dirent, dstType); this.props.onItemConvert(this.state.dirent, dstType);
}; };
onEditFileTagToggle = () => { onEditFileTagToggle = () => {
@@ -341,12 +411,12 @@ class DirentListItem extends React.Component {
}; };
onFileTagChanged = () => { onFileTagChanged = () => {
let direntPath = this.getDirentPath(this.props.dirent); let direntPath = this.getDirentPath(this.state.dirent);
this.props.onFileTagChanged(this.props.dirent, direntPath); this.props.onFileTagChanged(this.state.dirent, direntPath);
}; };
onItemRenameToggle = () => { onItemRenameToggle = () => {
this.props.onItemRenameToggle(this.props.dirent); this.props.onItemRenameToggle(this.state.dirent);
this.setState({ this.setState({
isOperationShow: false, isOperationShow: false,
isRenameing: true, isRenameing: true,
@@ -355,7 +425,7 @@ class DirentListItem extends React.Component {
}; };
onRenameConfirm = (newName) => { onRenameConfirm = (newName) => {
this.props.onItemRename(this.props.dirent, newName); this.props.onItemRename(this.state.dirent, newName);
this.onRenameCancel(); this.onRenameCancel();
}; };
@@ -381,12 +451,13 @@ class DirentListItem extends React.Component {
onLockItem = () => { onLockItem = () => {
let repoID = this.props.repoID; let repoID = this.props.repoID;
let filePath = this.getDirentPath(this.props.dirent); let dirent = this.state.dirent;
let filePath = this.getDirentPath(dirent);
seafileAPI.lockfile(repoID, filePath).then(() => { seafileAPI.lockfile(repoID, filePath).then(() => {
this.props.updateDirent(this.props.dirent, 'is_locked', true); this.props.updateDirent(dirent, 'is_locked', true);
this.props.updateDirent(this.props.dirent, 'locked_by_me', true); this.props.updateDirent(dirent, 'locked_by_me', true);
let lockName = username.split('@'); let lockName = username.split('@');
this.props.updateDirent(this.props.dirent, 'lock_owner_name', lockName[0]); this.props.updateDirent(dirent, 'lock_owner_name', lockName[0]);
}).catch(error => { }).catch(error => {
let errMessage = Utils.getErrorMsg(error); let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage); toaster.danger(errMessage);
@@ -395,13 +466,14 @@ class DirentListItem extends React.Component {
onFreezeDocument = () => { onFreezeDocument = () => {
let repoID = this.props.repoID; let repoID = this.props.repoID;
let filePath = this.getDirentPath(this.props.dirent); let dirent = this.state.dirent;
let filePath = this.getDirentPath(dirent);
seafileAPI.lockfile(repoID, filePath, -1).then(() => { seafileAPI.lockfile(repoID, filePath, -1).then(() => {
this.props.updateDirent(this.props.dirent, 'is_freezed', true); this.props.updateDirent(dirent, 'is_freezed', true);
this.props.updateDirent(this.props.dirent, 'is_locked', true); this.props.updateDirent(dirent, 'is_locked', true);
this.props.updateDirent(this.props.dirent, 'locked_by_me', true); this.props.updateDirent(dirent, 'locked_by_me', true);
let lockName = username.split('@'); let lockName = username.split('@');
this.props.updateDirent(this.props.dirent, 'lock_owner_name', lockName[0]); this.props.updateDirent(dirent, 'lock_owner_name', lockName[0]);
}).catch(error => { }).catch(error => {
let errMessage = Utils.getErrorMsg(error); let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage); toaster.danger(errMessage);
@@ -410,11 +482,12 @@ class DirentListItem extends React.Component {
onUnlockItem = () => { onUnlockItem = () => {
let repoID = this.props.repoID; let repoID = this.props.repoID;
let filePath = this.getDirentPath(this.props.dirent); let dirent = this.state.dirent;
let filePath = this.getDirentPath(dirent);
seafileAPI.unlockfile(repoID, filePath).then(() => { seafileAPI.unlockfile(repoID, filePath).then(() => {
this.props.updateDirent(this.props.dirent, 'is_locked', false); this.props.updateDirent(dirent, 'is_locked', false);
this.props.updateDirent(this.props.dirent, 'locked_by_me', false); this.props.updateDirent(dirent, 'locked_by_me', false);
this.props.updateDirent(this.props.dirent, 'lock_owner_name', ''); this.props.updateDirent(dirent, 'lock_owner_name', '');
}).catch(error => { }).catch(error => {
let errMessage = Utils.getErrorMsg(error); let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage); toaster.danger(errMessage);
@@ -422,9 +495,8 @@ class DirentListItem extends React.Component {
}; };
onHistory = () => { onHistory = () => {
let repoID = this.props.repoID; let filePath = this.getDirentPath(this.state.dirent);
let filePath = this.getDirentPath(this.props.dirent); let url = URLDecorator.getUrl({ type: 'file_revisions', repoID: this.props.repoID, filePath: filePath });
let url = URLDecorator.getUrl({ type: 'file_revisions', repoID: repoID, filePath: filePath });
location.href = url; location.href = url;
}; };
@@ -436,14 +508,14 @@ class DirentListItem extends React.Component {
onOpenViaClient = () => { onOpenViaClient = () => {
let repoID = this.props.repoID; let repoID = this.props.repoID;
let filePath = this.getDirentPath(this.props.dirent); let filePath = this.getDirentPath(this.state.dirent);
let url = URLDecorator.getUrl({ type: 'open_via_client', repoID: repoID, filePath: filePath }); let url = URLDecorator.getUrl({ type: 'open_via_client', repoID: repoID, filePath: filePath });
location.href = url; location.href = url;
}; };
onConvertWithONLYOFFICE = () => { onConvertWithONLYOFFICE = () => {
let repoID = this.props.repoID; let repoID = this.props.repoID;
let filePath = this.getDirentPath(this.props.dirent); let filePath = this.getDirentPath(this.state.dirent);
seafileAPI.onlyofficeConvert(repoID, filePath).then(res => { seafileAPI.onlyofficeConvert(repoID, filePath).then(res => {
this.props.loadDirentList(res.data.parent_dir); this.props.loadDirentList(res.data.parent_dir);
}).catch(error => { }).catch(error => {
@@ -455,7 +527,7 @@ class DirentListItem extends React.Component {
onItemDownload = (e) => { onItemDownload = (e) => {
e.preventDefault(); e.preventDefault();
e.nativeEvent.stopImmediatePropagation(); e.nativeEvent.stopImmediatePropagation();
let dirent = this.props.dirent; let dirent = this.state.dirent;
let repoID = this.props.repoID; let repoID = this.props.repoID;
let direntPath = this.getDirentPath(dirent); let direntPath = this.getDirentPath(dirent);
if (dirent.type === 'dir') { if (dirent.type === 'dir') {
@@ -464,7 +536,7 @@ class DirentListItem extends React.Component {
isZipDialogOpen: true isZipDialogOpen: true
}); });
} else { } else {
seafileAPI.zipDownload(repoID, this.props.path, this.props.dirent.name).then((res) => { seafileAPI.zipDownload(repoID, this.props.path, this.state.dirent.name).then((res) => {
const zipToken = res.data['zip_token']; const zipToken = res.data['zip_token'];
location.href = `${fileServerRoot}zip/${zipToken}`; location.href = `${fileServerRoot}zip/${zipToken}`;
}).catch((error) => { }).catch((error) => {
@@ -507,7 +579,7 @@ class DirentListItem extends React.Component {
} }
e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.effectAllowed = 'move';
let { selectedDirentList } = this.props; let { selectedDirentList } = this.props;
if (selectedDirentList.length > 0 && selectedDirentList.includes(this.props.dirent)) { // drag items and selectedDirentList include item if (selectedDirentList.length > 0 && selectedDirentList.includes(this.state.dirent)) { // drag items and selectedDirentList include item
this.props.onShowDirentsDraggablePreview(); this.props.onShowDirentsDraggablePreview();
e.dataTransfer.setDragImage(this.refs.empty_content, 0, 0); // Show an empty content e.dataTransfer.setDragImage(this.refs.empty_content, 0, 0); // Show an empty content
let selectedList = selectedDirentList.map(item => { let selectedList = selectedDirentList.map(item => {
@@ -524,8 +596,8 @@ class DirentListItem extends React.Component {
e.dataTransfer.setDragImage(this.refs.drag_icon, 15, 15); e.dataTransfer.setDragImage(this.refs.drag_icon, 15, 15);
} }
let nodeRootPath = this.getDirentPath(this.props.dirent); let nodeRootPath = this.getDirentPath(this.state.dirent);
let dragStartItemData = { nodeDirent: this.props.dirent, nodeParentPath: this.props.path, nodeRootPath: nodeRootPath }; let dragStartItemData = { nodeDirent: this.state.dirent, nodeParentPath: this.props.path, nodeRootPath: nodeRootPath };
dragStartItemData = JSON.stringify(dragStartItemData); dragStartItemData = JSON.stringify(dragStartItemData);
e.dataTransfer.setData('application/drag-item-info', dragStartItemData); e.dataTransfer.setData('application/drag-item-info', dragStartItemData);
@@ -535,7 +607,7 @@ class DirentListItem extends React.Component {
if (Utils.isIEBrowser() || !this.state.canDrag) { if (Utils.isIEBrowser() || !this.state.canDrag) {
return false; return false;
} }
if (this.props.dirent.type === 'dir') { if (this.state.dirent.type === 'dir') {
e.stopPropagation(); e.stopPropagation();
this.setState({ isDropTipshow: true }); this.setState({ isDropTipshow: true });
} }
@@ -557,7 +629,7 @@ class DirentListItem extends React.Component {
return false; return false;
} }
if (this.props.dirent.type === 'dir') { if (this.state.dirent.type === 'dir') {
e.stopPropagation(); e.stopPropagation();
} }
this.setState({ isDropTipshow: false }); this.setState({ isDropTipshow: false });
@@ -571,7 +643,7 @@ class DirentListItem extends React.Component {
if (e.dataTransfer.files.length) { // uploaded files if (e.dataTransfer.files.length) { // uploaded files
return; return;
} }
if (this.props.dirent.type === 'dir') { if (this.state.dirent.type === 'dir') {
e.stopPropagation(); e.stopPropagation();
} else { } else {
return; return;
@@ -583,7 +655,7 @@ class DirentListItem extends React.Component {
return draggedItem.nodeRootPath; return draggedItem.nodeRootPath;
}); });
let selectedPath = Utils.joinPath(this.props.path, this.props.dirent.name); let selectedPath = Utils.joinPath(this.props.path, this.state.dirent.name);
if (direntPaths.some(direntPath => { return direntPath === selectedPath;})) { // eg; A/B, A/C --> A/B if (direntPaths.some(direntPath => { return direntPath === selectedPath;})) { // eg; A/B, A/C --> A/B
return; return;
@@ -594,7 +666,7 @@ class DirentListItem extends React.Component {
} }
let { nodeDirent, nodeParentPath, nodeRootPath } = dragStartItemData; let { nodeDirent, nodeParentPath, nodeRootPath } = dragStartItemData;
let dropItemData = this.props.dirent; let dropItemData = this.state.dirent;
if (nodeDirent.name === dropItemData.name) { if (nodeDirent.name === dropItemData.name) {
return; return;
@@ -609,7 +681,7 @@ class DirentListItem extends React.Component {
} }
} }
let selectedPath = Utils.joinPath(this.props.path, this.props.dirent.name); let selectedPath = Utils.joinPath(this.props.path, this.state.dirent.name);
this.onItemMove(this.props.currentRepoInfo, nodeDirent, selectedPath, nodeParentPath); this.onItemMove(this.props.currentRepoInfo, nodeDirent, selectedPath, nodeParentPath);
}; };
@@ -618,12 +690,27 @@ class DirentListItem extends React.Component {
}; };
onItemContextMenu = (event) => { onItemContextMenu = (event) => {
let dirent = this.props.dirent; this.props.onItemContextMenu(event, this.state.dirent);
this.props.onItemContextMenu(event, dirent); };
getDirentHref = () => {
let { path, repoID } = this.props;
let dirent = this.state.dirent;
let direntPath = Utils.joinPath(path, dirent.name);
let dirHref = '';
if (this.props.currentRepoInfo) {
dirHref = siteRoot + 'library/' + repoID + '/' + this.props.currentRepoInfo.repo_name + Utils.encodePath(direntPath);
}
let fileHref = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(direntPath);
if (dirent.is_sdoc_revision && dirent.revision_id) {
fileHref = siteRoot + 'lib/' + repoID + '/revisions/' + dirent.revision_id + '/';
}
return dirent.type === 'dir' ? dirHref : fileHref;
}; };
renderItemOperation = () => { renderItemOperation = () => {
let { dirent, currentRepoInfo, selectedDirentList } = this.props; let { currentRepoInfo, selectedDirentList } = this.props;
let dirent = this.state.dirent;
let canDownload = true; let canDownload = true;
let canDelete = true; let canDelete = true;
const { isCustomPermission, customPermission } = this; const { isCustomPermission, customPermission } = this;
@@ -637,9 +724,9 @@ class DirentListItem extends React.Component {
let showShareBtn = Utils.isHasPermissionToShare(currentRepoInfo, dirent.permission, dirent); let showShareBtn = Utils.isHasPermissionToShare(currentRepoInfo, dirent.permission, dirent);
return ( return (
<Fragment> <>
{selectedDirentList.length > 1 ? {selectedDirentList.length > 1 ?
<Fragment> <>
{this.state.isOperationShow && !dirent.isSelected && {this.state.isOperationShow && !dirent.isSelected &&
<div className="operations"> <div className="operations">
{(dirent.permission === 'rw' || dirent.permission === 'r' || (isCustomPermission && canDownload)) && ( {(dirent.permission === 'rw' || dirent.permission === 'r' || (isCustomPermission && canDownload)) && (
@@ -652,7 +739,7 @@ class DirentListItem extends React.Component {
<a href="#" className="op-icon sf3-font-delete1 sf3-font" title={gettext('Delete')} role="button" aria-label={gettext('Delete')} onClick={this.onItemDelete}></a> <a href="#" className="op-icon sf3-font-delete1 sf3-font" title={gettext('Delete')} role="button" aria-label={gettext('Delete')} onClick={this.onItemDelete}></a>
)} )}
<ItemDropdownMenu <ItemDropdownMenu
item={this.props.dirent} item={this.state.dirent}
isHandleContextMenuEvent={true} isHandleContextMenuEvent={true}
getMenuList={this.props.getDirentItemMenuList} getMenuList={this.props.getDirentItemMenuList}
onMenuItemClick={this.onMenuItemClick} onMenuItemClick={this.onMenuItemClick}
@@ -661,8 +748,8 @@ class DirentListItem extends React.Component {
/> />
</div> </div>
} }
</Fragment> : </> :
<Fragment> <>
{this.state.isOperationShow && {this.state.isOperationShow &&
<div className="operations"> <div className="operations">
{(dirent.permission === 'rw' || dirent.permission === 'r' || (isCustomPermission && canDownload)) && ( {(dirent.permission === 'rw' || dirent.permission === 'r' || (isCustomPermission && canDownload)) && (
@@ -675,7 +762,7 @@ class DirentListItem extends React.Component {
<a href="#" className="op-icon sf3-font-delete1 sf3-font" title={gettext('Delete')} role="button" aria-label={gettext('Delete')} onClick={this.onItemDelete}></a> <a href="#" className="op-icon sf3-font-delete1 sf3-font" title={gettext('Delete')} role="button" aria-label={gettext('Delete')} onClick={this.onItemDelete}></a>
)} )}
<ItemDropdownMenu <ItemDropdownMenu
item={this.props.dirent} item={this.state.dirent}
isHandleContextMenuEvent={true} isHandleContextMenuEvent={true}
getMenuList={this.props.getDirentItemMenuList} getMenuList={this.props.getDirentItemMenuList}
onMenuItemClick={this.onMenuItemClick} onMenuItemClick={this.onMenuItemClick}
@@ -684,30 +771,20 @@ class DirentListItem extends React.Component {
/> />
</div> </div>
} }
</Fragment> </>
} }
</Fragment> </>
); );
}; };
render() { render() {
let { path, dirent, repoID } = this.props; let { path } = this.props;
let dirent = this.state.dirent;
let direntPath = Utils.joinPath(path, dirent.name); let direntPath = Utils.joinPath(path, dirent.name);
let dirHref = '';
if (this.props.currentRepoInfo) {
dirHref = siteRoot + 'library/' + repoID + '/' + this.props.currentRepoInfo.repo_name + Utils.encodePath(direntPath);
}
let fileHref = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(direntPath);
if (dirent.is_sdoc_revision && dirent.revision_id) {
fileHref = siteRoot + 'lib/' + repoID + '/revisions/' + dirent.revision_id + '/';
}
let iconUrl = Utils.getDirentIcon(dirent); let iconUrl = Utils.getDirentIcon(dirent);
let isActive = dirent.isSelected; let isSelected = dirent.isSelected;
let trClass = this.state.highlight ? 'tr-highlight ' : '';
trClass += this.state.isDropTipshow ? 'tr-drop-effect' : '';
trClass += isActive ? 'tr-active' : '';
let lockedInfo = dirent.is_freezed ? gettext('Frozen by {name}') : gettext('locked by {name}'); let lockedInfo = dirent.is_freezed ? gettext('Frozen by {name}') : gettext('locked by {name}');
lockedInfo = lockedInfo.replace('{name}', dirent.lock_owner_name); lockedInfo = lockedInfo.replace('{name}', dirent.lock_owner_name);
@@ -718,10 +795,14 @@ class DirentListItem extends React.Component {
const lockedMessage = dirent.is_freezed ? gettext('freezed') : gettext('locked'); const lockedMessage = dirent.is_freezed ? gettext('freezed') : gettext('locked');
return ( return (
<Fragment> <>
{isDesktop ? {isDesktop ?
<tr <tr
className={trClass} className={classnames(
{ 'tr-highlight': this.state.highlight },
{ 'tr-drop-effect': this.state.isDropTipshow },
{ 'tr-active': isSelected },
)}
draggable={canDrag} draggable={canDrag}
onFocus={this.onMouseEnter} onFocus={this.onMouseEnter}
onMouseEnter={this.onMouseEnter} onMouseEnter={this.onMouseEnter}
@@ -741,8 +822,8 @@ class DirentListItem extends React.Component {
type="checkbox" type="checkbox"
className="vam" className="vam"
onChange={this.onItemSelected} onChange={this.onItemSelected}
checked={isActive} checked={isSelected}
aria-label={isActive ? gettext('Unselect this item') : gettext('Select this item')} aria-label={isSelected ? gettext('Unselect this item') : gettext('Select this item')}
/> />
</td> </td>
<td className="pl10"> <td className="pl10">
@@ -763,18 +844,25 @@ class DirentListItem extends React.Component {
<img ref='drag_icon' src={iconUrl} width="24" alt='' /> <img ref='drag_icon' src={iconUrl} width="24" alt='' />
} }
{dirent.is_locked && <img className="locked" src={lockedImageUrl} alt={lockedMessage} title={lockedInfo}/>} {dirent.is_locked && <img className="locked" src={lockedImageUrl} alt={lockedMessage} title={lockedInfo}/>}
<div ref="empty_content" style={{ position: 'absolute', width: '1px', height: '1px' }}></div> <div ref="empty_content" className="empty-content"></div>
</div> </div>
</td> </td>
<td className="name"> <td className="name">
{this.state.isRenameing && <Rename hasSuffix={dirent.type !== 'dir'} name={dirent.name} onRenameConfirm={this.onRenameConfirm} onRenameCancel={this.onRenameCancel} />} {this.state.isRenameing &&
<Rename
hasSuffix={dirent.type !== 'dir'}
name={dirent.name}
onRenameConfirm={this.onRenameConfirm}
onRenameCancel={this.onRenameCancel}
/>
}
{!this.state.isRenameing && ( {!this.state.isRenameing && (
<Fragment> <>
{(!dirent.isDir() && !this.canPreview) ? {(!dirent.isDir() && !this.canPreview) ?
<a className="sf-link" onClick={this.onItemClick}>{dirent.name}</a> : <a className="sf-link" onClick={this.onItemClick}>{dirent.name}</a> :
<a href={dirent.type === 'dir' ? dirHref : fileHref} onClick={this.onItemClick}>{dirent.name}</a> <a href={this.getDirentHref()} onClick={this.onItemClick}>{dirent.name}</a>
} }
</Fragment> </>
)} )}
</td> </td>
<td className="tag-list-title"> <td className="tag-list-title">
@@ -787,12 +875,12 @@ class DirentListItem extends React.Component {
})} })}
</div> </div>
)} )}
{(dirent.type !== 'dir' && (!dirent.file_tags || dirent.file_tags.length == 0)) && ( {(dirent.type !== 'dir' && (!dirent.file_tags || dirent.file_tags.length == 0)) &&
<div id={this.tagListTitleID} className="dirent-item tag-list tag-list-stacked"></div> <div id={this.tagListTitleID} className="dirent-item tag-list tag-list-stacked"></div>
)} }
</td> </td>
<td className="operation">{this.renderItemOperation()}</td> <td className="operation">{this.renderItemOperation()}</td>
<td className="file-size">{dirent.size && dirent.size}</td> <td className="file-size">{dirent.size || ''}</td>
<td className="last-update" title={moment.unix(dirent.mtime).format('llll')}>{dirent.mtime_relative}</td> <td className="last-update" title={moment.unix(dirent.mtime).format('llll')}>{dirent.mtime_relative}</td>
</tr> </tr>
: :
@@ -807,14 +895,21 @@ class DirentListItem extends React.Component {
</div> </div>
</td> </td>
<td onClick={this.onItemClick}> <td onClick={this.onItemClick}>
{this.state.isRenameing && <Rename hasSuffix={dirent.type !== 'dir'} name={dirent.name} onRenameConfirm={this.onRenameConfirm} onRenameCancel={this.onRenameCancel} /> } {this.state.isRenameing &&
<Rename
hasSuffix={dirent.type !== 'dir'}
name={dirent.name}
onRenameConfirm={this.onRenameConfirm}
onRenameCancel={this.onRenameCancel}
/>
}
{!this.state.isRenameing && ( {!this.state.isRenameing && (
<Fragment> <>
{(!dirent.isDir() && !this.canPreview) ? {(!dirent.isDir() && !this.canPreview) ?
<a className="sf-link">{dirent.name}</a> : <a className="sf-link">{dirent.name}</a> :
<a href={dirent.type === 'dir' ? dirHref : fileHref}>{dirent.name}</a> <a href={this.getDirentHref()}>{dirent.name}</a>
} }
</Fragment> </>
)} )}
<br /> <br />
{dirent.size && <span className="item-meta-info">{dirent.size}</span>} {dirent.size && <span className="item-meta-info">{dirent.size}</span>}
@@ -865,7 +960,7 @@ class DirentListItem extends React.Component {
<MoveDirentDialog <MoveDirentDialog
path={this.props.path} path={this.props.path}
repoID={this.props.repoID} repoID={this.props.repoID}
dirent={this.props.dirent} dirent={this.state.dirent}
isMultipleOperation={this.state.isMultipleOperation} isMultipleOperation={this.state.isMultipleOperation}
onItemMove={this.props.onItemMove} onItemMove={this.props.onItemMove}
onCancelMove={this.onItemMoveToggle} onCancelMove={this.onItemMoveToggle}
@@ -878,7 +973,7 @@ class DirentListItem extends React.Component {
<CopyDirentDialog <CopyDirentDialog
path={this.props.path} path={this.props.path}
repoID={this.props.repoID} repoID={this.props.repoID}
dirent={this.props.dirent} dirent={this.state.dirent}
isMultipleOperation={this.state.isMultipleOperation} isMultipleOperation={this.state.isMultipleOperation}
onItemCopy={this.props.onItemCopy} onItemCopy={this.props.onItemCopy}
onCancelCopy={this.onItemCopyToggle} onCancelCopy={this.onItemCopyToggle}
@@ -917,7 +1012,7 @@ class DirentListItem extends React.Component {
<ZipDownloadDialog <ZipDownloadDialog
repoID={this.props.repoID} repoID={this.props.repoID}
path={this.props.path} path={this.props.path}
target={this.props.dirent.name} target={this.state.dirent.name}
toggleDialog={this.closeZipDialog} toggleDialog={this.closeZipDialog}
/> />
</ModalPortal> </ModalPortal>
@@ -958,7 +1053,7 @@ class DirentListItem extends React.Component {
/> />
</ModalPortal> </ModalPortal>
} }
</Fragment> </>
); );
} }
} }

View File

@@ -32,6 +32,10 @@ export default class ListTagPopover extends React.Component {
this.loadTags(); this.loadTags();
} }
componentWillUnmount() {
this.setState = () => {};
}
loadTags = () => { loadTags = () => {
seafileAPI.listRepoTags(this.props.repoID).then(res => { seafileAPI.listRepoTags(this.props.repoID).then(res => {
let repotagList = []; let repotagList = [];

View File

@@ -12,6 +12,12 @@
max-width: 24px; max-width: 24px;
} }
.dir-icon .empty-content {
position: absolute;
width: 1px;
height: 1px;
}
.dir-icon .locked { .dir-icon .locked {
position: absolute; position: absolute;
width: 1rem; width: 1rem;

View File

@@ -20,8 +20,8 @@ class Dirent {
this.has_been_shared_out = false; this.has_been_shared_out = false;
} }
if (json.type === 'file') { if (json.type === 'file') {
this.size_original = json.size; this.size_original = json.size_original || json.size;
this.size = Utils.bytesToSize(json.size); this.size = (typeof json.size === 'number') ? Utils.bytesToSize(json.size) : json.size;
this.is_locked = json.is_locked || false; this.is_locked = json.is_locked || false;
this.is_freezed = json.is_freezed || false; this.is_freezed = json.is_freezed || false;
this.lock_time = json.lock_time || ''; this.lock_time = json.lock_time || '';

View File

@@ -6,7 +6,7 @@ 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';
import { gettext, siteRoot, username, enableVideoThumbnail, enablePDFThumbnail, thumbnailDefaultSize } from '../../utils/constants'; import { gettext, siteRoot, username } from '../../utils/constants';
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';
@@ -588,10 +588,6 @@ class LibContentView extends React.Component {
isSessionExpired: false, isSessionExpired: false,
}); });
if (!this.state.repoEncrypted && direntList.length) {
this.getThumbnails(repoID, path, this.state.direntList);
}
if (this.state.currentRepoInfo.is_admin) { if (this.state.currentRepoInfo.is_admin) {
if (this.foldersSharedOut) { if (this.foldersSharedOut) {
this.identifyFoldersSharedOut(); this.identifyFoldersSharedOut();
@@ -644,37 +640,6 @@ class LibContentView extends React.Component {
this.setState({ itemsShowLength: 100 }); this.setState({ itemsShowLength: 100 });
}; };
shouldDisplayThumbnail = (item) => {
return ((Utils.imageCheck(item.name) || (enableVideoThumbnail && Utils.videoCheck(item.name)) || (enablePDFThumbnail && Utils.pdfCheck(item.name))) && !item.encoded_thumbnail_src);
};
getThumbnails = (repoID, path, direntList) => {
let items = direntList.filter((item) => this.shouldDisplayThumbnail(item));
if (items.length == 0) {
return ;
}
const _this = this;
const len = items.length;
let getThumbnail = (i) => {
const curItem = items[i];
const curItemPath = [path, curItem.name].join('/');
seafileAPI.createThumbnail(repoID, curItemPath, thumbnailDefaultSize).then((res) => {
curItem.encoded_thumbnail_src = res.data.encoded_thumbnail_src;
}).catch((error) => {
// do nothing
}).then(() => {
if (i < len - 1) {
getThumbnail(++i);
} else {
_this.setState({
direntList: direntList
});
}
});
};
getThumbnail(0);
};
updateMoveCopyTreeNode = (path) => { updateMoveCopyTreeNode = (path) => {
let repoID = this.props.repoID; let repoID = this.props.repoID;

View File

@@ -0,0 +1,52 @@
import { seafileAPI } from '../utils/seafile-api';
import { thumbnailDefaultSize } from '../utils/constants';
class ThumbnailCenter {
constructor() {
this.waitingQuery = [];
this.isStartQuery = false;
}
createThumbnail = ({ repoID, path, callback }) => {
this.waitingQuery.push({ repoID, path, callback });
if (!this.isStartQuery) {
this.startQuery();
}
};
cancelThumbnail = ({ repoID, path }) => {
const index = this.waitingQuery.findIndex(q => (q.repoID === repoID && q.path === path));
if (index > -1) {
this.waitingQuery.splice(index, 1);
}
if (this.waitingQuery.length === 0) {
this.isStartQuery = false;
}
};
startQuery = () => {
if (this.waitingQuery.length === 0) {
this.isStartQuery = false;
return;
}
this.isStartQuery = true;
let { repoID, path, callback } = this.waitingQuery[0];
seafileAPI.createThumbnail(repoID, path, thumbnailDefaultSize).then((res) => {
callback && callback(res.data.encoded_thumbnail_src);
}).catch((error) => {
// eslint-disable-next-line no-console
console.log(error);
}).then(() => {
this.waitingQuery.shift();
this.startQuery();
});
};
}
// server generates image and PDF thumbnails quickly, but generates video thumbnails slowly, so use two queues
const imageThumbnailCenter = new ThumbnailCenter();
const videoThumbnailCenter = new ThumbnailCenter();
export { imageThumbnailCenter, videoThumbnailCenter };