2019-04-08 04:04:17 +00:00
|
|
|
import React from 'react';
|
|
|
|
import ReactDOM from 'react-dom';
|
|
|
|
import { Button } from 'reactstrap';
|
|
|
|
import moment from 'moment';
|
|
|
|
import Account from './components/common/account';
|
2019-04-09 04:32:25 +00:00
|
|
|
import { gettext, siteRoot, mediaUrl, logoPath, logoWidth, logoHeight, siteTitle, thumbnailSizeForOriginal } from './utils/constants';
|
2019-04-08 04:04:17 +00:00
|
|
|
import { Utils } from './utils/utils';
|
|
|
|
import { seafileAPI } from './utils/seafile-api';
|
|
|
|
import Loading from './components/loading';
|
|
|
|
import toaster from './components/toast';
|
|
|
|
import ModalPortal from './components/modal-portal';
|
2019-04-12 07:54:05 +00:00
|
|
|
import ZipDownloadDialog from './components/dialog/zip-download-dialog';
|
2019-04-09 04:32:25 +00:00
|
|
|
import ImageDialog from './components/dialog/image-dialog';
|
2019-04-08 04:04:17 +00:00
|
|
|
|
|
|
|
import './css/shared-dir-view.css';
|
2019-07-03 08:12:10 +00:00
|
|
|
import './css/grid-view.css';
|
2019-04-08 04:04:17 +00:00
|
|
|
|
|
|
|
let loginUser = window.app.pageOptions.name;
|
2019-07-03 08:12:10 +00:00
|
|
|
const { token, trafficOverLimit, dirName, sharedBy, path, canDownload, mode, thumbnailSize } = window.shared.pageOptions;
|
2019-04-08 04:04:17 +00:00
|
|
|
|
|
|
|
const showDownloadIcon = !trafficOverLimit && canDownload;
|
|
|
|
|
|
|
|
class SharedDirView extends React.Component {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
isLoading: true,
|
|
|
|
errorMsg: '',
|
|
|
|
items: [],
|
2019-04-09 04:32:25 +00:00
|
|
|
|
2019-04-08 04:04:17 +00:00
|
|
|
isZipDialogOpen: false,
|
2019-04-09 04:32:25 +00:00
|
|
|
zipFolderPath: '',
|
|
|
|
|
|
|
|
isImagePopupOpen: false,
|
|
|
|
imageItems: [],
|
|
|
|
imageIndex: 0
|
2019-04-08 04:04:17 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
if (trafficOverLimit) {
|
|
|
|
toaster.danger(gettext('File download is disabled: the share link traffic of owner is used up.'), {
|
|
|
|
duration: 3
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-07-03 08:12:10 +00:00
|
|
|
seafileAPI.listSharedDir(token, path, thumbnailSize).then((res) => {
|
2019-04-08 04:04:17 +00:00
|
|
|
const items = res.data['dirent_list'];
|
|
|
|
this.setState({
|
|
|
|
isLoading: false,
|
|
|
|
errorMsg: '',
|
|
|
|
items: items
|
|
|
|
});
|
2019-04-09 04:32:25 +00:00
|
|
|
this.getThumbnails();
|
2019-04-08 04:04:17 +00:00
|
|
|
}).catch((error) => {
|
2019-07-16 02:01:09 +00:00
|
|
|
let errorMsg = Utils.getErrorMsg(error);
|
2019-04-08 04:04:17 +00:00
|
|
|
this.setState({
|
|
|
|
isLoading: false,
|
|
|
|
errorMsg: errorMsg
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-04-09 04:32:25 +00:00
|
|
|
getThumbnails = () => {
|
|
|
|
let items = this.state.items.filter((item) => {
|
|
|
|
return !item.is_dir && Utils.imageCheck(item.file_name) && !item.encoded_thumbnail_src;
|
|
|
|
});
|
|
|
|
if (items.length == 0) {
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
const len = items.length;
|
|
|
|
const _this = this;
|
|
|
|
let getThumbnail = function(i) {
|
|
|
|
const curItem = items[i];
|
|
|
|
seafileAPI.getShareLinkThumbnail(token, curItem.file_path, thumbnailSize).then((res) => {
|
|
|
|
curItem.encoded_thumbnail_src = res.data.encoded_thumbnail_src;
|
|
|
|
}).catch((error) => {
|
|
|
|
// do nothing
|
|
|
|
}).then(() => {
|
|
|
|
if (i < len - 1) {
|
|
|
|
getThumbnail(++i);
|
|
|
|
} else {
|
|
|
|
// when done, `setState()`
|
|
|
|
_this.setState({
|
|
|
|
items: _this.state.items
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
getThumbnail(0);
|
|
|
|
}
|
|
|
|
|
2019-04-08 04:04:17 +00:00
|
|
|
renderPath = () => {
|
|
|
|
// path: '/', or '/g/'
|
|
|
|
if (path == '/') {
|
|
|
|
return dirName;
|
|
|
|
}
|
|
|
|
|
|
|
|
let pathList = path.substr(0, path.length -1).split('/');
|
|
|
|
return (
|
|
|
|
<React.Fragment>
|
2019-07-03 08:12:10 +00:00
|
|
|
<a href={`?p=${encodeURIComponent('/')}&mode=${mode}`}>{dirName}</a>
|
2019-04-08 04:04:17 +00:00
|
|
|
<span> / </span>
|
|
|
|
{pathList.map((item, index) => {
|
|
|
|
if (index > 0 && index != pathList.length - 1) {
|
|
|
|
return (
|
|
|
|
<React.Fragment key={index}>
|
2019-07-03 08:12:10 +00:00
|
|
|
<a href={`?p=${encodeURIComponent(pathList.slice(0, index+1).join('/'))}&mode=${mode}`}>{pathList[index]}</a>
|
2019-04-08 04:04:17 +00:00
|
|
|
<span> / </span>
|
|
|
|
</React.Fragment>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)}
|
|
|
|
{pathList[pathList.length - 1]}
|
|
|
|
</React.Fragment>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
zipDownloadFolder = (folderPath) => {
|
|
|
|
this.setState({
|
|
|
|
isZipDialogOpen: true,
|
|
|
|
zipFolderPath: folderPath
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
closeZipDialog = () => {
|
|
|
|
this.setState({
|
|
|
|
isZipDialogOpen: false,
|
|
|
|
zipFolderPath: ''
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-04-09 04:32:25 +00:00
|
|
|
// for image popup
|
|
|
|
prepareImageItem = (item) => {
|
|
|
|
const name = item.file_name;
|
|
|
|
const fileExt = name.substr(name.lastIndexOf('.') + 1).toLowerCase();
|
|
|
|
const isGIF = fileExt == 'gif';
|
|
|
|
|
|
|
|
let src;
|
|
|
|
const fileURL = `${siteRoot}d/${token}/files/?p=${encodeURIComponent(item.file_path)}`;
|
|
|
|
if (!isGIF) {
|
|
|
|
src = `${siteRoot}thumbnail/${token}/${thumbnailSizeForOriginal}${Utils.encodePath(item.file_path)}`;
|
|
|
|
} else {
|
|
|
|
src = `${fileURL}&raw=1`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
'name': name,
|
|
|
|
'url': fileURL,
|
|
|
|
'src': src
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
showImagePopup = (curItem) => {
|
|
|
|
const items = this.state.items.filter((item) => {
|
|
|
|
return !item.is_dir && Utils.imageCheck(item.file_name);
|
|
|
|
});
|
|
|
|
const imageItems = items.map((item) => {
|
|
|
|
return this.prepareImageItem(item);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
isImagePopupOpen: true,
|
|
|
|
imageItems: imageItems,
|
|
|
|
imageIndex: items.indexOf(curItem)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
closeImagePopup = () => {
|
|
|
|
this.setState({
|
|
|
|
isImagePopupOpen: false
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
moveToPrevImage = () => {
|
|
|
|
const imageItemsLength = this.state.imageItems.length;
|
|
|
|
this.setState((prevState) => ({
|
|
|
|
imageIndex: (prevState.imageIndex + imageItemsLength - 1) % imageItemsLength
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
moveToNextImage = () => {
|
|
|
|
const imageItemsLength = this.state.imageItems.length;
|
|
|
|
this.setState((prevState) => ({
|
|
|
|
imageIndex: (prevState.imageIndex + 1) % imageItemsLength
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2019-04-08 04:04:17 +00:00
|
|
|
render() {
|
2019-07-03 08:12:10 +00:00
|
|
|
const modeBaseClass = 'btn btn-secondary btn-icon sf-view-mode-btn';
|
2019-04-08 04:04:17 +00:00
|
|
|
return (
|
|
|
|
<React.Fragment>
|
|
|
|
<div className="h-100 d-flex flex-column">
|
|
|
|
<div className="top-header d-flex justify-content-between">
|
|
|
|
<a href={siteRoot}>
|
|
|
|
<img src={mediaUrl + logoPath} height={logoHeight} width={logoWidth} title={siteTitle} alt="logo" />
|
|
|
|
</a>
|
|
|
|
{loginUser && <Account />}
|
|
|
|
</div>
|
|
|
|
<div className="o-auto">
|
|
|
|
<div className="shared-dir-view-main">
|
|
|
|
<h2 className="title">{dirName}</h2>
|
|
|
|
<p>{gettext('Shared by: ')}{sharedBy}</p>
|
|
|
|
<div className="d-flex justify-content-between align-items-center op-bar">
|
|
|
|
<p className="m-0">{gettext('Current path: ')}{this.renderPath()}</p>
|
2019-07-03 08:12:10 +00:00
|
|
|
<div>
|
|
|
|
<div className="view-mode btn-group">
|
|
|
|
<a href={`?p=${encodeURIComponent(path)}&mode=list`} className={`${modeBaseClass} sf2-icon-list-view ${mode == 'list' ? 'current-mode' : ''}`} title={gettext('List')}></a>
|
|
|
|
<a href={`?p=${encodeURIComponent(path)}&mode=grid`} className={`${modeBaseClass} sf2-icon-grid-view ${mode == 'grid' ? 'current-mode' : ''}`} title={gettext('Grid')}></a>
|
|
|
|
</div>
|
|
|
|
{showDownloadIcon &&
|
|
|
|
<Button color="success" onClick={this.zipDownloadFolder.bind(this, path)} className="ml-2 zip-btn">{gettext('ZIP')}</Button>
|
|
|
|
}
|
|
|
|
</div>
|
2019-04-08 04:04:17 +00:00
|
|
|
</div>
|
|
|
|
<Content
|
|
|
|
data={this.state}
|
|
|
|
zipDownloadFolder={this.zipDownloadFolder}
|
2019-04-09 04:32:25 +00:00
|
|
|
showImagePopup={this.showImagePopup}
|
2019-04-08 04:04:17 +00:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{this.state.isZipDialogOpen &&
|
|
|
|
<ModalPortal>
|
2019-04-12 07:54:05 +00:00
|
|
|
<ZipDownloadDialog
|
2019-04-08 04:04:17 +00:00
|
|
|
token={token}
|
|
|
|
path={this.state.zipFolderPath}
|
|
|
|
toggleDialog={this.closeZipDialog}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
}
|
2019-04-09 04:32:25 +00:00
|
|
|
{this.state.isImagePopupOpen &&
|
|
|
|
<ModalPortal>
|
|
|
|
<ImageDialog
|
|
|
|
imageItems={this.state.imageItems}
|
|
|
|
imageIndex={this.state.imageIndex}
|
|
|
|
closeImagePopup={this.closeImagePopup}
|
|
|
|
moveToPrevImage={this.moveToPrevImage}
|
|
|
|
moveToNextImage={this.moveToNextImage}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
}
|
2019-04-08 04:04:17 +00:00
|
|
|
</React.Fragment>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Content extends React.Component {
|
2019-07-03 08:12:10 +00:00
|
|
|
|
2019-04-08 04:04:17 +00:00
|
|
|
render() {
|
|
|
|
const { isLoading, errorMsg, items } = this.props.data;
|
|
|
|
|
|
|
|
if (isLoading) {
|
|
|
|
return <Loading />;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errorMsg) {
|
|
|
|
return <p className="error mt-6 text-center">{errorMsg}</p>;
|
|
|
|
}
|
|
|
|
|
2019-07-03 08:12:10 +00:00
|
|
|
return mode == 'list' ? (
|
2019-04-08 04:04:17 +00:00
|
|
|
<table className="table-hover">
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th width="5%"></th>
|
|
|
|
<th width="55%">{gettext('Name')}</th>
|
|
|
|
<th width="14%">{gettext('Size')}</th>
|
|
|
|
<th width="16%">{gettext('Last Update')}</th>
|
|
|
|
<th width="10%">{gettext('Operations')}</th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody>
|
|
|
|
{items.map((item, index) => {
|
|
|
|
return <Item
|
|
|
|
key={index}
|
|
|
|
item={item}
|
|
|
|
zipDownloadFolder={this.props.zipDownloadFolder}
|
2019-04-09 04:32:25 +00:00
|
|
|
showImagePopup={this.props.showImagePopup}
|
2019-04-08 04:04:17 +00:00
|
|
|
/>;
|
|
|
|
})}
|
|
|
|
</tbody>
|
|
|
|
</table>
|
2019-07-03 08:12:10 +00:00
|
|
|
) : (
|
|
|
|
<ul className="grid-view">
|
|
|
|
{items.map((item, index) => {
|
|
|
|
return <GridItem
|
|
|
|
key={index}
|
|
|
|
item={item}
|
|
|
|
zipDownloadFolder={this.props.zipDownloadFolder}
|
|
|
|
showImagePopup={this.props.showImagePopup}
|
|
|
|
/>;
|
|
|
|
})}
|
|
|
|
</ul>
|
2019-04-08 04:04:17 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Item extends React.Component {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
isIconShown: false
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
handleMouseOver = () => {
|
|
|
|
this.setState({isIconShown: true});
|
|
|
|
}
|
|
|
|
|
|
|
|
handleMouseOut = () => {
|
|
|
|
this.setState({isIconShown: false});
|
|
|
|
}
|
|
|
|
|
|
|
|
zipDownloadFolder = (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
this.props.zipDownloadFolder.bind(this, this.props.item.folder_path)();
|
|
|
|
}
|
|
|
|
|
2019-07-03 08:12:10 +00:00
|
|
|
handleFileClick = (e) => {
|
2019-04-09 04:32:25 +00:00
|
|
|
const item = this.props.item;
|
|
|
|
if (!Utils.imageCheck(item.file_name)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
this.props.showImagePopup(item);
|
|
|
|
}
|
|
|
|
|
2019-04-08 04:04:17 +00:00
|
|
|
render() {
|
|
|
|
const item = this.props.item;
|
|
|
|
const { isIconShown } = this.state;
|
|
|
|
|
|
|
|
if (item.is_dir) {
|
|
|
|
return (
|
|
|
|
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
|
|
|
|
<td className="text-center"><img src={Utils.getFolderIconUrl()} alt="" width="24" /></td>
|
|
|
|
<td>
|
2019-07-03 08:12:10 +00:00
|
|
|
<a href={`?p=${encodeURIComponent(item.folder_path.substr(0, item.folder_path.length - 1))}&mode=${mode}`}>{item.folder_name}</a>
|
2019-04-08 04:04:17 +00:00
|
|
|
</td>
|
|
|
|
<td></td>
|
|
|
|
<td>{moment(item.last_modified).format('YYYY-MM-DD')}</td>
|
|
|
|
<td>
|
|
|
|
{showDownloadIcon &&
|
2019-04-09 07:26:21 +00:00
|
|
|
<a className={`action-icon sf2-icon-download${isIconShown ? '' : ' invisible'}`} href="#" onClick={this.zipDownloadFolder} title={gettext('Download')} aria-label={gettext('Download')}>
|
2019-04-08 04:04:17 +00:00
|
|
|
</a>
|
|
|
|
}
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
const fileURL = `${siteRoot}d/${token}/files/?p=${encodeURIComponent(item.file_path)}`;
|
2019-04-09 04:32:25 +00:00
|
|
|
const thumbnailURL = item.encoded_thumbnail_src ? `${siteRoot}${item.encoded_thumbnail_src}` : '';
|
2019-04-08 04:04:17 +00:00
|
|
|
return (
|
|
|
|
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
|
2019-04-09 04:32:25 +00:00
|
|
|
<td className="text-center">
|
|
|
|
{thumbnailURL ?
|
|
|
|
<img className="thumbnail" src={thumbnailURL} alt="" /> :
|
|
|
|
<img src={Utils.getFileIconUrl(item.file_name)} alt="" width="24" />
|
|
|
|
}
|
|
|
|
</td>
|
2019-04-08 04:04:17 +00:00
|
|
|
<td>
|
2019-07-03 08:12:10 +00:00
|
|
|
<a href={fileURL} onClick={this.handleFileClick}>{item.file_name}</a>
|
2019-04-08 04:04:17 +00:00
|
|
|
</td>
|
|
|
|
<td>{Utils.bytesToSize(item.size)}</td>
|
|
|
|
<td>{moment(item.last_modified).format('YYYY-MM-DD')}</td>
|
|
|
|
<td>
|
|
|
|
{showDownloadIcon &&
|
2019-04-09 07:26:21 +00:00
|
|
|
<a className={`action-icon sf2-icon-download${isIconShown ? '' : ' invisible'}`} href={`${fileURL}&dl=1`} title={gettext('Download')} aria-label={gettext('Download')}>
|
2019-04-08 04:04:17 +00:00
|
|
|
</a>
|
|
|
|
}
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-03 08:12:10 +00:00
|
|
|
class GridItem extends React.Component {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
isIconShown: false
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
handleMouseOver = () => {
|
|
|
|
this.setState({isIconShown: true});
|
|
|
|
}
|
|
|
|
|
|
|
|
handleMouseOut = () => {
|
|
|
|
this.setState({isIconShown: false});
|
|
|
|
}
|
|
|
|
|
|
|
|
zipDownloadFolder = (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
this.props.zipDownloadFolder.bind(this, this.props.item.folder_path)();
|
|
|
|
}
|
|
|
|
|
|
|
|
handleFileClick = (e) => {
|
|
|
|
const item = this.props.item;
|
|
|
|
if (!Utils.imageCheck(item.file_name)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
this.props.showImagePopup(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const item = this.props.item;
|
|
|
|
const { isIconShown } = this.state;
|
|
|
|
|
|
|
|
if (item.is_dir) {
|
|
|
|
const folderURL = `?p=${encodeURIComponent(item.folder_path.substr(0, item.folder_path.length - 1))}&mode=${mode}`;
|
|
|
|
return (
|
|
|
|
<li className="grid-item" onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
|
|
|
|
<a href={folderURL} className="grid-file-img-link d-block">
|
|
|
|
<img src={Utils.getFolderIconUrl(false, 192)} alt="" width="96" height="96" />
|
|
|
|
</a>
|
|
|
|
<a href={folderURL} className="grid-file-name grid-file-name-link">{item.folder_name}</a>
|
|
|
|
{showDownloadIcon &&
|
|
|
|
<a className={`action-icon sf2-icon-download${isIconShown ? '' : ' invisible'}`} href="#" onClick={this.zipDownloadFolder} title={gettext('Download')} aria-label={gettext('Download')}>
|
|
|
|
</a>
|
|
|
|
}
|
|
|
|
</li>
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
const fileURL = `${siteRoot}d/${token}/files/?p=${encodeURIComponent(item.file_path)}`;
|
|
|
|
const thumbnailURL = item.encoded_thumbnail_src ? `${siteRoot}${item.encoded_thumbnail_src}` : '';
|
|
|
|
return (
|
|
|
|
<li className="grid-item" onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
|
|
|
|
<a href={fileURL} className="grid-file-img-link d-block" onClick={this.handleFileClick}>
|
|
|
|
{thumbnailURL ?
|
|
|
|
<img className="thumbnail" src={thumbnailURL} alt="" /> :
|
|
|
|
<img src={Utils.getFileIconUrl(item.file_name, 192)} alt="" width="96" height="96" />
|
|
|
|
}
|
|
|
|
</a>
|
|
|
|
<a href={fileURL} className="grid-file-name grid-file-name-link" onClick={this.handleFileClick}>{item.file_name}</a>
|
|
|
|
{showDownloadIcon &&
|
|
|
|
<a className={`action-icon sf2-icon-download${isIconShown ? '' : ' invisible'}`} href={`${fileURL}&dl=1`} title={gettext('Download')} aria-label={gettext('Download')}>
|
|
|
|
</a>
|
|
|
|
}
|
|
|
|
</li>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-08 04:04:17 +00:00
|
|
|
ReactDOM.render(
|
|
|
|
<SharedDirView />,
|
|
|
|
document.getElementById('wrapper')
|
|
|
|
);
|