import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import MD5 from 'MD5';
import { createRoot } from 'react-dom/client';
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem, UncontrolledTooltip } from 'reactstrap';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import Account from './components/common/account';
import { useGoFileserver, fileServerRoot, gettext, siteRoot, mediaUrl, logoPath, logoWidth, logoHeight, siteTitle,
thumbnailSizeForOriginal, thumbnailDefaultSize, thumbnailSizeForGrid } from './utils/constants';
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';
import ZipDownloadDialog from './components/dialog/zip-download-dialog';
import ImageDialog from './components/dialog/image-dialog';
import FileUploader from './components/shared-link-file-uploader/file-uploader';
import SaveSharedDirDialog from './components/dialog/save-shared-dir-dialog';
import CopyMoveDirentProgressDialog from './components/dialog/copy-move-dirent-progress-dialog';
import RepoInfoBar from './components/repo-info-bar';
import RepoTag from './models/repo-tag';
import { LIST_MODE } from './components/dir-view-mode/constants';
import { MetadataAIOperationsProvider } from './hooks/metadata-ai-operation';
import ViewModes from './components/view-modes';
import SortMenu from './components/sort-menu';
import { TreeHelper, TreeNode, TreeView } from './components/shared-dir-tree-view';
import ResizeBar from './components/resize-bar';
import {
DRAG_HANDLER_HEIGHT, INIT_SIDE_PANEL_RATE, MAX_SIDE_PANEL_RATE, MIN_SIDE_PANEL_RATE
} from './components/resize-bar/constants';
import './css/layout.css';
import './css/header.css';
import './css/shared-dir-view.css';
import './css/grid-view.css';
dayjs.locale(window.app.config.lang);
dayjs.extend(relativeTime);
let loginUser = window.app.pageOptions.name;
let {
token, dirName, dirPath, sharedBy,
repoID, relativePath,
mode, thumbnailSize,
trafficOverLimit, canDownload,
noQuota, canUpload, enableVideoThumbnail, enablePDFThumbnail
} = window.shared.pageOptions;
const showDownloadIcon = !trafficOverLimit && canDownload;
class SharedDirView extends React.Component {
constructor(props) {
super(props);
this.state = {
isTreeDataLoading: true,
treeData: TreeHelper.buildTree(),
// for resizing side/main panels
inResizing: false,
sidePanelRate: parseFloat(localStorage.getItem('sf_side_panel_rate') || INIT_SIDE_PANEL_RATE),
isLoading: true,
errorMsg: '',
items: [],
path: relativePath,
isDropdownMenuOpen: false,
currentMode: mode,
isAllItemsSelected: false,
selectedItems: [],
sortBy: 'name', // 'name' or 'time' or 'size'
sortOrder: 'asc', // 'asc' or 'desc'
isZipDialogOpen: false,
zipFolderPath: '',
usedRepoTags: [],
isSaveSharedDirDialogShow: false,
itemsForSave: [],
asyncCopyMoveTaskId: '',
asyncOperationProgress: 0,
asyncOperatedFilesLength: 0,
isCopyMoveProgressDialogShow: false,
isImagePopupOpen: false,
imageItems: [],
imageIndex: 0
};
this.resizeBarRef = React.createRef();
this.dragHandlerRef = React.createRef();
}
componentDidMount() {
if (trafficOverLimit) {
toaster.danger(gettext('File download is disabled: the share link traffic of owner is used up.'), {
duration: 3
});
}
this.loadTreePanel();
this.listItems();
this.getShareLinkRepoTags();
}
loadTreePanel = () => {
seafileAPI.listSharedDir(token, '/', thumbnailSize).then((res) => {
const { dirent_list } = res.data;
let tree = this.state.treeData;
this.addResponseListToNode(dirent_list, tree.root);
this.setState({
isTreeDataLoading: false,
treeData: tree
});
}).catch(() => {
this.setState({ isTreeDataLoading: false });
});
/* keep it for now
if (relativePath == '/') {
} else {
this.loadNodeAndParentsByPath(relativePath);
}
*/
};
/*
loadNodeAndParentsByPath = (path) => {
};
*/
addResponseListToNode = (list, node) => {
node.isLoaded = true;
node.isExpanded = true;
// only display folders in the tree
const dirList = list.filter(item => item.is_dir);
const direntList = Utils.sortDirentsInSharedDir(dirList, this.state.sortBy, this.state.sortOrder);
const nodeList = direntList.map(object => {
return new TreeNode({ object });
});
node.addChildren(nodeList);
};
listItems = () => {
const { path, currentMode } = this.state;
const thumbnailSize = currentMode == LIST_MODE ? thumbnailDefaultSize : thumbnailSizeForGrid;
seafileAPI.listSharedDir(token, path, thumbnailSize).then((res) => {
const items = res.data['dirent_list'].map(item => {
item.isSelected = false;
return item;
});
this.setState({
isLoading: false,
errorMsg: '',
items: Utils.sortDirentsInSharedDir(items, this.state.sortBy, this.state.sortOrder)
});
this.getThumbnails(thumbnailSize);
}).catch((error) => {
let errorMsg = Utils.getErrorMsg(error);
this.setState({
isLoading: false,
errorMsg: errorMsg
});
});
// update the URL
let normalizedPath = '';
if (path == '/') {
normalizedPath = path;
} else {
normalizedPath = path[path.length - 1] === '/' ? path.slice(0, path.length - 1) : path;
}
let url = new URL(location.href);
let searchParams = new URLSearchParams(url.search);
searchParams.set('p', normalizedPath);
searchParams.set('mode', currentMode);
url.search = searchParams.toString();
url = url.toString();
window.history.pushState({ url: url, path: path }, path, url);
};
sortItems = (sortBy, sortOrder) => {
this.setState({
sortBy: sortBy,
sortOrder: sortOrder,
items: Utils.sortDirentsInSharedDir(this.state.items, sortBy, sortOrder)
});
};
getThumbnails = (thumbnailSize) => {
let items = this.state.items.filter((item) => {
return !item.is_dir &&
(Utils.imageCheck(item.file_name) ||
(enableVideoThumbnail && Utils.videoCheck(item.file_name)) ||
(enablePDFThumbnail && Utils.pdfCheck(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);
};
toggleDropdownMenu = () => {
this.setState({
isDropdownMenuOpen: !this.state.isDropdownMenuOpen
});
};
onDropdownToggleKeyDown = (e) => {
if (e.key == 'Enter' || e.key == 'Space') {
this.toggleDropdownMenu();
}
};
onMenuItemKeyDown = (item, e) => {
if (e.key == 'Enter' || e.key == 'Space') {
item.onClick();
}
};
visitFolder = (folderPath) => {
this.setState({
path: folderPath
}, () => {
this.listItems();
});
};
renderPath = () => {
let opList = [];
if (showDownloadIcon) {
opList.push({
'icon': 'download1',
'text': gettext('ZIP'),
'onClick': this.zipDownloadFolder.bind(this, this.state.path)
});
if (canDownload && loginUser && (loginUser !== sharedBy)) {
opList.push({
'icon': 'save',
'text': gettext('Save'),
'onClick': this.saveAllItems
});
}
}
if (canUpload) {
opList.push({
'icon': 'upload-files',
'disabled': noQuota,
'title': noQuota ? gettext('The owner of this library has run out of space.') : '',
'text': gettext('Upload'),
'onClick': this.onUploadFile
});
}
const zipped = []; // be compatible with the old code
const rootItem = {
path: '/',
name: dirName
};
zipped.push(rootItem);
const { path } = this.state;
if (path != '/') {
const normalizedPath = path[path.length - 1] === '/' ? path.slice(0, path.length - 1) : path;
const pathList = normalizedPath.split('/');
pathList.shift();
let itemPath = '';
const subItems = pathList.map((item, index) => {
itemPath += '/' + item;
return {
path: itemPath + '/', // the ending '/' is necessary
name: item
};
});
zipped.push(...subItems);
}
return (
{zipped.map((item, index) => {
if (index != zipped.length - 1) {
return (
{item.name}
/
);
}
return null;
})}
{(!showDownloadIcon && !canUpload)
? {zipped[zipped.length - 1].name}
: (
{zipped[zipped.length - 1].name}
{canUpload
? <>>
:
}
{opList.map((item, index) => {
if (item == 'Divider') {
return ;
} else {
return (
{item.text}
);
}
})}
)
}
);
};
zipDownloadFolder = (folderPath) => {
if (!useGoFileserver) {
this.setState({
isZipDialogOpen: true,
zipFolderPath: folderPath
});
}
else {
seafileAPI.getShareLinkZipTask(token, folderPath).then((res) => {
const zipToken = res.data['zip_token'];
location.href = `${fileServerRoot}zip/${zipToken}`;
}).catch((error) => {
let errorMsg = Utils.getErrorMsg(error);
this.setState({
isLoading: false,
errorMsg: errorMsg
});
});
}
};
zipDownloadSelectedItems = () => {
const { path } = this.state;
if (!useGoFileserver) {
this.setState({
isZipDialogOpen: true,
zipFolderPath: path,
selectedItems: this.state.items.filter(item => item.isSelected)
.map(item => item.file_name || item.folder_name)
});
}
else {
let target = this.state.items.filter(item => item.isSelected).map(item => item.file_name || item.folder_name);
seafileAPI.getShareLinkDirentsZipTask(token, path, target).then((res) => {
const zipToken = res.data['zip_token'];
location.href = `${fileServerRoot}zip/${zipToken}`;
}).catch((error) => {
let errorMsg = Utils.getErrorMsg(error);
this.setState({
isLoading: false,
errorMsg: errorMsg
});
});
}
};
async getAsyncCopyMoveProgress() {
let { asyncCopyMoveTaskId } = this.state;
try {
let res = await seafileAPI.queryAsyncOperationProgress(asyncCopyMoveTaskId);
let data = res.data;
if (data.failed) {
let message = gettext('Failed to copy files to another library.');
toaster.danger(message);
this.setState({
asyncOperationProgress: 0,
isCopyMoveProgressDialogShow: false,
});
return;
}
if (data.successful) {
this.setState({
asyncOperationProgress: 0,
isCopyMoveProgressDialogShow: false,
});
let message = gettext('Successfully copied files to another library.');
toaster.success(message);
return;
}
// init state: total is 0
let asyncOperationProgress = !data.total ? 0 : parseInt((data.done / data.total * 100).toFixed(2));
this.getAsyncCopyMoveProgress();
this.setState({ asyncOperationProgress: asyncOperationProgress });
} catch (error) {
this.setState({
asyncOperationProgress: 0,
isCopyMoveProgressDialogShow: false,
});
}
}
saveSelectedItems = () => {
this.setState({
isSaveSharedDirDialogShow: true,
itemsForSave: this.state.items.filter(item => item.isSelected)
.map(item => item.file_name || item.folder_name)
});
};
saveAllItems = () => {
this.setState({
isSaveSharedDirDialogShow: true,
itemsForSave: this.state.items
.map(item => item.file_name || item.folder_name)
});
};
toggleSaveSharedDirCancel = () => {
this.setState({
isSaveSharedDirDialogShow: false,
itemsForSave: []
});
};
handleSaveSharedDir = (destRepoID, dstPath) => {
const { path, itemsForSave } = this.state;
seafileAPI.saveSharedDir(destRepoID, dstPath, token, path, itemsForSave).then((res) => {
this.setState({
isSaveSharedDirDialogShow: false,
itemsForSave: [],
isCopyMoveProgressDialogShow: true,
asyncCopyMoveTaskId: res.data.task_id,
asyncOperatedFilesLength: itemsForSave.length,
}, () => {
this.getAsyncCopyMoveProgress();
});
}).catch((error) => {
let errMessage = Utils.getErrorMsg(error);
this.setState({ errMessage: errMessage });
});
};
onProgressDialogToggle = () => {
let { asyncOperationProgress } = this.state;
if (asyncOperationProgress !== 100) {
let taskId = this.state.asyncCopyMoveTaskId;
seafileAPI.cancelCopyMoveOperation(taskId);
}
this.setState({
asyncOperationProgress: 0,
isCopyMoveProgressDialogShow: false,
});
};
closeZipDialog = () => {
this.setState({
isZipDialogOpen: false,
zipFolderPath: '',
selectedItems: []
});
};
// 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,
'parentDir': item.file_path.slice(0, item.file_path.indexOf(name)),
'thumbnail': `${siteRoot}thumbnail/${token}/${thumbnailSizeForOriginal}${Utils.encodePath(item.file_path)}`,
'src': src,
'downloadURL': fileURL + '&dl=1'
};
};
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
}));
};
unselectItems = () => {
this.setState({
isAllItemsSelected: false,
items: this.state.items.map((item) => {
item.isSelected = false;
return item;
})
});
};
toggleAllSelected = () => {
this.setState((prevState) => ({
isAllItemsSelected: !prevState.isAllItemsSelected,
items: this.state.items.map((item) => {
item.isSelected = !prevState.isAllItemsSelected;
return item;
})
}));
};
toggleItemSelected = (targetItem, isSelected) => {
this.setState({
items: this.state.items.map((item) => {
if (item === targetItem) {
item.isSelected = isSelected;
}
return item;
})
}, () => {
this.setState({
isAllItemsSelected: !this.state.items.some(item => !item.isSelected)
});
});
};
onUploadFile = (e) => {
e.nativeEvent.stopImmediatePropagation();
this.uploader.onFileUpload();
};
onFileUploadSuccess = (direntObject) => {
const { path } = this.state;
const { name, size } = direntObject;
const newItem = {
isSelected: false,
file_name: name,
file_path: Utils.joinPath(path, name),
is_dir: false,
last_modified: dayjs().format(),
size: size
};
const folderItems = this.state.items.filter(item => { return item.is_dir; });
// put the new file as the first file
let items = Array.from(this.state.items);
items.splice(folderItems.length, 0, newItem);
this.setState({ items: items });
seafileAPI.shareLinksUploadDone(token, Utils.joinPath(dirPath, name));
};
getShareLinkRepoTags = () => {
seafileAPI.getShareLinkRepoTags(token).then(res => {
let usedRepoTags = [];
res.data.repo_tags.forEach(item => {
let usedRepoTag = new RepoTag(item);
if (usedRepoTag.fileCount > 0) {
usedRepoTags.push(usedRepoTag);
}
});
this.setState({ usedRepoTags: usedRepoTags });
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
};
switchMode = (mode) => {
const { currentMode } = this.state;
if (mode == currentMode) {
return;
} else {
this.setState({
currentMode: mode,
isLoading: true
}, () => {
this.listItems();
});
}
};
onSelectSortOption = (item) => {
const [sortBy, sortOrder] = item.value.split('-');
this.sortItems(sortBy, sortOrder);
};
onTreeNodeCollapse = (node) => {
const tree = TreeHelper.collapseNode(this.state.treeData, node);
this.setState({ treeData: tree });
};
onTreeNodeExpanded = (node) => {
let tree = this.state.treeData.clone();
node = tree.getNodeByPath(node.path);
if (!node.isLoaded) {
seafileAPI.listSharedDir(token, node.path, thumbnailSize).then((res) => {
const { dirent_list } = res.data;
this.addResponseListToNode(dirent_list, node);
this.setState({ treeData: tree });
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
} else {
tree.expandNode(node);
this.setState({ treeData: tree });
}
};
onTreeNodeClick = (node) => {
if (node.object.is_dir) {
if (node.isLoaded && node.path === this.state.path) {
if (node.isExpanded) {
let tree = TreeHelper.collapseNode(this.state.treeData, node);
this.setState({ treeData: tree });
} else {
let tree = this.state.treeData.clone();
node = tree.getNodeByPath(node.path);
tree.expandNode(node);
this.setState({ treeData: tree });
}
}
if (!node.isLoaded) {
let tree = this.state.treeData.clone();
node = tree.getNodeByPath(node.path);
seafileAPI.listSharedDir(token, node.path, thumbnailSize).then((res) => {
const { dirent_list } = res.data;
this.addResponseListToNode(dirent_list, node);
tree.collapseNode(node);
this.setState({ treeData: tree });
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
if (node.path === this.state.path) {
return;
}
this.visitFolder(node.path);
}
};
onResizeMouseUp = () => {
if (this.state.inResizing) {
this.setState({
inResizing: false
});
}
localStorage.setItem('sf_side_panel_rate', this.state.sidePanelRate);
};
onResizeMouseDown = () => {
this.setState({
inResizing: true
});
};
onResizeMouseMove = (e) => {
let rate = e.nativeEvent.clientX / window.innerWidth;
this.setState({
sidePanelRate: Math.max(Math.min(rate, MAX_SIDE_PANEL_RATE), MIN_SIDE_PANEL_RATE),
});
};
onResizeMouseOver = (event) => {
if (!this.dragHandlerRef.current) return;
const { top } = this.resizeBarRef.current.getBoundingClientRect();
const dragHandlerRefTop = event.pageY - top - DRAG_HANDLER_HEIGHT / 2;
this.setDragHandlerTop(dragHandlerRefTop);
};
setDragHandlerTop = (top) => {
this.dragHandlerRef.current.style.top = top + 'px';
};
render() {
const {
usedRepoTags, currentMode: mode,
sortBy, sortOrder, isTreeDataLoading, treeData, path,
sidePanelRate, inResizing
} = this.state;
const mainPanelStyle = {
userSelect: inResizing ? 'none' : '',
flex: sidePanelRate ? `1 0 ${(1 - sidePanelRate) * 100}%` : `0 0 ${100 - INIT_SIDE_PANEL_RATE * 100}%`,
};
const sidePanelStyle = {
userSelect: inResizing ? 'none' : '',
flex: sidePanelRate ? `0 0 ${sidePanelRate * 100}%` : `0 0 ${INIT_SIDE_PANEL_RATE * 100}%`,
};
const isDesktop = Utils.isDesktop();
const selectedItemsLength = this.state.items.filter(item => item.isSelected).length;
const isRepoInfoBarShown = isDesktop && path == '/' && usedRepoTags.length != 0;
return (
{loginUser &&
}
{dirName}
{gettext('Shared by: ')}{sharedBy}
{isTreeDataLoading ? : (
)}
{isDesktop &&
}
{(showDownloadIcon && this.state.items.some(item => item.isSelected))
? (
{`${selectedItemsLength} ${gettext('selected')}`}
{(canDownload && loginUser && (loginUser !== sharedBy)) &&
}
)
: (
{gettext('Current path: ')}
{this.renderPath()}
)
}
{isDesktop && (
<>
>
)}
{!noQuota && canUpload && (
this.uploader = uploader}
dragAndDrop={false}
token={token}
path={dirPath === '/' ? dirPath : dirPath.replace(/\/+$/, '')}
relativePath={path === '/' ? path : path.replace(/\/+$/, '')}
repoID={repoID}
onFileUploadSuccess={this.onFileUploadSuccess}
/>
)}
{isRepoInfoBarShown && (
)}
{this.state.isZipDialogOpen &&
}
{this.state.isSaveSharedDirDialogShow &&
}
{this.state.isCopyMoveProgressDialogShow && (
)}
{this.state.isImagePopupOpen &&
}
);
}
}
class Content extends React.Component {
constructor(props) {
super(props);
}
sortByName = (e) => {
e.preventDefault();
const sortBy = 'name';
const sortOrder = this.props.sortOrder == 'asc' ? 'desc' : 'asc';
this.props.sortItems(sortBy, sortOrder);
};
sortByTime = (e) => {
e.preventDefault();
const sortBy = 'time';
const sortOrder = this.props.sortOrder == 'asc' ? 'desc' : 'asc';
this.props.sortItems(sortBy, sortOrder);
};
sortBySize = (e) => {
e.preventDefault();
const sortBy = 'size';
const sortOrder = this.props.sortOrder == 'asc' ? 'desc' : 'asc';
this.props.sortItems(sortBy, sortOrder);
};
render() {
const {
isDesktop,
isLoading, errorMsg, mode, items,
sortBy, sortOrder,
isAllItemsSelected
} = this.props;
if (isLoading) {
return ;
}
if (errorMsg) {
return
{errorMsg}
;
}
const tbody = (
{items.map((item, index) => {
return ;
})}
);
if (!isDesktop) {
return (
);
}
const sortIcon = ;
return mode == LIST_MODE ? (
) : (
{items.map((item, index) => {
return ;
})}
);
}
}
Content.propTypes = {
isDesktop: PropTypes.bool,
isLoading: PropTypes.bool,
isAllItemsSelected: PropTypes.bool,
errorMsg: PropTypes.string,
mode: PropTypes.string,
items: PropTypes.array,
sortItems: PropTypes.func,
sortBy: PropTypes.string,
sortOrder: PropTypes.string,
toggleAllSelected: PropTypes.func,
toggleItemSelected: PropTypes.func,
zipDownloadFolder: PropTypes.func,
showImagePopup: PropTypes.func,
visitFolder: PropTypes.func.isRequired
};
class Item extends React.Component {
constructor(props) {
super(props);
this.state = {
isIconShown: false,
isOpMenuOpen: false
};
}
toggleOpMenu = () => {
this.setState({ isOpMenuOpen: !this.state.isOpMenuOpen });
};
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);
};
toggleItemSelected = (e) => {
this.props.toggleItemSelected(this.props.item, e.target.checked);
};
onFolderItemClick = (e) => {
e.preventDefault();
const { item } = this.props;
const { folder_path } = item;
this.props.visitFolder(folder_path);
};
render() {
const { item, isDesktop, mode } = this.props;
const { isIconShown } = this.state;
let toolTipID = '';
let tagTitle = '';
if (item.file_tags && item.file_tags.length > 0) {
toolTipID = MD5(item.file_name).slice(0, 7);
tagTitle = item.file_tags.map(item => item.tag_name).join(' ');
}
if (item.is_dir) {
return isDesktop ? (
{showDownloadIcon &&
|
}
}) |
{item.folder_name}
|
|
|
{dayjs(item.last_modified).fromNow()} |
{showDownloadIcon &&
}
|
) : (
}) |
{item.folder_name}
{dayjs(item.last_modified).fromNow()}
|
{showDownloadIcon &&
}
|
);
} else {
const fileURL = `${siteRoot}d/${token}/files/?p=${encodeURIComponent(item.file_path)}`;
const thumbnailURL = item.encoded_thumbnail_src ? `${siteRoot}${item.encoded_thumbnail_src}` : '';
return isDesktop ? (
{showDownloadIcon &&
|
}
{thumbnailURL ?
:
}
|
{item.file_name}
|
{(item.file_tags && item.file_tags.length > 0) && (
{item.file_tags.map((fileTag, index) => {
let length = item.file_tags.length;
return (
);
})}
{tagTitle}
)}
|
{Utils.bytesToSize(item.size)} |
{dayjs(item.last_modified).fromNow()} |
{showDownloadIcon &&
}
|
) : (
{thumbnailURL ?
:
}
|
{item.file_name}
{Utils.bytesToSize(item.size)}
{dayjs(item.last_modified).fromNow()}
|
{showDownloadIcon &&
}
|
);
}
}
}
Item.propTypes = {
isDesktop: PropTypes.bool,
mode: PropTypes.string,
item: PropTypes.object,
sortItems: PropTypes.func,
sortBy: PropTypes.string,
sortOrder: PropTypes.string,
toggleAllSelected: PropTypes.func,
toggleItemSelected: PropTypes.func,
zipDownloadFolder: PropTypes.func,
showImagePopup: PropTypes.func,
visitFolder: PropTypes.func.isRequired
};
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);
};
onFolderItemClick = (e) => {
e.preventDefault();
const { item } = this.props;
const { folder_path } = item;
this.props.visitFolder(folder_path);
};
render() {
const { item, mode } = this.props;
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 (
{item.folder_name}
{showDownloadIcon &&
}
);
} else {
const fileURL = `${siteRoot}d/${token}/files/?p=${encodeURIComponent(item.file_path)}`;
const thumbnailURL = item.encoded_thumbnail_src ? `${siteRoot}${item.encoded_thumbnail_src}` : '';
return (
{thumbnailURL ?
:
}
{item.file_name}
{showDownloadIcon &&
}
);
}
}
}
GridItem.propTypes = {
mode: PropTypes.string,
item: PropTypes.object,
zipDownloadFolder: PropTypes.func,
showImagePopup: PropTypes.func,
visitFolder: PropTypes.func.isRequired
};
const root = createRoot(document.getElementById('wrapper'));
root.render();