2024-09-13 12:28:20 +00:00
|
|
|
import React from 'react';
|
2019-02-20 03:54:25 +00:00
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import cookie from 'react-cookies';
|
2024-10-21 03:29:17 +00:00
|
|
|
import dayjs from 'dayjs';
|
|
|
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
2024-09-13 12:28:20 +00:00
|
|
|
import classnames from 'classnames';
|
2024-08-12 04:14:03 +00:00
|
|
|
import MediaQuery from 'react-responsive';
|
|
|
|
import { Modal } from 'reactstrap';
|
2022-12-29 04:21:47 +00:00
|
|
|
import { navigate } from '@gatsbyjs/reach-router';
|
2024-09-20 06:29:37 +00:00
|
|
|
import { gettext, siteRoot, username } from '../../utils/constants';
|
2019-02-20 03:54:25 +00:00
|
|
|
import { seafileAPI } from '../../utils/seafile-api';
|
|
|
|
import { Utils } from '../../utils/utils';
|
|
|
|
import collabServer from '../../utils/collab-server';
|
2024-09-13 12:28:20 +00:00
|
|
|
import { Dirent, FileTag, RepoTag, RepoInfo } from '../../models';
|
2019-02-20 03:54:25 +00:00
|
|
|
import TreeNode from '../../components/tree-view/tree-node';
|
|
|
|
import treeHelper from '../../components/tree-view/tree-helper';
|
|
|
|
import toaster from '../../components/toast';
|
|
|
|
import ModalPortal from '../../components/modal-portal';
|
|
|
|
import LibDecryptDialog from '../../components/dialog/lib-decrypt-dialog';
|
2019-02-21 09:37:04 +00:00
|
|
|
import FileUploader from '../../components/file-uploader/file-uploader';
|
2020-02-29 09:02:11 +00:00
|
|
|
import CopyMoveDirentProgressDialog from '../../components/dialog/copy-move-dirent-progress-dialog';
|
2022-11-28 01:27:17 +00:00
|
|
|
import DeleteFolderDialog from '../../components/dialog/delete-folder-dialog';
|
2024-07-11 09:45:30 +00:00
|
|
|
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
|
2024-08-09 02:32:28 +00:00
|
|
|
import { PRIVATE_FILE_TYPE } from '../../constants';
|
2024-11-22 09:11:55 +00:00
|
|
|
import { MetadataStatusProvider } from '../../hooks';
|
2024-08-15 09:38:42 +00:00
|
|
|
import { MetadataProvider, CollaboratorsProvider } from '../../metadata/hooks';
|
2024-11-22 09:11:55 +00:00
|
|
|
import { TagsProvider } from '../../tag/hooks';
|
|
|
|
import { LIST_MODE, METADATA_MODE, DIRENT_DETAIL_MODE, TAGS_MODE } from '../../components/dir-view-mode/constants';
|
2024-09-13 12:28:20 +00:00
|
|
|
import CurDirPath from '../../components/cur-dir-path';
|
|
|
|
import DirTool from '../../components/cur-dir-path/dir-tool';
|
2024-10-29 09:52:44 +00:00
|
|
|
import Detail from '../../components/dirent-detail';
|
2024-09-13 12:28:20 +00:00
|
|
|
import DirColumnView from '../../components/dir-view-mode/dir-column-view';
|
|
|
|
import SelectedDirentsToolbar from '../../components/toolbar/selected-dirents-toolbar';
|
2024-10-18 08:01:00 +00:00
|
|
|
import { VIEW_TYPE } from '../../metadata/constants';
|
2024-09-13 12:28:20 +00:00
|
|
|
|
|
|
|
import '../../css/lib-content-view.css';
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2024-10-21 03:29:17 +00:00
|
|
|
dayjs.extend(relativeTime);
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
const propTypes = {
|
2024-07-11 09:45:30 +00:00
|
|
|
eventBus: PropTypes.object,
|
2024-07-09 09:00:12 +00:00
|
|
|
isSidePanelFolded: PropTypes.bool,
|
2019-02-20 03:54:25 +00:00
|
|
|
pathPrefix: PropTypes.array.isRequired,
|
|
|
|
onTabNavClick: PropTypes.func.isRequired,
|
2019-02-21 09:37:04 +00:00
|
|
|
repoID: PropTypes.string,
|
2019-02-20 03:54:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class LibContentView extends React.Component {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
2024-05-17 07:10:09 +00:00
|
|
|
|
|
|
|
let isTreePanelShown = true;
|
|
|
|
const storedTreePanelState = localStorage.getItem('sf_dir_view_tree_panel_open');
|
|
|
|
if (storedTreePanelState != undefined) {
|
|
|
|
isTreePanelShown = storedTreePanelState == 'true';
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
this.state = {
|
2024-09-10 03:46:50 +00:00
|
|
|
currentMode: cookie.load('seafile_view_mode') || LIST_MODE,
|
2024-05-17 07:10:09 +00:00
|
|
|
isTreePanelShown: isTreePanelShown, // display the 'dirent tree' side panel
|
2019-02-20 03:54:25 +00:00
|
|
|
path: '',
|
|
|
|
pathExist: true,
|
|
|
|
isViewFile: false,
|
|
|
|
currentRepoInfo: null,
|
|
|
|
repoName: '',
|
|
|
|
repoEncrypted: false,
|
|
|
|
libNeedDecrypt: false,
|
|
|
|
isGroupOwnedRepo: false,
|
|
|
|
userPerm: '',
|
|
|
|
selectedDirentList: [],
|
2024-07-23 15:08:53 +00:00
|
|
|
lastSelectedIndex: null,
|
2019-03-15 02:10:24 +00:00
|
|
|
fileTags: [],
|
2024-02-02 12:52:58 +00:00
|
|
|
repoTags: [],
|
2019-02-20 03:54:25 +00:00
|
|
|
usedRepoTags: [],
|
|
|
|
isTreeDataLoading: true,
|
|
|
|
treeData: treeHelper.buildTree(),
|
|
|
|
currentNode: null,
|
|
|
|
isFileLoading: true,
|
2019-04-19 07:50:27 +00:00
|
|
|
filePermission: '',
|
2019-02-20 03:54:25 +00:00
|
|
|
content: '',
|
|
|
|
lastModified: '',
|
|
|
|
latestContributor: '',
|
|
|
|
isDirentListLoading: true,
|
|
|
|
direntList: [],
|
|
|
|
isDirentSelected: false,
|
2019-05-29 05:57:12 +00:00
|
|
|
sortBy: cookie.load('seafile-repo-dir-sort-by') || 'name', // 'name' or 'time' or 'size'
|
2019-04-12 06:30:08 +00:00
|
|
|
sortOrder: cookie.load('seafile-repo-dir-sort-order') || 'asc', // 'asc' or 'desc'
|
2019-02-20 03:54:25 +00:00
|
|
|
isAllDirentSelected: false,
|
2024-07-18 03:58:42 +00:00
|
|
|
dirID: '', // for update dir list
|
2019-02-20 03:54:25 +00:00
|
|
|
errorMsg: '',
|
2019-03-18 09:32:49 +00:00
|
|
|
isDirentDetailShow: false,
|
2019-04-24 05:37:48 +00:00
|
|
|
itemsShowLength: 100,
|
2019-05-12 07:05:53 +00:00
|
|
|
isSessionExpired: false,
|
2020-02-29 09:02:11 +00:00
|
|
|
isCopyMoveProgressDialogShow: false,
|
2022-11-28 01:27:17 +00:00
|
|
|
isDeleteFolderDialogOpen: false,
|
2020-02-29 09:02:11 +00:00
|
|
|
asyncCopyMoveTaskId: '',
|
2020-03-31 05:45:36 +00:00
|
|
|
asyncOperationType: 'move',
|
2020-02-29 09:02:11 +00:00
|
|
|
asyncOperationProgress: 0,
|
2020-03-02 08:44:39 +00:00
|
|
|
asyncOperatedFilesLength: 0,
|
2024-08-12 09:15:56 +00:00
|
|
|
viewId: '0000',
|
2024-11-22 09:11:55 +00:00
|
|
|
tagId: '',
|
2024-09-14 07:29:26 +00:00
|
|
|
currentDirent: null,
|
2019-02-20 03:54:25 +00:00
|
|
|
};
|
|
|
|
|
2019-04-22 10:05:18 +00:00
|
|
|
this.oldonpopstate = window.onpopstate;
|
2019-02-20 03:54:25 +00:00
|
|
|
window.onpopstate = this.onpopstate;
|
|
|
|
this.lastModifyTime = new Date();
|
2019-02-23 03:59:26 +00:00
|
|
|
this.isNeedUpdateHistoryState = true; // Load, refresh page, switch mode for the first time, no need to set historyState
|
2020-04-10 10:08:27 +00:00
|
|
|
this.currentMoveItemName = '';
|
|
|
|
this.currentMoveItemPath = '';
|
2024-09-11 02:13:10 +00:00
|
|
|
this.unsubscribeEventBus = null;
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
|
2024-10-18 08:01:00 +00:00
|
|
|
updateCurrentDirent = (dirent = null) => {
|
|
|
|
this.setState({ currentDirent: dirent });
|
|
|
|
};
|
|
|
|
|
|
|
|
updateCurrentNotExistDirent = (deletedDirent) => {
|
2024-09-13 12:28:20 +00:00
|
|
|
let { currentDirent } = this.state;
|
|
|
|
if (currentDirent && deletedDirent.name === currentDirent.name) {
|
|
|
|
this.setState({ currentDirent: null });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
onItemsScroll = (e) => {
|
|
|
|
let target = e.target;
|
|
|
|
if (target.scrollTop === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (target.scrollTop + target.clientHeight + 1 >= target.scrollHeight) {
|
|
|
|
this.onListContainerScroll();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-10-18 08:01:00 +00:00
|
|
|
showDirentDetail = () => {
|
|
|
|
this.setState({ isDirentDetailShow: true });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-03-18 09:32:49 +00:00
|
|
|
|
2019-04-10 08:04:26 +00:00
|
|
|
toggleDirentDetail = () => {
|
2024-10-18 08:01:00 +00:00
|
|
|
this.setState({ isDirentDetailShow: !this.state.isDirentDetailShow });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-04-10 08:04:26 +00:00
|
|
|
|
2019-03-19 10:20:45 +00:00
|
|
|
closeDirentDetail = () => {
|
2024-10-18 08:01:00 +00:00
|
|
|
this.setState({ isDirentDetailShow: false });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-03-19 10:20:45 +00:00
|
|
|
|
2024-07-13 13:03:31 +00:00
|
|
|
componentDidMount() {
|
2024-09-11 02:13:10 +00:00
|
|
|
this.unsubscribeEvent = this.props.eventBus.subscribe(EVENT_BUS_TYPE.SEARCH_LIBRARY_CONTENT, this.onSearchedClick);
|
2024-07-13 13:03:31 +00:00
|
|
|
this.calculatePara(this.props);
|
|
|
|
}
|
|
|
|
|
|
|
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
|
|
if (nextProps.repoID !== this.props.repoID) {
|
|
|
|
this.calculatePara(nextProps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-18 03:58:42 +00:00
|
|
|
calculatePara = async (props) => {
|
2024-09-11 02:13:10 +00:00
|
|
|
const { repoID } = props;
|
2024-08-09 02:32:28 +00:00
|
|
|
|
2024-08-12 09:15:56 +00:00
|
|
|
const path = this.getPathFromLocation(repoID);
|
2021-09-13 02:37:07 +00:00
|
|
|
|
|
|
|
try {
|
2024-08-09 02:32:28 +00:00
|
|
|
const repoInfo = await this.fetchRepoInfo(repoID);
|
|
|
|
const isGroupOwnedRepo = repoInfo.owner_email.includes('@seafile_group');
|
2021-09-13 02:37:07 +00:00
|
|
|
|
2022-08-17 03:20:19 +00:00
|
|
|
this.setState({
|
2024-08-27 10:30:42 +00:00
|
|
|
treeData: treeHelper.buildTree(),
|
2022-08-17 03:20:19 +00:00
|
|
|
currentRepoInfo: repoInfo,
|
2019-02-20 03:54:25 +00:00
|
|
|
repoName: repoInfo.repo_name,
|
2020-11-02 05:56:35 +00:00
|
|
|
libNeedDecrypt: repoInfo.lib_need_decrypt,
|
2019-02-20 03:54:25 +00:00
|
|
|
repoEncrypted: repoInfo.encrypted,
|
2019-05-17 07:53:51 +00:00
|
|
|
isGroupOwnedRepo: isGroupOwnedRepo,
|
2021-09-13 02:37:07 +00:00
|
|
|
path: path
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
2022-08-17 03:20:19 +00:00
|
|
|
|
2024-08-27 10:30:42 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
|
|
|
this.loadSidePanel(path);
|
|
|
|
}
|
|
|
|
|
2024-08-09 02:32:28 +00:00
|
|
|
if (repoInfo.permission.startsWith('custom-')) {
|
|
|
|
await this.setCustomPermission(repoID, repoInfo.permission);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.isNeedUpdateHistoryState = false;
|
|
|
|
|
2019-02-27 05:53:36 +00:00
|
|
|
if (!repoInfo.lib_need_decrypt) {
|
2019-02-20 03:54:25 +00:00
|
|
|
this.loadDirData(path);
|
|
|
|
}
|
2021-09-13 02:37:07 +00:00
|
|
|
} catch (error) {
|
2024-08-09 02:32:28 +00:00
|
|
|
this.handleError(error);
|
|
|
|
}
|
|
|
|
};
|
2020-11-02 05:56:35 +00:00
|
|
|
|
2024-08-09 02:32:28 +00:00
|
|
|
getPathFromLocation = (repoID) => {
|
2024-08-27 06:53:29 +00:00
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
|
const viewID = urlParams.get('view');
|
|
|
|
if (viewID) return `/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}`;
|
|
|
|
|
2024-11-23 02:28:46 +00:00
|
|
|
const tagID = urlParams.get('tag');
|
|
|
|
if (tagID) return `/${PRIVATE_FILE_TYPE.TAGS_PROPERTIES}`;
|
|
|
|
|
2024-08-09 02:32:28 +00:00
|
|
|
let location = window.location.href.split('?')[0];
|
|
|
|
location = decodeURIComponent(location);
|
|
|
|
let path = location.slice(location.indexOf(repoID) + repoID.length + 1);
|
|
|
|
path = path.slice(path.indexOf('/'));
|
|
|
|
if (path.length > 1 && path.endsWith('/')) {
|
|
|
|
path = path.slice(0, -1);
|
|
|
|
}
|
|
|
|
return path;
|
|
|
|
};
|
|
|
|
|
|
|
|
fetchRepoInfo = async (repoID) => {
|
|
|
|
const repoRes = await seafileAPI.getRepoInfo(repoID);
|
|
|
|
return new RepoInfo(repoRes.data);
|
|
|
|
};
|
|
|
|
|
|
|
|
setCustomPermission = async (repoID, permission) => {
|
|
|
|
const permissionID = permission.split('-')[1];
|
|
|
|
const permissionRes = await seafileAPI.getCustomPermission(repoID, permissionID);
|
|
|
|
window.custom_permission = permissionRes.data.permission;
|
|
|
|
};
|
|
|
|
|
|
|
|
handleError = (error) => {
|
|
|
|
let errorMsg = gettext('Please check the network.');
|
|
|
|
if (error.response) {
|
|
|
|
switch (error.response.status) {
|
|
|
|
case 403:
|
|
|
|
errorMsg = gettext('Permission denied');
|
|
|
|
break;
|
|
|
|
case 404:
|
|
|
|
errorMsg = gettext('Library share permission not found.');
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
errorMsg = gettext('Error');
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
2021-09-13 02:37:07 +00:00
|
|
|
}
|
2024-08-09 02:32:28 +00:00
|
|
|
this.setState({
|
|
|
|
isDirentListLoading: false,
|
|
|
|
errorMsg: errorMsg
|
|
|
|
});
|
|
|
|
toaster.danger(errorMsg);
|
2024-07-13 13:03:31 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
componentWillUnmount() {
|
2019-04-22 10:05:18 +00:00
|
|
|
window.onpopstate = this.oldonpopstate;
|
2019-02-20 03:54:25 +00:00
|
|
|
collabServer.unwatchRepo(this.props.repoID, this.onRepoUpdateEvent);
|
2024-07-13 13:03:31 +00:00
|
|
|
this.unsubscribeEvent();
|
2024-09-11 02:13:10 +00:00
|
|
|
this.unsubscribeEventBus && this.unsubscribeEventBus();
|
2024-07-11 09:45:30 +00:00
|
|
|
this.props.eventBus.dispatch(EVENT_BUS_TYPE.CURRENT_LIBRARY_CHANGED, {
|
|
|
|
repoID: '',
|
|
|
|
repoName: '',
|
|
|
|
path: '',
|
|
|
|
isViewFile: false,
|
|
|
|
isLibView: false,
|
2024-07-11 12:37:57 +00:00
|
|
|
currentRepoInfo: null,
|
2024-07-11 09:45:30 +00:00
|
|
|
});
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
|
2024-09-13 12:28:20 +00:00
|
|
|
componentDidUpdate(prevProps, prevState) {
|
|
|
|
if (prevState.path !== this.state.path) {
|
|
|
|
this.setState({ currentDirent: null });
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
this.lastModifyTime = new Date();
|
2024-07-11 09:45:30 +00:00
|
|
|
this.props.eventBus.dispatch(EVENT_BUS_TYPE.CURRENT_LIBRARY_CHANGED, {
|
|
|
|
repoID: this.props.repoID,
|
|
|
|
repoName: this.state.repoName,
|
2024-07-11 12:37:57 +00:00
|
|
|
currentRepoInfo: this.state.currentRepoInfo,
|
2024-07-11 09:45:30 +00:00
|
|
|
path: this.state.path,
|
|
|
|
isViewFile: this.state.isViewFile,
|
|
|
|
isLibView: true,
|
|
|
|
});
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
onpopstate = (event) => {
|
2019-03-29 10:27:43 +00:00
|
|
|
if (event.state && event.state.key) { // root path
|
|
|
|
if (this.state.path === '/') {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
let path = '/';
|
|
|
|
this.loadDirentList(path);
|
|
|
|
this.setState({
|
|
|
|
path: path,
|
|
|
|
isViewFile: false
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else if (event.state && event.state.path) { // file path
|
2019-02-20 03:54:25 +00:00
|
|
|
let path = event.state.path;
|
|
|
|
this.loadDirentList(path);
|
|
|
|
this.setState({
|
|
|
|
path: path,
|
|
|
|
isViewFile: false
|
|
|
|
});
|
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onRepoUpdateEvent = () => {
|
|
|
|
let currentTime = new Date();
|
2024-07-11 04:09:57 +00:00
|
|
|
if ((parseFloat(currentTime - this.lastModifyTime) / 1000) <= 5) {
|
2019-02-20 03:54:25 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
let { path, dirID } = this.state;
|
2019-05-10 07:40:01 +00:00
|
|
|
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2019-05-10 07:40:01 +00:00
|
|
|
if (this.state.isViewFile) {
|
2022-04-11 09:27:02 +00:00
|
|
|
this.updateColumnMarkdownData(path);
|
2019-05-10 07:40:01 +00:00
|
|
|
} else {
|
|
|
|
seafileAPI.dirMetaData(repoID, path).then((res) => {
|
|
|
|
if (res.data.id !== dirID) {
|
|
|
|
this.loadDirentList(path);
|
|
|
|
}
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
2019-05-14 02:15:09 +00:00
|
|
|
});
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
2019-05-10 07:40:01 +00:00
|
|
|
} else {
|
|
|
|
seafileAPI.dirMetaData(repoID, path).then((res) => {
|
|
|
|
if (res.data.id !== dirID) {
|
|
|
|
this.loadDirentList(path);
|
|
|
|
}
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
2019-05-14 02:15:09 +00:00
|
|
|
});
|
2019-05-10 07:40:01 +00:00
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
updateUsedRepoTags = () => {
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
seafileAPI.listRepoTags(repoID).then(res => {
|
2024-02-02 12:52:58 +00:00
|
|
|
let repoTags = [];
|
2019-02-20 03:54:25 +00:00
|
|
|
let usedRepoTags = [];
|
|
|
|
res.data.repo_tags.forEach(item => {
|
2024-02-02 12:52:58 +00:00
|
|
|
const repoTag = new RepoTag(item);
|
|
|
|
repoTags.push(repoTag);
|
|
|
|
if (repoTag.fileCount > 0) {
|
|
|
|
usedRepoTags.push(repoTag);
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
});
|
2024-02-02 12:52:58 +00:00
|
|
|
this.setState({
|
|
|
|
repoTags: repoTags,
|
|
|
|
usedRepoTags: usedRepoTags
|
|
|
|
});
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2022-04-11 09:27:02 +00:00
|
|
|
updateColumnMarkdownData = (filePath) => {
|
2019-05-10 07:40:01 +00:00
|
|
|
let repoID = this.props.repoID;
|
|
|
|
// update state
|
|
|
|
this.setState({
|
|
|
|
path: filePath,
|
|
|
|
isViewFile: true
|
|
|
|
});
|
|
|
|
|
|
|
|
// update data
|
|
|
|
seafileAPI.getFileInfo(repoID, filePath).then((res) => {
|
2024-03-25 09:22:01 +00:00
|
|
|
let { mtime, permission, last_modifier_name } = res.data;
|
2019-05-10 07:40:01 +00:00
|
|
|
seafileAPI.getFileDownloadLink(repoID, filePath).then((res) => {
|
|
|
|
seafileAPI.getFileContent(res.data).then((res) => {
|
|
|
|
if (this.state.content !== res.data) {
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ isFileLoading: true });
|
2019-05-10 07:40:01 +00:00
|
|
|
}
|
|
|
|
this.setState({
|
|
|
|
content: res.data,
|
|
|
|
filePermission: permission,
|
|
|
|
latestContributor: last_modifier_name,
|
2024-10-21 03:29:17 +00:00
|
|
|
lastModified: dayjs.unix(mtime).fromNow(),
|
2019-05-10 07:40:01 +00:00
|
|
|
isFileLoading: false,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}).catch(() => {
|
|
|
|
this.setState({
|
|
|
|
isFileLoading: false,
|
|
|
|
});
|
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-05-10 07:40:01 +00:00
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
// load data
|
|
|
|
loadDirData = (path) => {
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
|
|
|
|
// listen current repo
|
|
|
|
collabServer.watchRepo(repoID, this.onRepoUpdateEvent);
|
|
|
|
|
|
|
|
// list used FileTags
|
|
|
|
this.updateUsedRepoTags();
|
|
|
|
|
2024-08-09 02:32:28 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
|
|
|
this.loadSidePanel(path);
|
|
|
|
}
|
|
|
|
|
2024-11-23 02:28:46 +00:00
|
|
|
if (!(path.includes(PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES) || path.includes(PRIVATE_FILE_TYPE.TAGS_PROPERTIES))) {
|
2024-08-09 02:32:28 +00:00
|
|
|
this.showDir(path);
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
loadSidePanel = (path) => {
|
|
|
|
let repoID = this.props.repoID;
|
2024-11-23 02:28:46 +00:00
|
|
|
if (path === '/' || path.includes(PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES) || path.includes(PRIVATE_FILE_TYPE.TAGS_PROPERTIES)) {
|
2019-02-20 03:54:25 +00:00
|
|
|
seafileAPI.listDir(repoID, '/').then(res => {
|
2022-04-11 09:52:07 +00:00
|
|
|
const { dirent_list, user_perm } = res.data;
|
2019-02-20 03:54:25 +00:00
|
|
|
let tree = this.state.treeData;
|
2022-04-11 09:52:07 +00:00
|
|
|
this.addResponseListToNode(dirent_list, tree.root);
|
2019-02-20 03:54:25 +00:00
|
|
|
this.setState({
|
|
|
|
isTreeDataLoading: false,
|
2022-04-11 09:52:07 +00:00
|
|
|
treeData: tree,
|
|
|
|
userPerm: user_perm,
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
}).catch(() => {
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ isTreeDataLoading: false });
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.loadNodeAndParentsByPath(path);
|
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
showDir = (path) => {
|
|
|
|
let repoID = this.props.repoID;
|
2024-10-14 02:18:40 +00:00
|
|
|
this.props.resetTitle();
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2019-05-12 07:05:53 +00:00
|
|
|
if (!this.state.isSessionExpired) {
|
2024-08-06 09:30:11 +00:00
|
|
|
// update state
|
2019-05-12 07:05:53 +00:00
|
|
|
this.setState({
|
|
|
|
isDirentListLoading: true,
|
|
|
|
isViewFile: false,
|
|
|
|
selectedDirentList: [],
|
|
|
|
});
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
// update data
|
2019-02-23 03:59:26 +00:00
|
|
|
this.loadDirentList(path);
|
2019-05-05 09:20:37 +00:00
|
|
|
this.resetShowLength();
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2019-02-23 03:59:26 +00:00
|
|
|
if (!this.isNeedUpdateHistoryState) {
|
|
|
|
this.isNeedUpdateHistoryState = true;
|
|
|
|
return;
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
// update location
|
|
|
|
let repoInfo = this.state.currentRepoInfo;
|
|
|
|
let url = siteRoot + 'library/' + repoID + '/' + encodeURIComponent(repoInfo.repo_name) + Utils.encodePath(path);
|
2024-07-18 03:58:42 +00:00
|
|
|
window.history.pushState({ url: url, path: path }, path, url);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2024-09-07 08:29:45 +00:00
|
|
|
showFile = (filePath, noRedirection) => {
|
2019-02-20 03:54:25 +00:00
|
|
|
let repoID = this.props.repoID;
|
|
|
|
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2019-03-15 02:10:24 +00:00
|
|
|
seafileAPI.listFileTags(repoID, filePath).then(res => {
|
|
|
|
let fileTags = res.data.file_tags.map(item => {
|
|
|
|
return new FileTag(item);
|
|
|
|
});
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ fileTags: fileTags });
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
2019-03-15 02:10:24 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
// update state
|
|
|
|
this.setState({
|
|
|
|
isFileLoading: true,
|
2024-09-09 03:55:41 +00:00
|
|
|
path: noRedirection ? this.state.path : filePath,
|
2019-02-20 03:54:25 +00:00
|
|
|
isViewFile: true
|
|
|
|
});
|
|
|
|
|
|
|
|
// update data
|
|
|
|
seafileAPI.getFileInfo(repoID, filePath).then((res) => {
|
2024-03-25 09:22:01 +00:00
|
|
|
let { mtime, permission, last_modifier_name } = res.data;
|
2019-02-20 03:54:25 +00:00
|
|
|
seafileAPI.getFileDownloadLink(repoID, filePath).then((res) => {
|
|
|
|
seafileAPI.getFileContent(res.data).then((res) => {
|
|
|
|
this.setState({
|
|
|
|
content: res.data,
|
2019-04-19 07:50:27 +00:00
|
|
|
filePermission: permission,
|
2019-02-20 03:54:25 +00:00
|
|
|
latestContributor: last_modifier_name,
|
2024-10-21 03:29:17 +00:00
|
|
|
lastModified: dayjs.unix(mtime).fromNow(),
|
2019-02-20 03:54:25 +00:00
|
|
|
isFileLoading: false,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2019-05-12 07:05:53 +00:00
|
|
|
}).catch((err) => {
|
2019-12-19 05:44:30 +00:00
|
|
|
let errMsg = Utils.getErrorMsg(err, true);
|
|
|
|
if (!err.response || err.response.status !== 403) {
|
|
|
|
toaster.danger(errMsg);
|
2019-05-12 07:05:53 +00:00
|
|
|
}
|
2019-02-21 09:37:04 +00:00
|
|
|
this.setState({
|
|
|
|
isFileLoading: false,
|
|
|
|
});
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
|
2024-09-07 08:29:45 +00:00
|
|
|
if (noRedirection) return;
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
// update location
|
|
|
|
let repoInfo = this.state.currentRepoInfo;
|
|
|
|
let url = siteRoot + 'library/' + repoID + '/' + encodeURIComponent(repoInfo.repo_name) + Utils.encodePath(filePath);
|
2024-07-18 03:58:42 +00:00
|
|
|
window.history.pushState({ url: url, path: filePath }, filePath, url);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2024-10-18 08:01:00 +00:00
|
|
|
showFileMetadata = (filePath, viewId, viewType) => {
|
2024-06-29 09:58:27 +00:00
|
|
|
const repoID = this.props.repoID;
|
|
|
|
const repoInfo = this.state.currentRepoInfo;
|
2024-08-09 02:32:28 +00:00
|
|
|
this.setState({
|
2024-09-10 03:46:50 +00:00
|
|
|
currentMode: METADATA_MODE,
|
2024-08-09 02:32:28 +00:00
|
|
|
path: filePath,
|
2024-08-12 09:15:56 +00:00
|
|
|
viewId: viewId,
|
2024-10-18 08:01:00 +00:00
|
|
|
isDirentDetailShow: viewType === VIEW_TYPE.GALLERY ? this.state.isDirentDetailShow : false,
|
2024-08-09 02:32:28 +00:00
|
|
|
});
|
2024-08-27 06:53:29 +00:00
|
|
|
const url = `${siteRoot}library/${repoID}/${encodeURIComponent(repoInfo.repo_name)}/?view=${encodeURIComponent(viewId)}`;
|
2024-07-18 03:58:42 +00:00
|
|
|
window.history.pushState({ url: url, path: '' }, '', url);
|
2024-06-29 09:58:27 +00:00
|
|
|
};
|
|
|
|
|
2024-11-09 02:36:08 +00:00
|
|
|
hideMetadataView = () => {
|
2024-08-12 09:15:56 +00:00
|
|
|
this.setState({
|
2024-09-10 03:46:50 +00:00
|
|
|
currentMode: LIST_MODE,
|
2024-08-12 09:15:56 +00:00
|
|
|
path: '',
|
|
|
|
viewId: '',
|
|
|
|
isDirentDetailShow: false
|
2024-09-08 04:16:33 +00:00
|
|
|
}, () => {
|
|
|
|
this.showDir('/');
|
2024-08-12 09:15:56 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2024-11-22 09:11:55 +00:00
|
|
|
showTagsView = (filePath, tagId) => {
|
|
|
|
const repoID = this.props.repoID;
|
|
|
|
const repoInfo = this.state.currentRepoInfo;
|
|
|
|
this.setState({
|
|
|
|
currentMode: TAGS_MODE,
|
|
|
|
path: filePath,
|
|
|
|
tagId: tagId,
|
|
|
|
isDirentDetailShow: false
|
|
|
|
});
|
|
|
|
const url = `${siteRoot}library/${repoID}/${encodeURIComponent(repoInfo.repo_name)}/?tag=${encodeURIComponent(tagId)}`;
|
|
|
|
window.history.pushState({ url: url, path: '' }, '', url);
|
|
|
|
};
|
|
|
|
|
2019-02-23 03:59:26 +00:00
|
|
|
loadDirentList = (path) => {
|
2024-10-15 03:59:54 +00:00
|
|
|
const { repoID } = this.props;
|
|
|
|
const { sortBy, sortOrder } = this.state;
|
2024-09-12 07:00:42 +00:00
|
|
|
this.setState({
|
|
|
|
isDirentListLoading: true,
|
|
|
|
direntList: [],
|
|
|
|
});
|
2024-07-18 03:58:42 +00:00
|
|
|
seafileAPI.listDir(repoID, path, { 'with_thumbnail': true }).then(res => {
|
2024-10-15 03:59:54 +00:00
|
|
|
const { dirent_list, user_perm: userPerm, dir_id: dirID } = res.data;
|
|
|
|
const direntList = Utils.sortDirents(dirent_list.map(item => new Dirent(item)), sortBy, sortOrder);
|
2019-02-20 03:54:25 +00:00
|
|
|
this.setState({
|
|
|
|
pathExist: true,
|
2024-10-15 03:59:54 +00:00
|
|
|
userPerm,
|
2019-02-20 03:54:25 +00:00
|
|
|
isDirentListLoading: false,
|
2024-10-15 03:59:54 +00:00
|
|
|
direntList,
|
|
|
|
dirID,
|
|
|
|
path,
|
2019-05-12 07:05:53 +00:00
|
|
|
isSessionExpired: false,
|
2024-10-15 03:59:54 +00:00
|
|
|
currentDirent: null,
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
|
2023-11-15 09:01:50 +00:00
|
|
|
if (this.state.currentRepoInfo.is_admin) {
|
|
|
|
if (this.foldersSharedOut) {
|
|
|
|
this.identifyFoldersSharedOut();
|
|
|
|
} else {
|
|
|
|
this.foldersSharedOut = [];
|
|
|
|
seafileAPI.getAllRepoFolderShareInfo(repoID).then(res => {
|
|
|
|
res.data.share_info_list.forEach(item => {
|
|
|
|
if (this.foldersSharedOut.indexOf(item.path) === -1) {
|
|
|
|
this.foldersSharedOut.push(item.path);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this.identifyFoldersSharedOut();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2019-05-12 07:05:53 +00:00
|
|
|
}).catch((err) => {
|
2019-12-19 05:44:30 +00:00
|
|
|
Utils.getErrorMsg(err, true);
|
|
|
|
if (err.response && err.response.status === 403) {
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ isDirentListLoading: false });
|
2019-05-12 07:05:53 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
this.setState({
|
|
|
|
isDirentListLoading: false,
|
|
|
|
pathExist: false,
|
|
|
|
});
|
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2023-11-15 09:01:50 +00:00
|
|
|
identifyFoldersSharedOut = () => {
|
|
|
|
const { path, direntList } = this.state;
|
|
|
|
if (this.foldersSharedOut.length == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
direntList.forEach(dirent => {
|
|
|
|
if (dirent.type == 'dir' && this.foldersSharedOut.indexOf(Utils.joinPath(path, dirent.name) + '/') !== -1) {
|
|
|
|
dirent.has_been_shared_out = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this.setState({
|
|
|
|
direntList: direntList
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2019-04-24 07:16:26 +00:00
|
|
|
onListContainerScroll = () => {
|
2024-10-15 03:59:54 +00:00
|
|
|
this.setState({ itemsShowLength: this.state.itemsShowLength + 100 });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-04-23 06:43:47 +00:00
|
|
|
|
2019-04-24 07:16:26 +00:00
|
|
|
resetShowLength = () => {
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ itemsShowLength: 100 });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-04-23 06:43:47 +00:00
|
|
|
|
2019-06-12 12:50:12 +00:00
|
|
|
updateMoveCopyTreeNode = (path) => {
|
2019-06-12 09:50:55 +00:00
|
|
|
let repoID = this.props.repoID;
|
|
|
|
|
2019-06-12 12:50:12 +00:00
|
|
|
let tree = this.state.treeData.clone();
|
|
|
|
let node = tree.getNodeByPath(path);
|
|
|
|
|
2023-09-07 07:39:48 +00:00
|
|
|
// for node not loaded, such as a deep folder '/vv/aa'
|
|
|
|
if (!node) { // node: null
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-06-12 13:43:33 +00:00
|
|
|
let nodeChildren = node.children.map(item => item.object);
|
|
|
|
let nodeChildrenNames = nodeChildren.map(item => item.name);
|
2019-06-12 12:50:12 +00:00
|
|
|
|
2019-06-12 09:50:55 +00:00
|
|
|
seafileAPI.listDir(repoID, path).then(res => {
|
2019-06-12 13:43:33 +00:00
|
|
|
let newDirentList = res.data.dirent_list;
|
|
|
|
let newAddedDirents = newDirentList.filter(item => {
|
2019-06-21 05:59:17 +00:00
|
|
|
return !nodeChildrenNames.includes(item.name);
|
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
newAddedDirents.forEach(item => {
|
2019-06-12 09:50:55 +00:00
|
|
|
this.addNodeToTree(item.name, path, item.type);
|
2019-06-21 05:59:17 +00:00
|
|
|
});
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
2019-06-12 09:50:55 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-06-12 12:50:12 +00:00
|
|
|
|
2020-02-29 09:02:11 +00:00
|
|
|
async getAsyncCopyMoveProgress() {
|
|
|
|
let { asyncOperationType, asyncCopyMoveTaskId } = this.state;
|
|
|
|
try {
|
|
|
|
let res = await seafileAPI.queryAsyncOperationProgress(asyncCopyMoveTaskId);
|
|
|
|
let data = res.data;
|
|
|
|
if (data.failed) {
|
2020-03-02 08:44:39 +00:00
|
|
|
let message = gettext('Failed to move files to another library.');
|
2020-02-29 09:02:11 +00:00
|
|
|
if (asyncOperationType === 'copy') {
|
2020-03-02 08:44:39 +00:00
|
|
|
message = gettext('Failed to copy files to another library.');
|
2020-02-29 09:02:11 +00:00
|
|
|
}
|
|
|
|
toaster.danger(message);
|
|
|
|
this.setState({
|
|
|
|
asyncOperationProgress: 0,
|
|
|
|
isCopyMoveProgressDialogShow: false,
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2020-11-02 05:56:35 +00:00
|
|
|
|
2020-02-29 09:02:11 +00:00
|
|
|
if (data.successful) {
|
2020-04-02 07:45:33 +00:00
|
|
|
if (asyncOperationType === 'move') {
|
2020-04-10 10:08:27 +00:00
|
|
|
if (this.currentMoveItemName && this.currentMoveItemPath) {
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2020-04-10 10:08:27 +00:00
|
|
|
this.deleteTreeNode(this.currentMoveItemPath);
|
|
|
|
}
|
|
|
|
this.moveDirent(this.currentMoveItemName);
|
2020-04-11 09:54:52 +00:00
|
|
|
this.currentMoveItemName = '';
|
|
|
|
this.currentMoveItemPath = '';
|
2020-04-10 10:08:27 +00:00
|
|
|
} else {
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2020-04-10 10:08:27 +00:00
|
|
|
let direntPaths = this.getSelectedDirentPaths();
|
|
|
|
this.deleteTreeNodes(direntPaths);
|
|
|
|
}
|
|
|
|
let direntNames = this.getSelectedDirentNames();
|
|
|
|
this.moveDirents(direntNames);
|
2020-04-02 07:45:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ isCopyMoveProgressDialogShow: false });
|
2020-03-02 08:44:39 +00:00
|
|
|
let message = gettext('Successfully moved files to another library.');
|
2020-02-29 09:02:11 +00:00
|
|
|
if (asyncOperationType === 'copy') {
|
2020-03-02 09:47:34 +00:00
|
|
|
message = gettext('Successfully copied files to another library.');
|
2020-02-29 09:02:11 +00:00
|
|
|
}
|
|
|
|
toaster.success(message);
|
|
|
|
return;
|
|
|
|
}
|
2020-04-02 07:45:33 +00:00
|
|
|
// init state: total is 0
|
2024-07-18 03:58:42 +00:00
|
|
|
let asyncOperationProgress = !data.total ? 0 : parseInt((data.done / data.total * 100).toFixed(2));
|
2020-02-29 09:02:11 +00:00
|
|
|
|
|
|
|
this.getAsyncCopyMoveProgress();
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ asyncOperationProgress: asyncOperationProgress });
|
2020-02-29 09:02:11 +00:00
|
|
|
} catch (error) {
|
|
|
|
this.setState({
|
|
|
|
asyncOperationProgress: 0,
|
|
|
|
isCopyMoveProgressDialogShow: false,
|
2020-11-02 05:56:35 +00:00
|
|
|
});
|
2020-02-29 09:02:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cancelCopyMoveDirent = () => {
|
2020-04-11 10:03:45 +00:00
|
|
|
let taskId = this.state.asyncCopyMoveTaskId;
|
2020-02-29 09:02:11 +00:00
|
|
|
seafileAPI.cancelCopyMoveOperation(taskId);
|
2020-04-11 10:09:39 +00:00
|
|
|
|
|
|
|
this.currentMoveItemName = '';
|
|
|
|
this.currentMoveItemPath = '';
|
2020-04-11 10:03:45 +00:00
|
|
|
let direntList = this.state.direntList;
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ direntList: direntList.slice(0) });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2020-02-29 09:02:11 +00:00
|
|
|
|
|
|
|
onMoveProgressDialogToggle = () => {
|
|
|
|
let { asyncOperationProgress } = this.state;
|
2020-04-02 07:45:33 +00:00
|
|
|
if (asyncOperationProgress !== 100) {
|
2020-02-29 09:02:11 +00:00
|
|
|
this.cancelCopyMoveDirent();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
asyncOperationProgress: 0,
|
|
|
|
isCopyMoveProgressDialogShow: false,
|
2020-11-02 05:56:35 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2020-02-29 09:02:11 +00:00
|
|
|
|
2024-08-06 04:02:47 +00:00
|
|
|
updateRecentlyUsedRepos = (repo, destPath) => {
|
2024-08-05 08:08:05 +00:00
|
|
|
const recentlyUsed = JSON.parse(localStorage.getItem('recently-used-list')) || [];
|
2024-11-09 08:48:46 +00:00
|
|
|
const updatedRecentlyUsed = [{ repo_id: repo.repo_id, path: destPath }, ...recentlyUsed.filter(item => item.path !== destPath)];
|
2024-08-05 02:48:16 +00:00
|
|
|
|
|
|
|
const seen = new Set();
|
2024-08-06 04:02:47 +00:00
|
|
|
const filteredRecentlyUsed = updatedRecentlyUsed.filter(item => {
|
|
|
|
if (seen.has(item.path)) {
|
2024-08-05 02:48:16 +00:00
|
|
|
return false;
|
|
|
|
} else {
|
2024-08-06 04:02:47 +00:00
|
|
|
seen.add(item.path);
|
2024-08-05 02:48:16 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (filteredRecentlyUsed.length > 10) {
|
|
|
|
updatedRecentlyUsed.pop(); // Limit to 10 recent directories
|
|
|
|
}
|
|
|
|
|
2024-08-05 08:08:05 +00:00
|
|
|
localStorage.setItem('recently-used-list', JSON.stringify(filteredRecentlyUsed));
|
2024-08-05 02:48:16 +00:00
|
|
|
};
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
// toolbar operations
|
2024-11-18 13:21:33 +00:00
|
|
|
onMoveItems = (destRepo, destDirentPath, byDialog = false) => {
|
2019-02-20 03:54:25 +00:00
|
|
|
let repoID = this.props.repoID;
|
2020-03-31 05:45:36 +00:00
|
|
|
let selectedDirentList = this.state.selectedDirentList;
|
2020-11-02 05:56:35 +00:00
|
|
|
|
2020-03-31 05:45:36 +00:00
|
|
|
let dirNames = this.getSelectedDirentNames();
|
|
|
|
let direntPaths = this.getSelectedDirentPaths();
|
2024-08-05 02:48:16 +00:00
|
|
|
|
2019-06-12 06:59:33 +00:00
|
|
|
seafileAPI.moveDir(repoID, destRepo.repo_id, destDirentPath, this.state.path, dirNames).then(res => {
|
2019-07-23 01:42:09 +00:00
|
|
|
if (repoID !== destRepo.repo_id) {
|
2020-02-29 09:02:11 +00:00
|
|
|
this.setState({
|
|
|
|
asyncCopyMoveTaskId: res.data.task_id,
|
2024-02-07 03:08:41 +00:00
|
|
|
asyncOperatedFilesLength: selectedDirentList.length,
|
|
|
|
asyncOperationProgress: 0,
|
|
|
|
asyncOperationType: 'move',
|
|
|
|
isCopyMoveProgressDialogShow: true
|
2020-02-29 09:02:11 +00:00
|
|
|
}, () => {
|
2020-04-02 07:45:33 +00:00
|
|
|
// After moving successfully, delete related files
|
2020-02-29 09:02:11 +00:00
|
|
|
this.getAsyncCopyMoveProgress();
|
2019-07-23 01:42:09 +00:00
|
|
|
});
|
2020-02-29 09:02:11 +00:00
|
|
|
}
|
|
|
|
|
2020-04-02 07:45:33 +00:00
|
|
|
if (repoID === destRepo.repo_id) {
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2020-04-02 07:45:33 +00:00
|
|
|
this.deleteTreeNodes(direntPaths);
|
2019-06-12 06:59:33 +00:00
|
|
|
}
|
2020-11-02 05:56:35 +00:00
|
|
|
|
2020-04-02 07:45:33 +00:00
|
|
|
this.moveDirents(dirNames);
|
2020-02-29 09:02:11 +00:00
|
|
|
|
2020-04-02 07:45:33 +00:00
|
|
|
// 2. tow columns mode need update left tree
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2020-04-02 07:45:33 +00:00
|
|
|
this.updateMoveCopyTreeNode(destDirentPath);
|
|
|
|
}
|
2020-02-29 09:02:11 +00:00
|
|
|
|
2020-04-02 07:45:33 +00:00
|
|
|
// show tip message if move to current repo
|
2024-07-18 03:58:42 +00:00
|
|
|
let message = Utils.getMoveSuccessMessage(dirNames);
|
2019-07-23 01:42:09 +00:00
|
|
|
toaster.success(message);
|
2019-06-12 06:59:33 +00:00
|
|
|
}
|
2020-04-02 07:45:33 +00:00
|
|
|
|
2024-11-18 13:21:33 +00:00
|
|
|
if (byDialog) {
|
2024-11-09 08:48:46 +00:00
|
|
|
this.updateRecentlyUsedRepos(destRepo, destDirentPath);
|
|
|
|
}
|
2024-08-05 02:48:16 +00:00
|
|
|
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch((error) => {
|
2024-02-07 03:08:41 +00:00
|
|
|
if (!error.response.data.lib_need_decrypt) {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
if (errMessage === gettext('Error')) {
|
|
|
|
errMessage = Utils.getCopyFailedMessage(dirNames);
|
|
|
|
}
|
|
|
|
toaster.danger(errMessage);
|
|
|
|
} else {
|
|
|
|
this.setState({
|
|
|
|
libNeedDecryptWhenMove: true,
|
|
|
|
destRepoWhenCopyMove: destRepo,
|
|
|
|
destDirentPathWhenCopyMove: destDirentPath,
|
|
|
|
});
|
2019-07-16 02:01:09 +00:00
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2024-11-18 13:21:33 +00:00
|
|
|
onCopyItems = (destRepo, destDirentPath, byDialog = false) => {
|
2019-02-20 03:54:25 +00:00
|
|
|
let repoID = this.props.repoID;
|
2020-03-31 05:45:36 +00:00
|
|
|
let selectedDirentList = this.state.selectedDirentList;
|
2020-11-02 05:56:35 +00:00
|
|
|
|
2020-03-31 05:45:36 +00:00
|
|
|
let dirNames = this.getSelectedDirentNames();
|
2019-06-12 06:59:33 +00:00
|
|
|
seafileAPI.copyDir(repoID, destRepo.repo_id, destDirentPath, this.state.path, dirNames).then(res => {
|
2019-07-23 01:42:09 +00:00
|
|
|
if (repoID !== destRepo.repo_id) {
|
2020-02-29 09:02:11 +00:00
|
|
|
this.setState({
|
|
|
|
asyncCopyMoveTaskId: res.data.task_id,
|
2024-02-07 03:08:41 +00:00
|
|
|
asyncOperatedFilesLength: selectedDirentList.length,
|
|
|
|
asyncOperationProgress: 0,
|
|
|
|
asyncOperationType: 'copy',
|
|
|
|
isCopyMoveProgressDialogShow: true
|
2020-02-29 09:02:11 +00:00
|
|
|
}, () => {
|
|
|
|
this.getAsyncCopyMoveProgress();
|
2019-07-23 01:42:09 +00:00
|
|
|
});
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
2020-02-29 09:02:11 +00:00
|
|
|
|
2020-04-02 07:45:33 +00:00
|
|
|
if (repoID === destRepo.repo_id) {
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2020-04-02 07:45:33 +00:00
|
|
|
this.updateMoveCopyTreeNode(destDirentPath);
|
|
|
|
}
|
2020-02-29 09:02:11 +00:00
|
|
|
|
2020-04-02 07:45:33 +00:00
|
|
|
if (destDirentPath === this.state.path) {
|
|
|
|
this.loadDirentList(this.state.path);
|
|
|
|
}
|
|
|
|
|
|
|
|
// show tip message if copy to current repo
|
2024-07-18 03:58:42 +00:00
|
|
|
let message = Utils.getCopySuccessfulMessage(dirNames);
|
2020-04-02 07:45:33 +00:00
|
|
|
toaster.success(message);
|
2024-11-18 13:21:33 +00:00
|
|
|
|
|
|
|
if (byDialog) {
|
|
|
|
this.updateRecentlyUsedRepos(destRepo, destDirentPath);
|
|
|
|
}
|
2020-02-29 09:02:11 +00:00
|
|
|
}
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch((error) => {
|
2024-02-07 03:08:41 +00:00
|
|
|
if (!error.response.data.lib_need_decrypt) {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
if (errMessage === gettext('Error')) {
|
|
|
|
errMessage = Utils.getCopyFailedMessage(dirNames);
|
|
|
|
}
|
|
|
|
toaster.danger(errMessage);
|
|
|
|
} else {
|
|
|
|
this.setState({
|
|
|
|
libNeedDecryptWhenCopy: true,
|
|
|
|
destRepoWhenCopyMove: destRepo,
|
|
|
|
destDirentPathWhenCopyMove: destDirentPath,
|
|
|
|
});
|
2019-07-16 02:01:09 +00:00
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2023-04-22 00:03:58 +00:00
|
|
|
restoreDeletedDirents = (commitID, paths, e) => {
|
|
|
|
const { repoID } = this.props;
|
|
|
|
e.preventDefault();
|
|
|
|
toaster.closeAll();
|
|
|
|
seafileAPI.restoreDirents(repoID, commitID, paths).then(res => {
|
|
|
|
const { success, failed } = res.data;
|
|
|
|
success.forEach(dirent => {
|
|
|
|
let name = Utils.getFileName(dirent.path);
|
|
|
|
let parentPath = Utils.getDirName(dirent.path);
|
|
|
|
if (!dirent.is_dir) {
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2023-04-22 00:03:58 +00:00
|
|
|
this.addNodeToTree(name, parentPath, 'file');
|
|
|
|
}
|
|
|
|
if (parentPath === this.state.path && !this.state.isViewFile) {
|
|
|
|
this.addDirent(name, 'file');
|
2024-09-03 02:31:42 +00:00
|
|
|
if (Utils.imageCheck(name)) {
|
|
|
|
this.props.eventBus.dispatch(EVENT_BUS_TYPE.RESTORE_IMAGE);
|
|
|
|
}
|
2023-04-22 00:03:58 +00:00
|
|
|
}
|
|
|
|
} else {
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2023-04-22 00:03:58 +00:00
|
|
|
this.addNodeToTree(name, parentPath, 'dir');
|
|
|
|
}
|
|
|
|
if (parentPath === this.state.path && !this.state.isViewFile) {
|
|
|
|
this.addDirent(name, 'dir');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (success.length) {
|
|
|
|
let msg = success.length > 1 ? gettext('Restored {name} and {n} other items') :
|
|
|
|
gettext('Restored {name}');
|
|
|
|
msg = msg.replace('{name}', success[0].path.split('/').pop())
|
|
|
|
.replace('{n}', success.length - 1);
|
|
|
|
toaster.success(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (failed.length) {
|
|
|
|
let msg = failed.length > 1 ? gettext('Failed to restore {name} and {n} other items') :
|
|
|
|
gettext('Failed to restore {name}');
|
|
|
|
msg = msg.replace('{name}', failed[0].path.split('/').pop())
|
|
|
|
.replace('{n}', failed.length - 1);
|
|
|
|
toaster.danger(msg);
|
|
|
|
}
|
|
|
|
}).catch((error) => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2023-04-22 00:03:58 +00:00
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
onDeleteItems = () => {
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
let direntPaths = this.getSelectedDirentPaths();
|
|
|
|
let dirNames = this.getSelectedDirentNames();
|
|
|
|
|
2024-09-13 12:28:20 +00:00
|
|
|
this.setState({ currentDirent: null });
|
2019-02-20 03:54:25 +00:00
|
|
|
seafileAPI.deleteMutipleDirents(repoID, this.state.path, dirNames).then(res => {
|
2024-09-29 09:52:25 +00:00
|
|
|
this.deleteItemsAjaxCallback(direntPaths, dirNames);
|
2024-09-23 01:48:43 +00:00
|
|
|
|
2019-06-05 13:10:42 +00:00
|
|
|
let msg = '';
|
|
|
|
if (direntPaths.length > 1) {
|
2023-04-22 00:03:58 +00:00
|
|
|
msg = gettext('Successfully deleted {name} and {n} other items.');
|
2019-06-05 13:10:42 +00:00
|
|
|
msg = msg.replace('{name}', dirNames[0]);
|
|
|
|
msg = msg.replace('{n}', dirNames.length - 1);
|
|
|
|
} else {
|
|
|
|
msg = gettext('Successfully deleted {name}.');
|
|
|
|
msg = msg.replace('{name}', dirNames[0]);
|
|
|
|
}
|
2023-04-22 00:03:58 +00:00
|
|
|
const successTipWithUndo = (
|
|
|
|
<>
|
|
|
|
<span>{msg}</span>
|
|
|
|
<a className="action-link p-0 ml-1" href="#" onClick={this.restoreDeletedDirents.bind(this, res.data.commit_id, direntPaths)}>{gettext('Undo')}</a>
|
|
|
|
</>
|
|
|
|
);
|
2024-07-18 03:58:42 +00:00
|
|
|
toaster.success(successTipWithUndo, { duration: 5 });
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch((error) => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
if (errMessage === gettext('Error')) {
|
2023-04-22 00:03:58 +00:00
|
|
|
errMessage = gettext('Failed to delete {name} and {n} other items.');
|
2019-07-16 02:01:09 +00:00
|
|
|
errMessage = errMessage.replace('{name}', dirNames[0]);
|
|
|
|
errMessage = errMessage.replace('{n}', dirNames.length - 1);
|
|
|
|
}
|
|
|
|
toaster.danger(errMessage);
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2024-09-23 01:48:43 +00:00
|
|
|
removeFromRecentlyUsed = (repoID, path) => {
|
|
|
|
const recentlyUsed = JSON.parse(localStorage.getItem('recently-used-list')) || [];
|
|
|
|
const updatedRecentlyUsed = recentlyUsed.filter(item =>
|
2024-11-11 07:26:56 +00:00
|
|
|
!(item.repo_id === repoID && item.path === path)
|
2024-09-23 01:48:43 +00:00
|
|
|
);
|
|
|
|
localStorage.setItem('recently-used-list', JSON.stringify(updatedRecentlyUsed));
|
|
|
|
};
|
|
|
|
|
2024-11-08 10:04:48 +00:00
|
|
|
onAddFolder = (dirPath, options = {}) => {
|
|
|
|
const { successCallback = () => {} } = options;
|
2019-02-20 03:54:25 +00:00
|
|
|
let repoID = this.props.repoID;
|
|
|
|
seafileAPI.createDir(repoID, dirPath).then(() => {
|
|
|
|
let name = Utils.getFileName(dirPath);
|
|
|
|
let parentPath = Utils.getDirName(dirPath);
|
|
|
|
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2019-02-20 03:54:25 +00:00
|
|
|
this.addNodeToTree(name, parentPath, 'dir');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parentPath === this.state.path && !this.state.isViewFile) {
|
|
|
|
this.addDirent(name, 'dir');
|
|
|
|
}
|
2024-11-08 10:04:48 +00:00
|
|
|
|
|
|
|
successCallback();
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch((error) => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2024-03-25 09:22:01 +00:00
|
|
|
onAddFile = (filePath) => {
|
2019-02-20 03:54:25 +00:00
|
|
|
let repoID = this.props.repoID;
|
2024-03-25 09:22:01 +00:00
|
|
|
seafileAPI.createFile(repoID, filePath).then(res => {
|
2019-02-20 03:54:25 +00:00
|
|
|
let name = Utils.getFileName(filePath);
|
|
|
|
let parentPath = Utils.getDirName(filePath);
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2019-02-20 03:54:25 +00:00
|
|
|
this.addNodeToTree(name, parentPath, 'file');
|
|
|
|
}
|
|
|
|
if (parentPath === this.state.path && !this.state.isViewFile) {
|
|
|
|
this.addDirent(name, 'file', res.data.size);
|
|
|
|
}
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch((error) => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
switchViewMode = (mode) => {
|
|
|
|
if (mode === this.state.currentMode) {
|
|
|
|
return;
|
|
|
|
}
|
2024-10-18 08:01:00 +00:00
|
|
|
if (mode === DIRENT_DETAIL_MODE) {
|
2019-04-10 08:04:26 +00:00
|
|
|
this.toggleDirentDetail();
|
|
|
|
return;
|
|
|
|
}
|
2019-04-13 03:56:44 +00:00
|
|
|
cookie.save('seafile_view_mode', mode);
|
2019-02-20 03:54:25 +00:00
|
|
|
let path = this.state.path;
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown && this.state.isViewFile) {
|
2019-02-20 03:54:25 +00:00
|
|
|
path = Utils.getDirName(path);
|
|
|
|
this.setState({
|
|
|
|
path: path,
|
|
|
|
isViewFile: false,
|
|
|
|
});
|
|
|
|
let repoInfo = this.state.currentRepoInfo;
|
|
|
|
|
|
|
|
let url = siteRoot + 'library/' + repoInfo.repo_id + '/' + encodeURIComponent(repoInfo.repo_name) + Utils.encodePath(path);
|
2024-07-18 03:58:42 +00:00
|
|
|
window.history.pushState({ url: url, path: path }, path, url);
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2019-02-20 03:54:25 +00:00
|
|
|
this.loadSidePanel(this.state.path);
|
|
|
|
}
|
2019-02-23 03:59:26 +00:00
|
|
|
this.isNeedUpdateHistoryState = false;
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ currentMode: mode });
|
2019-02-21 10:09:17 +00:00
|
|
|
this.showDir(path);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onSearchedClick = (item) => {
|
2024-06-06 08:57:20 +00:00
|
|
|
if (item.repo_id !== this.props.repoID) {
|
|
|
|
this.openSearchedNewTab(item);
|
|
|
|
}
|
2020-11-02 05:56:35 +00:00
|
|
|
let path = item.is_dir ? item.path.slice(0, item.path.length - 1) : item.path;
|
2019-02-20 03:54:25 +00:00
|
|
|
if (this.state.currentPath === path) {
|
|
|
|
return;
|
|
|
|
}
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2019-02-20 03:54:25 +00:00
|
|
|
// load sidePanel
|
|
|
|
let index = -1;
|
|
|
|
let paths = Utils.getPaths(path);
|
|
|
|
for (let i = 0; i < paths.length; i++) {
|
2023-09-13 00:40:50 +00:00
|
|
|
// eslint-disable-next-line
|
2019-02-20 03:54:25 +00:00
|
|
|
let node = this.state.treeData.getNodeByPath(node);
|
|
|
|
if (!node) {
|
|
|
|
index = i;
|
|
|
|
break;
|
2020-11-02 05:56:35 +00:00
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
if (index === -1) { // all the data has been loaded already.
|
|
|
|
let node = this.state.treeData.getNodeByPath(path);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ currentNode: node });
|
2019-02-20 03:54:25 +00:00
|
|
|
} else {
|
|
|
|
this.loadNodeAndParentsByPath(path);
|
|
|
|
}
|
|
|
|
|
2019-04-13 03:56:44 +00:00
|
|
|
// load mainPanel
|
|
|
|
if (item.is_dir) {
|
|
|
|
this.showDir(path);
|
|
|
|
} else {
|
2024-09-23 06:37:39 +00:00
|
|
|
this.openSearchedNewTab(item);
|
2019-04-13 03:56:44 +00:00
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
} else {
|
2019-04-13 03:56:44 +00:00
|
|
|
if (item.is_dir) {
|
|
|
|
this.showDir(path);
|
2019-02-20 03:54:25 +00:00
|
|
|
} else {
|
2024-06-06 08:57:20 +00:00
|
|
|
this.openSearchedNewTab(item);
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2024-06-06 08:57:20 +00:00
|
|
|
openSearchedNewTab = (item) => {
|
|
|
|
let path = item.is_dir ? item.path.slice(0, item.path.length - 1) : item.path;
|
|
|
|
let url = siteRoot + 'lib/' + item.repo_id + '/file' + Utils.encodePath(path);
|
|
|
|
let isWeChat = Utils.isWeChat();
|
|
|
|
if (!isWeChat) {
|
|
|
|
let newWindow = window.open('about:blank');
|
|
|
|
newWindow.location.href = url;
|
|
|
|
} else {
|
|
|
|
location.href = url;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
onMainNavBarClick = (nodePath) => {
|
2024-07-18 03:58:42 +00:00
|
|
|
// just for dir
|
2024-11-09 02:36:08 +00:00
|
|
|
this.resetSelected(nodePath);
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2019-02-20 03:54:25 +00:00
|
|
|
let tree = this.state.treeData.clone();
|
|
|
|
let node = tree.getNodeByPath(nodePath);
|
|
|
|
tree.expandNode(node);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree, currentNode: node });
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.showDir(nodePath);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onLinkClick = (link) => {
|
|
|
|
const url = link;
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
if (Utils.isInternalMarkdownLink(url, repoID)) {
|
|
|
|
let path = Utils.getPathFromInternalMarkdownLink(url, repoID);
|
|
|
|
this.showFile(path);
|
|
|
|
} else if (Utils.isInternalDirLink(url, repoID)) {
|
|
|
|
let path = Utils.getPathFromInternalDirLink(url, repoID);
|
|
|
|
this.showDir(path);
|
|
|
|
} else {
|
|
|
|
window.open(url);
|
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
// list&tree operations
|
|
|
|
onMainPanelItemRename = (dirent, newName) => {
|
|
|
|
let path = Utils.joinPath(this.state.path, dirent.name);
|
|
|
|
this.renameItem(path, dirent.isDir(), newName);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onMainPanelItemDelete = (dirent) => {
|
2024-10-18 08:01:00 +00:00
|
|
|
this.updateCurrentNotExistDirent(dirent);
|
2019-02-20 03:54:25 +00:00
|
|
|
let path = Utils.joinPath(this.state.path, dirent.name);
|
|
|
|
this.deleteItem(path, dirent.isDir());
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onRenameTreeNode = (node, newName) => {
|
|
|
|
this.renameItem(node.path, node.object.isDir(), newName);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onDeleteTreeNode = (node) => {
|
|
|
|
this.deleteItem(node.path, node.object.isDir());
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
renameItem = (path, isDir, newName) => {
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
if (isDir) {
|
|
|
|
seafileAPI.renameDir(repoID, path, newName).then(() => {
|
|
|
|
this.renameItemAjaxCallback(path, newName);
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch((error) => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
if (errMessage === gettext('Error')) {
|
|
|
|
let name = Utils.getFileName(path);
|
|
|
|
errMessage = gettext('Renaming {name} failed').replace('{name}', name);
|
|
|
|
}
|
|
|
|
toaster.danger(errMessage);
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
seafileAPI.renameFile(repoID, path, newName).then(() => {
|
|
|
|
this.renameItemAjaxCallback(path, newName);
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch((error) => {
|
2023-11-15 07:39:47 +00:00
|
|
|
let errMessage = '';
|
2023-11-11 09:17:41 +00:00
|
|
|
if (error.response.status == 403 && error.response.data && error.response.data['error_msg']) {
|
|
|
|
errMessage = error.response.data['error_msg'];
|
|
|
|
} else {
|
|
|
|
errMessage = Utils.getErrorMsg(error);
|
2023-11-15 07:39:47 +00:00
|
|
|
}
|
2019-07-16 02:01:09 +00:00
|
|
|
if (errMessage === gettext('Error')) {
|
|
|
|
let name = Utils.getFileName(path);
|
|
|
|
errMessage = gettext('Renaming {name} failed').replace('{name}', name);
|
|
|
|
}
|
|
|
|
toaster.danger(errMessage);
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2024-09-29 09:52:25 +00:00
|
|
|
renameItemAjaxCallback = (path, newName) => {
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2019-02-20 03:54:25 +00:00
|
|
|
this.renameTreeNode(path, newName);
|
|
|
|
}
|
|
|
|
this.renameDirent(path, newName);
|
2024-09-29 09:52:25 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2022-11-28 01:27:17 +00:00
|
|
|
toggleDeleteFolderDialog = () => {
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ isDeleteFolderDialogOpen: !this.state.isDeleteFolderDialogOpen });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2022-11-28 01:27:17 +00:00
|
|
|
|
|
|
|
deleteFolder = () => {
|
|
|
|
const { repoID } = this.props;
|
|
|
|
const { folderToDelete: path } = this.state;
|
2023-05-16 07:46:38 +00:00
|
|
|
seafileAPI.deleteDir(repoID, path).then((res) => {
|
2022-11-28 01:27:17 +00:00
|
|
|
this.deleteItemAjaxCallback(path, true);
|
|
|
|
let name = Utils.getFileName(path);
|
|
|
|
var msg = gettext('Successfully deleted {name}').replace('{name}', name);
|
2023-05-16 07:46:38 +00:00
|
|
|
const successTipWithUndo = (
|
|
|
|
<>
|
|
|
|
<span>{msg}</span>
|
|
|
|
<a className="action-link p-0 ml-1" href="#" onClick={this.restoreDeletedDirents.bind(this, res.data.commit_id, [path])}>{gettext('Undo')}</a>
|
|
|
|
</>
|
|
|
|
);
|
2024-07-18 03:58:42 +00:00
|
|
|
toaster.success(successTipWithUndo, { duration: 5 });
|
2022-11-28 01:27:17 +00:00
|
|
|
}).catch((error) => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
if (errMessage === gettext('Error')) {
|
|
|
|
let name = Utils.getFileName(path);
|
|
|
|
errMessage = gettext('Failed to delete {name}').replace('{name}', name);
|
|
|
|
}
|
|
|
|
toaster.danger(errMessage);
|
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2022-11-28 01:27:17 +00:00
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
deleteItem(path, isDir) {
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
if (isDir) {
|
2022-11-28 01:27:17 +00:00
|
|
|
this.setState({ folderToDelete: path }, () => {
|
|
|
|
this.toggleDeleteFolderDialog();
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
} else {
|
2023-05-16 07:46:38 +00:00
|
|
|
seafileAPI.deleteFile(repoID, path).then((res) => {
|
2019-02-20 03:54:25 +00:00
|
|
|
this.deleteItemAjaxCallback(path, isDir);
|
2019-05-28 09:48:44 +00:00
|
|
|
let name = Utils.getFileName(path);
|
2019-05-29 04:02:07 +00:00
|
|
|
var msg = gettext('Successfully deleted {name}').replace('{name}', name);
|
2023-05-16 07:46:38 +00:00
|
|
|
const successTipWithUndo = (
|
|
|
|
<>
|
|
|
|
<span>{msg}</span>
|
|
|
|
<a className="action-link p-0 ml-1" href="#" onClick={this.restoreDeletedDirents.bind(this, res.data.commit_id, [path])}>{gettext('Undo')}</a>
|
|
|
|
</>
|
|
|
|
);
|
2024-07-18 03:58:42 +00:00
|
|
|
toaster.success(successTipWithUndo, { duration: 5 });
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch((error) => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
if (errMessage === gettext('Error')) {
|
|
|
|
let name = Utils.getFileName(path);
|
|
|
|
errMessage = gettext('Failed to delete {name}').replace('{name}', name);
|
|
|
|
}
|
|
|
|
toaster.danger(errMessage);
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
deleteItemAjaxCallback(path) {
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2019-02-20 03:54:25 +00:00
|
|
|
this.deleteTreeNode(path);
|
|
|
|
}
|
|
|
|
this.deleteDirent(path);
|
2024-09-23 01:48:43 +00:00
|
|
|
this.removeFromRecentlyUsed(this.props.repoID, path);
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
|
2024-09-29 09:52:25 +00:00
|
|
|
deleteItemsAjaxCallback = (direntPaths, dirNames) => {
|
|
|
|
if (this.state.isTreePanelShown) {
|
|
|
|
this.deleteTreeNodes(direntPaths);
|
|
|
|
}
|
|
|
|
this.deleteDirents(dirNames);
|
|
|
|
this.removeFromRecentlyUsed(this.props.repoID, this.state.path);
|
|
|
|
};
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
// list operations
|
2024-11-18 13:21:33 +00:00
|
|
|
onMoveItem = (destRepo, dirent, moveToDirentPath, nodeParentPath, byDialog = false) => {
|
2024-10-18 08:01:00 +00:00
|
|
|
this.updateCurrentNotExistDirent(dirent);
|
2019-02-20 03:54:25 +00:00
|
|
|
let repoID = this.props.repoID;
|
2024-07-18 03:58:42 +00:00
|
|
|
// just for view list state
|
2019-02-20 03:54:25 +00:00
|
|
|
let dirName = dirent.name;
|
2019-03-20 03:04:36 +00:00
|
|
|
if (!nodeParentPath) {
|
|
|
|
nodeParentPath = this.state.path;
|
|
|
|
}
|
|
|
|
let direntPath = Utils.joinPath(nodeParentPath, dirName);
|
2020-02-29 09:02:11 +00:00
|
|
|
|
2019-05-05 09:20:37 +00:00
|
|
|
seafileAPI.moveDir(repoID, destRepo.repo_id, moveToDirentPath, nodeParentPath, dirName).then(res => {
|
2019-07-23 01:42:09 +00:00
|
|
|
if (repoID !== destRepo.repo_id) {
|
2024-02-07 03:08:41 +00:00
|
|
|
this.setState({
|
|
|
|
asyncCopyMoveTaskId: res.data.task_id,
|
|
|
|
asyncOperatedFilesLength: 1,
|
|
|
|
asyncOperationProgress: 0,
|
|
|
|
asyncOperationType: 'move',
|
|
|
|
isCopyMoveProgressDialogShow: true,
|
2024-02-19 08:12:09 +00:00
|
|
|
}, () => {
|
2020-04-10 10:08:27 +00:00
|
|
|
this.currentMoveItemName = dirName;
|
|
|
|
this.currentMoveItemPath = direntPath;
|
|
|
|
this.getAsyncCopyMoveProgress(dirName, direntPath);
|
2019-07-23 01:42:09 +00:00
|
|
|
});
|
2020-02-29 09:02:11 +00:00
|
|
|
}
|
|
|
|
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2020-02-29 09:02:11 +00:00
|
|
|
this.deleteTreeNode(direntPath);
|
|
|
|
}
|
|
|
|
|
2020-11-02 05:56:35 +00:00
|
|
|
// 1. move to current repo
|
2020-02-29 09:02:11 +00:00
|
|
|
// 2. tow columns mode need update left tree
|
2024-05-07 08:45:51 +00:00
|
|
|
if (repoID === destRepo.repo_id &&
|
|
|
|
this.state.isTreePanelShown) {
|
2020-02-29 09:02:11 +00:00
|
|
|
this.updateMoveCopyTreeNode(moveToDirentPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.moveDirent(direntPath, moveToDirentPath);
|
|
|
|
|
|
|
|
// show tip message if move to current repo
|
|
|
|
if (repoID === destRepo.repo_id) {
|
2019-07-24 23:55:52 +00:00
|
|
|
let message = gettext('Successfully moved {name}.');
|
|
|
|
message = message.replace('{name}', dirName);
|
2019-07-23 01:42:09 +00:00
|
|
|
toaster.success(message);
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
2024-08-05 02:48:16 +00:00
|
|
|
|
2024-11-18 13:21:33 +00:00
|
|
|
if (byDialog) {
|
2024-11-09 08:48:46 +00:00
|
|
|
this.updateRecentlyUsedRepos(destRepo, moveToDirentPath);
|
|
|
|
}
|
2024-08-05 02:48:16 +00:00
|
|
|
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch((error) => {
|
2024-02-07 03:08:41 +00:00
|
|
|
if (!error.response.data.lib_need_decrypt) {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
if (errMessage === gettext('Error')) {
|
|
|
|
errMessage = gettext('Failed to move {name}.');
|
|
|
|
errMessage = errMessage.replace('{name}', dirName);
|
|
|
|
}
|
|
|
|
toaster.danger(errMessage);
|
|
|
|
} else {
|
|
|
|
this.setState({
|
|
|
|
libNeedDecryptWhenMove: true,
|
|
|
|
destRepoWhenCopyMove: destRepo,
|
|
|
|
destDirentPathWhenCopyMove: moveToDirentPath,
|
|
|
|
copyMoveSingleItem: true,
|
|
|
|
srcDirentWhenCopyMove: dirent,
|
|
|
|
srcNodeParentPathWhenCopyMove: nodeParentPath,
|
|
|
|
});
|
2019-07-16 02:01:09 +00:00
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2024-11-18 13:21:33 +00:00
|
|
|
onCopyItem = (destRepo, dirent, copyToDirentPath, nodeParentPath, byDialog = false) => {
|
2019-02-20 03:54:25 +00:00
|
|
|
let repoID = this.props.repoID;
|
2024-07-18 03:58:42 +00:00
|
|
|
// just for view list state
|
2019-02-20 03:54:25 +00:00
|
|
|
let dirName = dirent.name;
|
2019-03-20 03:04:36 +00:00
|
|
|
if (!nodeParentPath) {
|
|
|
|
nodeParentPath = this.state.path;
|
|
|
|
}
|
2020-02-29 09:02:11 +00:00
|
|
|
|
2019-03-20 03:04:36 +00:00
|
|
|
seafileAPI.copyDir(repoID, destRepo.repo_id, copyToDirentPath, nodeParentPath, dirName).then(res => {
|
2020-11-02 05:56:35 +00:00
|
|
|
|
2019-07-23 01:42:09 +00:00
|
|
|
if (repoID !== destRepo.repo_id) {
|
2020-02-29 09:02:11 +00:00
|
|
|
this.setState({
|
|
|
|
asyncCopyMoveTaskId: res.data.task_id,
|
2024-02-07 03:08:41 +00:00
|
|
|
asyncOperatedFilesLength: 1,
|
|
|
|
asyncOperationProgress: 0,
|
|
|
|
asyncOperationType: 'copy',
|
|
|
|
isCopyMoveProgressDialogShow: true
|
2020-02-29 09:02:11 +00:00
|
|
|
}, () => {
|
|
|
|
this.getAsyncCopyMoveProgress();
|
2019-07-23 01:42:09 +00:00
|
|
|
});
|
2020-02-29 09:02:11 +00:00
|
|
|
}
|
|
|
|
|
2020-04-10 10:08:27 +00:00
|
|
|
if (repoID === destRepo.repo_id) {
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2020-04-10 10:08:27 +00:00
|
|
|
this.updateMoveCopyTreeNode(copyToDirentPath);
|
|
|
|
}
|
2020-02-29 09:02:11 +00:00
|
|
|
|
2020-04-10 10:08:27 +00:00
|
|
|
if (copyToDirentPath === nodeParentPath) {
|
|
|
|
this.loadDirentList(this.state.path);
|
|
|
|
}
|
2020-02-29 09:02:11 +00:00
|
|
|
|
2019-07-23 01:42:09 +00:00
|
|
|
let message = gettext('Successfully copied %(name)s.');
|
|
|
|
message = message.replace('%(name)s', dirName);
|
|
|
|
toaster.success(message);
|
2024-11-18 13:21:33 +00:00
|
|
|
|
|
|
|
if (byDialog) {
|
|
|
|
this.updateRecentlyUsedRepos(destRepo, copyToDirentPath);
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch((error) => {
|
2024-02-07 03:08:41 +00:00
|
|
|
if (!error.response.data.lib_need_decrypt) {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
if (errMessage === gettext('Error')) {
|
|
|
|
errMessage = gettext('Failed to copy %(name)s');
|
|
|
|
errMessage = errMessage.replace('%(name)s', dirName);
|
|
|
|
}
|
|
|
|
toaster.danger(errMessage);
|
|
|
|
} else {
|
|
|
|
this.setState({
|
|
|
|
libNeedDecryptWhenCopy: true,
|
|
|
|
destRepoWhenCopyMove: destRepo,
|
|
|
|
destDirentPathWhenCopyMove: copyToDirentPath,
|
|
|
|
copyMoveSingleItem: true,
|
|
|
|
srcDirentWhenCopyMove: dirent,
|
|
|
|
srcNodeParentPathWhenCopyMove: nodeParentPath,
|
|
|
|
});
|
2019-07-16 02:01:09 +00:00
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2023-09-14 06:36:58 +00:00
|
|
|
onConvertItem = (dirent, dstType) => {
|
|
|
|
let path = Utils.joinPath(this.state.path, dirent.name);
|
|
|
|
let repoID = this.props.repoID;
|
2024-07-18 03:58:42 +00:00
|
|
|
toaster.notifyInProgress(gettext('Converting, please wait...'), { 'id': 'conversion' });
|
2023-09-14 06:36:58 +00:00
|
|
|
seafileAPI.convertFile(repoID, path, dstType).then((res) => {
|
2023-09-25 13:56:00 +00:00
|
|
|
let newFileName = res.data.obj_name;
|
2023-09-14 06:36:58 +00:00
|
|
|
let parentDir = res.data.parent_dir;
|
2023-09-25 13:56:00 +00:00
|
|
|
let new_path = parentDir + '/' + newFileName;
|
|
|
|
let parentPath = Utils.getDirName(new_path);
|
2023-09-14 06:36:58 +00:00
|
|
|
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2023-09-25 13:56:00 +00:00
|
|
|
this.addNodeToTree(newFileName, parentPath, 'file');
|
2023-09-18 02:20:47 +00:00
|
|
|
}
|
2023-09-25 13:56:00 +00:00
|
|
|
|
|
|
|
this.addDirent(newFileName, 'file', res.data.size);
|
2023-10-08 06:20:14 +00:00
|
|
|
let message = gettext('Successfully converted the file.');
|
2024-07-18 03:58:42 +00:00
|
|
|
toaster.success(message, { 'id': 'conversion' });
|
2023-09-14 06:36:58 +00:00
|
|
|
|
|
|
|
}).catch((error) => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
if (errMessage === gettext('Error')) {
|
|
|
|
let name = Utils.getFileName(path);
|
2023-10-08 06:20:14 +00:00
|
|
|
errMessage = gettext('Failed to convert {name}.').replace('{name}', name);
|
2023-09-14 06:36:58 +00:00
|
|
|
}
|
2024-07-18 03:58:42 +00:00
|
|
|
toaster.danger(errMessage, { 'id': 'conversion' });
|
2023-09-14 06:36:58 +00:00
|
|
|
});
|
2023-09-25 13:56:00 +00:00
|
|
|
|
2023-09-18 02:20:47 +00:00
|
|
|
};
|
2023-09-14 06:36:58 +00:00
|
|
|
|
2024-07-23 15:08:53 +00:00
|
|
|
onDirentClick = (clickedDirent, event) => {
|
2024-09-13 12:28:20 +00:00
|
|
|
this.setState({
|
|
|
|
currentDirent: clickedDirent && clickedDirent.isActive ? null : clickedDirent
|
|
|
|
});
|
2024-07-23 15:08:53 +00:00
|
|
|
const { direntList, selectedDirentList, lastSelectedIndex } = this.state;
|
|
|
|
if (clickedDirent) {
|
|
|
|
const clickedIndex = direntList.findIndex(dirent => dirent.name === clickedDirent.name);
|
|
|
|
|
|
|
|
let newSelectedDirentList = [...selectedDirentList];
|
|
|
|
const isCtrlOrMetaKeyPressed = event && (event.ctrlKey || event.metaKey);
|
|
|
|
const isShiftKeyPressed = event && event.shiftKey;
|
|
|
|
|
|
|
|
if (isCtrlOrMetaKeyPressed) {
|
|
|
|
// Ctrl (Cmd on Mac) key is pressed: Toggle selection of the clicked item
|
|
|
|
const isSelected = newSelectedDirentList.some(dirent => dirent.name === clickedDirent.name);
|
|
|
|
if (isSelected) {
|
|
|
|
newSelectedDirentList = newSelectedDirentList.filter(dirent => dirent.name !== clickedDirent.name);
|
|
|
|
} else {
|
|
|
|
newSelectedDirentList.push(clickedDirent);
|
|
|
|
}
|
|
|
|
} else if (isShiftKeyPressed && lastSelectedIndex !== null) {
|
2024-08-29 01:58:15 +00:00
|
|
|
// Shift key is pressed: Select or unselect items based on the clicked index
|
|
|
|
const firstSelectedIndex = direntList.findIndex(dirent => dirent.name === newSelectedDirentList[0].name);
|
|
|
|
if (clickedIndex < firstSelectedIndex) {
|
|
|
|
// Selected all the items between the clicked item and the first selected item
|
|
|
|
const rangeSelectedDirents = direntList.slice(clickedIndex, firstSelectedIndex + 1);
|
|
|
|
const rangeSelectedNames = new Set(rangeSelectedDirents.map(dirent => dirent.name));
|
|
|
|
newSelectedDirentList = [
|
|
|
|
...newSelectedDirentList.filter(dirent => rangeSelectedNames.has(dirent.name)),
|
|
|
|
...rangeSelectedDirents
|
|
|
|
];
|
|
|
|
} else if (clickedIndex >= firstSelectedIndex && clickedIndex < lastSelectedIndex) {
|
|
|
|
// Unselect items between clicked item and the last selected item
|
|
|
|
const rangeSelectedDirents = direntList.slice(firstSelectedIndex, clickedIndex + 1);
|
|
|
|
const rangeSelectedNames = new Set(rangeSelectedDirents.map(dirent => dirent.name));
|
|
|
|
newSelectedDirentList = newSelectedDirentList.filter(dirent => rangeSelectedNames.has(dirent.name));
|
|
|
|
} else {
|
|
|
|
// Select all items between the first selected and the clicked item
|
|
|
|
const rangeStart = Math.min(firstSelectedIndex, clickedIndex);
|
|
|
|
const rangeEnd = Math.max(firstSelectedIndex, clickedIndex);
|
|
|
|
const rangeSelectedDirents = direntList.slice(rangeStart, rangeEnd + 1);
|
|
|
|
|
|
|
|
// Merge the new range selection with the existing selection
|
|
|
|
const rangeSelectedNames = new Set(rangeSelectedDirents.map(dirent => dirent.name));
|
|
|
|
newSelectedDirentList = [
|
|
|
|
...newSelectedDirentList.filter(dirent => !rangeSelectedNames.has(dirent.name)),
|
|
|
|
...rangeSelectedDirents
|
|
|
|
];
|
|
|
|
}
|
2024-07-23 15:08:53 +00:00
|
|
|
} else {
|
|
|
|
newSelectedDirentList = [clickedDirent];
|
|
|
|
}
|
|
|
|
|
2019-04-13 01:57:44 +00:00
|
|
|
this.setState({
|
2024-07-23 15:08:53 +00:00
|
|
|
direntList: direntList.map(dirent => {
|
|
|
|
dirent.isSelected = newSelectedDirentList.some(selectedDirent => selectedDirent.name === dirent.name);
|
|
|
|
return dirent;
|
|
|
|
}),
|
|
|
|
isDirentSelected: newSelectedDirentList.length > 0,
|
2024-07-26 09:07:26 +00:00
|
|
|
isAllDirentSelected: newSelectedDirentList.length === direntList.length,
|
2024-07-23 15:08:53 +00:00
|
|
|
selectedDirentList: newSelectedDirentList,
|
|
|
|
lastSelectedIndex: clickedIndex,
|
2019-04-13 01:57:44 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.setState({
|
2024-07-23 15:08:53 +00:00
|
|
|
direntList: direntList.map(dirent => {
|
|
|
|
dirent.isSelected = false;
|
|
|
|
return dirent;
|
|
|
|
}),
|
2019-04-13 01:57:44 +00:00
|
|
|
isDirentSelected: false,
|
2024-07-26 09:07:26 +00:00
|
|
|
isAllDirentSelected: false,
|
2019-04-13 01:57:44 +00:00
|
|
|
selectedDirentList: [],
|
2024-07-23 15:08:53 +00:00
|
|
|
lastSelectedIndex: null,
|
2019-04-13 01:57:44 +00:00
|
|
|
});
|
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-04-12 12:21:54 +00:00
|
|
|
|
2024-07-26 09:15:18 +00:00
|
|
|
onSelectedDirentListUpdate = (newSelectedDirentList, lastSelectedIndex = null) => {
|
|
|
|
this.setState({
|
|
|
|
direntList: this.state.direntList.map(dirent => {
|
|
|
|
dirent.isSelected = newSelectedDirentList.some(selectedDirent => selectedDirent.name === dirent.name);
|
|
|
|
return dirent;
|
|
|
|
}),
|
|
|
|
isDirentSelected: newSelectedDirentList.length > 0,
|
|
|
|
selectedDirentList: newSelectedDirentList,
|
|
|
|
lastSelectedIndex: lastSelectedIndex,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2019-04-12 12:21:54 +00:00
|
|
|
onItemClick = (dirent) => {
|
2024-11-09 02:36:08 +00:00
|
|
|
this.resetSelected(dirent);
|
2019-02-20 03:54:25 +00:00
|
|
|
let repoID = this.props.repoID;
|
|
|
|
let direntPath = Utils.joinPath(this.state.path, dirent.name);
|
2024-07-18 03:58:42 +00:00
|
|
|
if (dirent.isDir()) { // is dir
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2019-02-20 03:54:25 +00:00
|
|
|
this.loadTreeNodeByPath(direntPath);
|
|
|
|
}
|
|
|
|
this.showDir(direntPath);
|
2024-07-18 03:58:42 +00:00
|
|
|
} else { // is file
|
2024-11-18 04:07:03 +00:00
|
|
|
let url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(direntPath);
|
|
|
|
if (dirent.is_sdoc_revision && dirent.revision_id) {
|
|
|
|
url = siteRoot + 'lib/' + repoID + '/revisions/' + dirent.revision_id + '/';
|
|
|
|
}
|
|
|
|
let isWeChat = Utils.isWeChat();
|
|
|
|
if (!isWeChat) {
|
|
|
|
window.open(url);
|
2019-02-20 03:54:25 +00:00
|
|
|
} else {
|
2024-11-18 04:07:03 +00:00
|
|
|
location.href = url;
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2024-10-14 08:29:56 +00:00
|
|
|
onDirentSelected = (dirent, event) => {
|
2024-09-13 12:28:20 +00:00
|
|
|
this.setState({
|
|
|
|
currentDirent: dirent && dirent.isActive ? null : dirent
|
|
|
|
});
|
2024-10-14 08:29:56 +00:00
|
|
|
|
|
|
|
const { direntList, lastSelectedIndex, selectedDirentList } = this.state;
|
|
|
|
const clickedDirentIndex = dirent ? direntList.findIndex((currDirent) => currDirent.name === dirent.name) : -1;
|
|
|
|
const clickedDirent = clickedDirentIndex > -1 ? direntList[clickedDirentIndex] : null;
|
|
|
|
let nextSelectedIndex = null;
|
|
|
|
if (clickedDirent && !clickedDirent.isSelected) {
|
|
|
|
nextSelectedIndex = clickedDirentIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
let nextDirentList = direntList;
|
|
|
|
if (event && event.shiftKey && lastSelectedIndex !== null && nextSelectedIndex !== null) {
|
|
|
|
// select multiple files with shift key
|
|
|
|
const start = Math.min(lastSelectedIndex, nextSelectedIndex);
|
|
|
|
const end = Math.max(lastSelectedIndex, nextSelectedIndex);
|
|
|
|
const direntListInRange = direntList.slice(start, end + 1);
|
|
|
|
if (direntListInRange.length > 0) {
|
|
|
|
const nameDirentSelectedMap = selectedDirentList.reduce((currNameDirentSelectedMap, currDirent) => {
|
|
|
|
return { ...currNameDirentSelectedMap, [currDirent.name]: true };
|
|
|
|
}, {});
|
|
|
|
const nameDirentSelectingMap = direntListInRange.reduce((currNameDirentSelectingMap, currDirent) => {
|
|
|
|
return { ...currNameDirentSelectingMap, [currDirent.name]: true };
|
|
|
|
}, {});
|
|
|
|
nextDirentList = direntList.map((currDirent) => {
|
|
|
|
if (nameDirentSelectedMap[currDirent.name] || nameDirentSelectingMap[currDirent.name]) {
|
|
|
|
currDirent.isSelected = true;
|
|
|
|
}
|
|
|
|
return currDirent;
|
|
|
|
});
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
2024-10-14 08:29:56 +00:00
|
|
|
} else {
|
|
|
|
nextDirentList = direntList.map(item => {
|
|
|
|
if (dirent && item.name === dirent.name) {
|
|
|
|
item.isSelected = !item.isSelected;
|
|
|
|
}
|
|
|
|
return item;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
const nextSelectedDirentList = nextDirentList.filter(item => item.isSelected);
|
|
|
|
if (nextSelectedDirentList.length) {
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ isDirentSelected: true });
|
2024-10-14 08:29:56 +00:00
|
|
|
if (nextSelectedDirentList.length === nextDirentList.length) {
|
2019-02-20 03:54:25 +00:00
|
|
|
this.setState({
|
|
|
|
isAllDirentSelected: true,
|
2024-10-14 08:29:56 +00:00
|
|
|
direntList: nextDirentList,
|
|
|
|
selectedDirentList: nextSelectedDirentList,
|
|
|
|
lastSelectedIndex: nextSelectedIndex,
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.setState({
|
|
|
|
isAllDirentSelected: false,
|
2024-10-14 08:29:56 +00:00
|
|
|
direntList: nextDirentList,
|
|
|
|
selectedDirentList: nextSelectedDirentList,
|
|
|
|
lastSelectedIndex: nextSelectedIndex,
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.setState({
|
|
|
|
isDirentSelected: false,
|
|
|
|
isAllDirentSelected: false,
|
2024-10-14 08:29:56 +00:00
|
|
|
direntList: nextDirentList,
|
|
|
|
selectedDirentList: [],
|
|
|
|
lastSelectedIndex: nextSelectedIndex,
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onAllDirentSelected = () => {
|
2024-07-26 09:07:26 +00:00
|
|
|
this.setState(prevState => {
|
|
|
|
const isAllDirentSelected = !prevState.isAllDirentSelected;
|
|
|
|
const direntList = prevState.direntList.map(item => {
|
|
|
|
item.isSelected = isAllDirentSelected;
|
2019-02-20 03:54:25 +00:00
|
|
|
return item;
|
|
|
|
});
|
2024-07-26 09:07:26 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
isDirentSelected: isAllDirentSelected,
|
|
|
|
isAllDirentSelected: isAllDirentSelected,
|
2019-02-20 03:54:25 +00:00
|
|
|
direntList: direntList,
|
2024-07-26 09:07:26 +00:00
|
|
|
selectedDirentList: isAllDirentSelected ? [...direntList] : []
|
|
|
|
};
|
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onFileTagChanged = (dirent, direntPath) => {
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
seafileAPI.listFileTags(repoID, direntPath).then(res => {
|
|
|
|
let fileTags = res.data.file_tags.map(item => {
|
|
|
|
return new FileTag(item);
|
|
|
|
});
|
2023-06-07 12:41:12 +00:00
|
|
|
|
|
|
|
if (this.state.isViewFile) {
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ fileTags: fileTags });
|
2023-06-07 12:41:12 +00:00
|
|
|
} else {
|
|
|
|
this.updateDirent(dirent, 'file_tags', fileTags);
|
|
|
|
}
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
this.updateUsedRepoTags();
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onFileUploadSuccess = (direntObject) => {
|
2024-07-23 15:08:53 +00:00
|
|
|
const isExist = this.state.direntList.some(item => item.name === direntObject.name && item.type === direntObject.type);
|
2019-02-20 03:54:25 +00:00
|
|
|
if (isExist) {
|
2024-09-11 10:27:35 +00:00
|
|
|
const dirent = this.state.direntList.find(dirent => dirent.name === direntObject.name && dirent.type === direntObject.type);
|
2024-10-21 03:29:17 +00:00
|
|
|
const mtime = dayjs.unix(direntObject.mtime).fromNow();
|
2024-09-11 10:27:35 +00:00
|
|
|
dirent && this.updateDirent(dirent, 'mtime', mtime);
|
2019-02-20 03:54:25 +00:00
|
|
|
} else {
|
2021-09-13 02:37:07 +00:00
|
|
|
// use current dirent parent's permission as it's permission
|
|
|
|
direntObject.permission = this.state.userPerm;
|
2024-07-22 03:41:30 +00:00
|
|
|
const dirent = new Dirent(direntObject);
|
|
|
|
|
|
|
|
this.setState(prevState => ({
|
|
|
|
direntList: direntObject.type === 'dir' ? [dirent, ...prevState.direntList] : [...prevState.direntList, dirent]
|
|
|
|
}));
|
|
|
|
|
2024-05-07 08:45:51 +00:00
|
|
|
if (this.state.isTreePanelShown) {
|
2019-02-20 03:54:25 +00:00
|
|
|
this.addNodeToTree(dirent.name, this.state.path, dirent.type);
|
|
|
|
}
|
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2024-03-25 09:22:01 +00:00
|
|
|
addDirent = (name, type, size) => {
|
|
|
|
let item = this.createDirent(name, type, size);
|
2019-02-20 03:54:25 +00:00
|
|
|
let direntList = this.state.direntList;
|
|
|
|
if (type === 'dir') {
|
|
|
|
direntList.unshift(item);
|
|
|
|
} else {
|
|
|
|
// there will be there conditions;
|
|
|
|
// first: direntList.length === 0;
|
|
|
|
// second: all the direntList's items are dir;
|
|
|
|
// third: direntList has dir and file;
|
|
|
|
let length = direntList.length;
|
|
|
|
if (length === 0 || direntList[length - 1].type === 'dir') {
|
|
|
|
direntList.push(item);
|
|
|
|
} else {
|
|
|
|
let index = 0;
|
|
|
|
for (let i = 0; i <= length; i++) {
|
|
|
|
if (direntList[i].type === 'file') {
|
|
|
|
index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
direntList.splice(index, 0, item);
|
|
|
|
}
|
|
|
|
}
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ direntList: direntList });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
renameDirent = (direntPath, newName) => {
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
let parentPath = Utils.getDirName(direntPath);
|
|
|
|
let newDirentPath = Utils.joinPath(parentPath, newName);
|
|
|
|
if (direntPath === this.state.path) {
|
|
|
|
// the renamed item is current viewed item
|
|
|
|
// example: direntPath = /A/B/C, state.path = /A/B/C
|
|
|
|
|
|
|
|
this.setState({ path: newDirentPath });
|
2019-03-05 05:36:31 +00:00
|
|
|
let repoInfo = this.state.currentRepoInfo;
|
|
|
|
let url = siteRoot + 'library/' + repoID + '/' + encodeURIComponent(repoInfo.repo_name) + newDirentPath;
|
2024-07-18 03:58:42 +00:00
|
|
|
window.history.replaceState({ url: url, path: newDirentPath }, newDirentPath, url);
|
2019-02-20 03:54:25 +00:00
|
|
|
} else if (Utils.isChildPath(direntPath, this.state.path)) {
|
|
|
|
// example: direntPath = /A/B/C/D, state.path = /A/B/C
|
|
|
|
let oldName = Utils.getFileName(direntPath);
|
|
|
|
let direntList = this.state.direntList.map(item => {
|
|
|
|
if (item.name === oldName) {
|
|
|
|
item.name = newName;
|
|
|
|
}
|
|
|
|
return item;
|
|
|
|
});
|
|
|
|
this.setState({ direntList: direntList });
|
|
|
|
} else if (Utils.isAncestorPath(direntPath, this.state.path)) {
|
|
|
|
// example: direntPath = /A/B, state.path = /A/B/C
|
|
|
|
let newPath = Utils.renameAncestorPath(this.state.path, direntPath, newDirentPath);
|
|
|
|
this.setState({ path: newPath });
|
|
|
|
|
2019-03-05 05:36:31 +00:00
|
|
|
let repoInfo = this.state.currentRepoInfo;
|
|
|
|
let url = siteRoot + 'library/' + repoID + '/' + encodeURIComponent(repoInfo.repo_name) + newPath;
|
2024-07-18 03:58:42 +00:00
|
|
|
window.history.replaceState({ url: url, path: newPath }, newPath, url);
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
deleteDirent(direntPath) {
|
|
|
|
if (direntPath === this.state.path) {
|
|
|
|
// The deleted item is current item
|
|
|
|
let parentPath = Utils.getDirName(direntPath);
|
|
|
|
this.showDir(parentPath);
|
|
|
|
} else if (Utils.isChildPath(direntPath, this.state.path)) {
|
|
|
|
// The deleted item is inside current path
|
|
|
|
let name = Utils.getFileName(direntPath);
|
|
|
|
let direntList = this.state.direntList.filter(item => {
|
|
|
|
return item.name !== name;
|
|
|
|
});
|
2024-07-04 08:54:39 +00:00
|
|
|
this.recalculateSelectedDirents([name], direntList);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ direntList: direntList });
|
2019-02-20 03:54:25 +00:00
|
|
|
} else if (Utils.isAncestorPath(direntPath, this.state.path)) {
|
|
|
|
// the deleted item is ancester of the current item
|
|
|
|
let parentPath = Utils.getDirName(direntPath);
|
|
|
|
this.showDir(parentPath);
|
|
|
|
}
|
|
|
|
// else do nothing
|
|
|
|
}
|
|
|
|
|
2020-04-02 07:45:33 +00:00
|
|
|
// only one scence: The deleted items are inside current path
|
|
|
|
deleteDirents = (direntNames) => {
|
|
|
|
let direntList = this.state.direntList.filter(item => {
|
|
|
|
return direntNames.indexOf(item.name) === -1;
|
|
|
|
});
|
2024-07-04 08:54:39 +00:00
|
|
|
this.recalculateSelectedDirents(direntNames, direntList);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ direntList: direntList });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2020-04-02 07:45:33 +00:00
|
|
|
|
2019-05-07 02:06:24 +00:00
|
|
|
moveDirent = (direntPath, moveToDirentPath = null) => {
|
|
|
|
let name = Utils.getFileName(direntPath);
|
2019-05-05 09:20:37 +00:00
|
|
|
if (moveToDirentPath === this.state.path) {
|
2019-05-07 02:14:29 +00:00
|
|
|
this.loadDirentList(this.state.path);
|
2019-05-05 09:20:37 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
let direntList = this.state.direntList.filter(item => {
|
|
|
|
return item.name !== name;
|
|
|
|
});
|
2024-07-04 08:54:39 +00:00
|
|
|
this.recalculateSelectedDirents([name], direntList);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ direntList: direntList });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2020-04-02 07:45:33 +00:00
|
|
|
// only one scence: The moved items are inside current path
|
|
|
|
moveDirents = (direntNames) => {
|
|
|
|
let direntList = this.state.direntList.filter(item => {
|
|
|
|
return direntNames.indexOf(item.name) === -1;
|
|
|
|
});
|
2024-07-04 08:54:39 +00:00
|
|
|
this.recalculateSelectedDirents(direntNames, direntList);
|
2023-09-20 00:07:51 +00:00
|
|
|
this.setState({
|
|
|
|
direntList: direntList,
|
|
|
|
selectedDirentList: [],
|
|
|
|
isDirentSelected: false,
|
|
|
|
isAllDirentSelected: false,
|
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2020-04-02 07:45:33 +00:00
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
updateDirent = (dirent, paramKey, paramValue) => {
|
|
|
|
let newDirentList = this.state.direntList.map(item => {
|
|
|
|
if (item.name === dirent.name) {
|
|
|
|
item[paramKey] = paramValue;
|
|
|
|
}
|
|
|
|
return item;
|
|
|
|
});
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ direntList: newDirentList });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
// tree operations
|
|
|
|
loadTreeNodeByPath = (path) => {
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
let tree = this.state.treeData.clone();
|
|
|
|
let node = tree.getNodeByPath(path);
|
|
|
|
if (!node.isLoaded) {
|
|
|
|
seafileAPI.listDir(repoID, node.path).then(res => {
|
|
|
|
this.addResponseListToNode(res.data.dirent_list, node);
|
|
|
|
let parentNode = tree.getNodeByPath(node.parentNode.path);
|
|
|
|
parentNode.isExpanded = true;
|
|
|
|
this.setState({
|
|
|
|
treeData: tree,
|
|
|
|
currentNode: node
|
|
|
|
});
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
let parentNode = tree.getNodeByPath(node.parentNode.path);
|
|
|
|
parentNode.isExpanded = true;
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree, currentNode: node }); // tree
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
loadNodeAndParentsByPath = (path) => {
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
let tree = this.state.treeData.clone();
|
|
|
|
if (Utils.isMarkdownFile(path)) {
|
|
|
|
path = Utils.getDirName(path);
|
|
|
|
}
|
2024-07-18 03:58:42 +00:00
|
|
|
seafileAPI.listDir(repoID, path, { with_parents: true }).then(res => {
|
2022-04-11 09:52:07 +00:00
|
|
|
const { dirent_list: direntList, user_perm } = res.data;
|
2019-02-20 03:54:25 +00:00
|
|
|
let results = {};
|
|
|
|
for (let i = 0; i < direntList.length; i++) {
|
|
|
|
let object = direntList[i];
|
2019-04-17 02:48:44 +00:00
|
|
|
let parentDir = object.parent_dir;
|
2024-07-18 03:58:42 +00:00
|
|
|
let key = parentDir === '/' ? '/' : parentDir.slice(0, parentDir.length - 1);
|
2019-02-20 03:54:25 +00:00
|
|
|
if (!results[key]) {
|
|
|
|
results[key] = [];
|
|
|
|
}
|
|
|
|
results[key].push(object);
|
|
|
|
}
|
|
|
|
for (let key in results) {
|
|
|
|
let node = tree.getNodeByPath(key);
|
|
|
|
if (!node.isLoaded) {
|
|
|
|
this.addResponseListToNode(results[key], node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.setState({
|
|
|
|
isTreeDataLoading: false,
|
2022-04-11 09:52:07 +00:00
|
|
|
treeData: tree,
|
|
|
|
userPerm: user_perm,
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
}).catch(() => {
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ isLoadFailed: true });
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onTreeNodeClick = (node) => {
|
2024-11-09 02:36:08 +00:00
|
|
|
this.resetSelected(node);
|
2019-02-20 03:54:25 +00:00
|
|
|
let repoID = this.props.repoID;
|
2024-06-29 09:58:27 +00:00
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
if (!this.state.pathExist) {
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ pathExist: true });
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (node.object.isDir()) {
|
2019-02-21 09:37:04 +00:00
|
|
|
let isLoaded = node.isLoaded;
|
2019-02-20 03:54:25 +00:00
|
|
|
if (!node.isLoaded) {
|
|
|
|
let tree = this.state.treeData.clone();
|
|
|
|
node = tree.getNodeByPath(node.path);
|
|
|
|
seafileAPI.listDir(repoID, node.path).then(res => {
|
|
|
|
this.addResponseListToNode(res.data.dirent_list, node);
|
|
|
|
tree.collapseNode(node);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree });
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
}
|
2019-02-21 09:37:04 +00:00
|
|
|
if (isLoaded && node.path === this.state.path) {
|
2019-02-20 03:54:25 +00:00
|
|
|
if (node.isExpanded) {
|
|
|
|
let tree = treeHelper.collapseNode(this.state.treeData, node);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree });
|
2019-02-20 03:54:25 +00:00
|
|
|
} else {
|
|
|
|
let tree = this.state.treeData.clone();
|
|
|
|
node = tree.getNodeByPath(node.path);
|
|
|
|
tree.expandNode(node);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree });
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-29 09:58:27 +00:00
|
|
|
if (node.path === this.state.path) {
|
2019-02-20 03:54:25 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-07-18 03:58:42 +00:00
|
|
|
if (node.object.isDir()) { // isDir
|
2024-11-23 02:28:46 +00:00
|
|
|
if (this.state.path.includes(PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES) ||
|
|
|
|
this.state.path.includes(PRIVATE_FILE_TYPE.TAGS_PROPERTIES)) {
|
2024-09-05 05:42:07 +00:00
|
|
|
this.isNeedUpdateHistoryState = true;
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
this.showDir(node.path);
|
|
|
|
} else {
|
2024-11-18 04:07:03 +00:00
|
|
|
if (Utils.isFileMetadata(node?.object?.type)) {
|
2024-06-29 09:58:27 +00:00
|
|
|
if (node.path !== this.state.path) {
|
2024-10-18 08:01:00 +00:00
|
|
|
this.showFileMetadata(node.path, node.view_id || '0000', node.view_type || VIEW_TYPE.TABLE);
|
2024-06-29 09:58:27 +00:00
|
|
|
}
|
2024-11-22 09:11:55 +00:00
|
|
|
} else if (Utils.isTags(node?.object?.type)) {
|
|
|
|
if (node.path !== this.state.path) {
|
|
|
|
this.showTagsView(node.path, node.tag_id);
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
} else {
|
2023-07-24 02:47:12 +00:00
|
|
|
let url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(node.path);
|
|
|
|
let dirent = node.object;
|
|
|
|
if (dirent.is_sdoc_revision && dirent.revision_id) {
|
|
|
|
url = siteRoot + 'lib/' + repoID + '/revisions/' + dirent.revision_id + '/';
|
|
|
|
}
|
2020-12-10 03:30:16 +00:00
|
|
|
window.open(url);
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onTreeNodeCollapse = (node) => {
|
|
|
|
let tree = treeHelper.collapseNode(this.state.treeData, node);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onTreeNodeExpanded = (node) => {
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
let tree = this.state.treeData.clone();
|
|
|
|
node = tree.getNodeByPath(node.path);
|
|
|
|
if (!node.isLoaded) {
|
|
|
|
seafileAPI.listDir(repoID, node.path).then(res => {
|
|
|
|
this.addResponseListToNode(res.data.dirent_list, node);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree });
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
tree.expandNode(node);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree });
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
addNodeToTree = (name, parentPath, type) => {
|
|
|
|
let node = this.createTreeNode(name, type);
|
|
|
|
let tree = treeHelper.addNodeToParentByPath(this.state.treeData, node, parentPath);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
renameTreeNode = (path, newName) => {
|
|
|
|
let tree = treeHelper.renameNodeByPath(this.state.treeData, path, newName);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
deleteTreeNode = (path) => {
|
|
|
|
let tree = treeHelper.deleteNodeByPath(this.state.treeData, path);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2020-04-02 07:45:33 +00:00
|
|
|
deleteTreeNodes = (paths) => {
|
|
|
|
let tree = treeHelper.deleteNodeListByPaths(this.state.treeData, paths);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2020-04-02 07:45:33 +00:00
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
moveTreeNode = (nodePath, moveToPath, moveToRepo, nodeName) => {
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
if (repoID !== moveToRepo.repo_id) {
|
|
|
|
let tree = treeHelper.deleteNodeByPath(this.state.treeData, nodePath);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree });
|
2019-02-20 03:54:25 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
let tree = treeHelper.moveNodeByPath(this.state.treeData, nodePath, moveToPath, nodeName);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
copyTreeNode = (nodePath, copyToPath, destRepo, nodeName) => {
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
if (repoID !== destRepo.repo_id) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let tree = treeHelper.copyNodeByPath(this.state.treeData, nodePath, copyToPath, nodeName);
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ treeData: tree });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
createTreeNode(name, type) {
|
|
|
|
let object = this.createDirent(name, type);
|
2024-07-18 03:58:42 +00:00
|
|
|
return new TreeNode({ object });
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
|
2024-03-25 09:22:01 +00:00
|
|
|
createDirent(name, type, size) {
|
2021-09-13 02:37:07 +00:00
|
|
|
// use current dirent parent's permission as it's permission
|
|
|
|
const { userPerm: permission } = this.state;
|
2024-07-11 04:09:57 +00:00
|
|
|
const mtime = new Date().getTime() / 1000;
|
2023-07-22 07:54:25 +00:00
|
|
|
const obj = { name, type, mtime, size, permission };
|
|
|
|
const dirent = new Dirent(obj);
|
2019-02-20 03:54:25 +00:00
|
|
|
return dirent;
|
|
|
|
}
|
|
|
|
|
|
|
|
addResponseListToNode = (list, node) => {
|
|
|
|
node.isLoaded = true;
|
|
|
|
node.isExpanded = true;
|
|
|
|
let direntList = list.map(item => {
|
|
|
|
return new Dirent(item);
|
|
|
|
});
|
|
|
|
direntList = Utils.sortDirents(direntList, 'name', 'asc');
|
|
|
|
|
|
|
|
let nodeList = direntList.map(object => {
|
2024-07-18 03:58:42 +00:00
|
|
|
return new TreeNode({ object });
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
|
|
|
node.addChildren(nodeList);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
getSelectedDirentPaths = () => {
|
2024-10-15 03:59:54 +00:00
|
|
|
return this.state.selectedDirentList.map(selectedDirent => Utils.joinPath(this.state.path, selectedDirent.name));
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
getSelectedDirentNames = () => {
|
2024-10-15 03:59:54 +00:00
|
|
|
return this.state.selectedDirentList.map(selectedDirent => selectedDirent.name);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2024-11-09 02:36:08 +00:00
|
|
|
resetSelected = (node) => {
|
2024-11-23 02:28:46 +00:00
|
|
|
const currentModel = this.state.currentMode;
|
|
|
|
const path = node.path || '';
|
|
|
|
let nextModel = cookie.load('seafile_view_mode') || LIST_MODE;
|
|
|
|
if (currentModel === METADATA_MODE && path.startsWith('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/')) {
|
|
|
|
nextModel = METADATA_MODE;
|
|
|
|
}
|
|
|
|
if (currentModel === TAGS_MODE && path.startsWith('/' + PRIVATE_FILE_TYPE.TAGS_PROPERTIES + '/')) {
|
|
|
|
nextModel = TAGS_MODE;
|
|
|
|
}
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
this.setState({
|
|
|
|
isDirentSelected: false,
|
|
|
|
isAllDirentSelected: false,
|
2024-11-23 02:28:46 +00:00
|
|
|
currentMode: nextModel,
|
2019-02-20 03:54:25 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2024-07-04 08:54:39 +00:00
|
|
|
recalculateSelectedDirents = (unSelectNames, newDirentList) => {
|
2019-12-25 02:16:09 +00:00
|
|
|
let selectedDirentList = this.state.selectedDirentList.slice(0);
|
|
|
|
if (selectedDirentList.length > 0) {
|
|
|
|
selectedDirentList = selectedDirentList.filter(item => {
|
2024-07-04 08:54:39 +00:00
|
|
|
return !unSelectNames.includes(item.name);
|
2019-12-25 02:16:09 +00:00
|
|
|
});
|
|
|
|
}
|
2020-11-02 05:56:35 +00:00
|
|
|
this.setState({
|
2019-12-25 02:16:09 +00:00
|
|
|
selectedDirentList: selectedDirentList,
|
|
|
|
isDirentSelected: selectedDirentList.length > 0,
|
2024-07-27 04:04:33 +00:00
|
|
|
isAllDirentSelected: newDirentList.length ? selectedDirentList.length === newDirentList.length : false,
|
2019-12-25 02:16:09 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-12-25 02:16:09 +00:00
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
onLibDecryptDialog = () => {
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ libNeedDecrypt: false });
|
2019-02-20 03:54:25 +00:00
|
|
|
this.loadDirData(this.state.path);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2020-11-02 05:56:35 +00:00
|
|
|
|
2024-02-07 03:08:41 +00:00
|
|
|
onLibDecryptWhenCopyMove = () => {
|
|
|
|
if (this.state.libNeedDecryptWhenCopy) {
|
2024-02-19 08:12:09 +00:00
|
|
|
if (this.state.copyMoveSingleItem) {
|
2024-07-18 03:58:42 +00:00
|
|
|
this.onCopyItem(this.state.destRepoWhenCopyMove, this.state.srcDirentWhenCopyMove, this.state.destDirentPathWhenCopyMove, this.state.srcNodeParentPathWhenCopyMove);
|
2024-02-19 08:12:09 +00:00
|
|
|
} else {
|
|
|
|
this.onCopyItems(this.state.destRepoWhenCopyMove, this.state.destDirentPathWhenCopyMove);
|
|
|
|
}
|
|
|
|
this.setState({
|
|
|
|
libNeedDecryptWhenCopy: false,
|
|
|
|
copyMoveSingleItem: false,
|
|
|
|
});
|
2024-02-07 03:08:41 +00:00
|
|
|
}
|
|
|
|
if (this.state.libNeedDecryptWhenMove) {
|
2024-02-19 08:12:09 +00:00
|
|
|
if (this.state.copyMoveSingleItem) {
|
|
|
|
this.onMoveItem(this.state.destRepoWhenCopyMove, this.state.srcDirentWhenCopyMove, this.state.destDirentPathWhenCopyMove, this.state.srcNodeParentPathWhenCopyMove);
|
|
|
|
} else {
|
|
|
|
this.onMoveItems(this.state.destRepoWhenCopyMove, this.state.destDirentPathWhenCopyMove);
|
|
|
|
}
|
|
|
|
this.setState({
|
|
|
|
libNeedDecryptWhenMove: false,
|
|
|
|
copyMoveSingleItem: false,
|
|
|
|
});
|
2024-02-07 03:08:41 +00:00
|
|
|
}
|
2024-02-19 08:12:09 +00:00
|
|
|
};
|
2024-02-07 03:08:41 +00:00
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
sortItems = (sortBy, sortOrder) => {
|
2019-04-12 06:30:08 +00:00
|
|
|
cookie.save('seafile-repo-dir-sort-by', sortBy);
|
|
|
|
cookie.save('seafile-repo-dir-sort-order', sortOrder);
|
2019-02-20 03:54:25 +00:00
|
|
|
this.setState({
|
|
|
|
sortBy: sortBy,
|
|
|
|
sortOrder: sortOrder,
|
|
|
|
items: Utils.sortDirents(this.state.direntList, sortBy, sortOrder)
|
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2019-02-21 09:37:04 +00:00
|
|
|
onUploadFile = (e) => {
|
|
|
|
e.nativeEvent.stopImmediatePropagation();
|
|
|
|
this.uploader.onFileUpload();
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-21 09:37:04 +00:00
|
|
|
|
|
|
|
onUploadFolder = (e) => {
|
|
|
|
e.nativeEvent.stopImmediatePropagation();
|
|
|
|
this.uploader.onFolderUpload();
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-21 09:37:04 +00:00
|
|
|
|
2019-03-15 02:10:24 +00:00
|
|
|
onToolbarFileTagChanged = () => {
|
|
|
|
let repoID = this.props.repoID;
|
|
|
|
let filePath = this.state.path;
|
|
|
|
seafileAPI.listFileTags(repoID, filePath).then(res => {
|
|
|
|
let fileTags = res.data.file_tags.map(item => {
|
|
|
|
return new FileTag(item);
|
|
|
|
});
|
|
|
|
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ fileTags: fileTags });
|
2019-07-16 02:01:09 +00:00
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
2019-03-15 02:10:24 +00:00
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-03-15 02:10:24 +00:00
|
|
|
|
2019-03-18 09:32:49 +00:00
|
|
|
unSelectDirent = () => {
|
2024-06-15 00:52:02 +00:00
|
|
|
const direntList = this.state.direntList.map(item => {
|
|
|
|
item.isSelected = false;
|
|
|
|
return item;
|
|
|
|
});
|
2019-03-18 09:32:49 +00:00
|
|
|
this.setState({
|
|
|
|
isDirentSelected: false,
|
2024-06-15 00:52:02 +00:00
|
|
|
direntList,
|
2019-03-18 09:32:49 +00:00
|
|
|
selectedDirentList: []
|
|
|
|
});
|
2024-09-14 09:10:20 +00:00
|
|
|
const dirent = null;
|
2019-03-18 09:32:49 +00:00
|
|
|
this.onDirentSelected(dirent);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-03-18 09:32:49 +00:00
|
|
|
|
2019-03-25 07:23:37 +00:00
|
|
|
onDeleteRepoTag = (deletedTagID) => {
|
|
|
|
let direntList = this.state.direntList.map(dirent => {
|
|
|
|
if (dirent.file_tags) {
|
|
|
|
let fileTags = dirent.file_tags.filter(item => {
|
|
|
|
return item.repo_tag_id !== deletedTagID;
|
|
|
|
});
|
|
|
|
dirent.file_tags = fileTags;
|
|
|
|
}
|
|
|
|
return dirent;
|
|
|
|
});
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ direntList: direntList });
|
2019-03-25 07:23:37 +00:00
|
|
|
this.updateUsedRepoTags();
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-03-25 07:23:37 +00:00
|
|
|
|
2022-08-17 03:20:19 +00:00
|
|
|
|
|
|
|
handleSubmit = (e) => {
|
|
|
|
let options = {
|
|
|
|
'share_type': 'personal',
|
|
|
|
'from': this.state.currentRepoInfo.owner_email
|
|
|
|
};
|
|
|
|
seafileAPI.leaveShareRepo(this.props.repoID, options).then(res => {
|
|
|
|
navigate(siteRoot + 'shared-libs/');
|
|
|
|
}).catch((error) => {
|
|
|
|
let errorMsg = Utils.getErrorMsg(error, true);
|
|
|
|
toaster.danger(errorMsg);
|
|
|
|
});
|
|
|
|
|
|
|
|
e.preventDefault();
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2022-08-17 03:20:19 +00:00
|
|
|
|
2024-05-16 12:14:26 +00:00
|
|
|
toggleTreePanel = () => {
|
|
|
|
this.setState({
|
|
|
|
isTreePanelShown: !this.state.isTreePanelShown
|
2024-05-17 07:10:09 +00:00
|
|
|
}, () => {
|
|
|
|
if (this.state.isTreePanelShown) {
|
|
|
|
this.loadSidePanel(this.state.path);
|
|
|
|
}
|
|
|
|
localStorage.setItem('sf_dir_view_tree_panel_open', String(this.state.isTreePanelShown));
|
2024-05-16 12:14:26 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
render() {
|
2024-11-09 02:36:08 +00:00
|
|
|
const { repoID } = this.props;
|
|
|
|
let { currentRepoInfo, userPerm, isCopyMoveProgressDialogShow, isDeleteFolderDialogOpen, errorMsg,
|
|
|
|
path, usedRepoTags, isDirentSelected } = this.state;
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
if (this.state.libNeedDecrypt) {
|
|
|
|
return (
|
|
|
|
<ModalPortal>
|
2024-11-09 02:36:08 +00:00
|
|
|
<LibDecryptDialog
|
|
|
|
repoID={repoID}
|
|
|
|
onLibDecryptDialog={this.onLibDecryptDialog}
|
|
|
|
/>
|
2019-02-20 03:54:25 +00:00
|
|
|
</ModalPortal>
|
|
|
|
);
|
|
|
|
}
|
2024-02-07 03:08:41 +00:00
|
|
|
if (this.state.libNeedDecryptWhenCopy || this.state.libNeedDecryptWhenMove) {
|
|
|
|
return (
|
|
|
|
<ModalPortal>
|
|
|
|
<LibDecryptDialog
|
|
|
|
repoID={this.state.destRepoWhenCopyMove.repo_id}
|
|
|
|
onLibDecryptDialog={this.onLibDecryptWhenCopyMove}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
);
|
|
|
|
}
|
2024-11-09 02:36:08 +00:00
|
|
|
if (errorMsg) {
|
2022-08-17 03:20:19 +00:00
|
|
|
return (
|
2024-09-13 12:28:20 +00:00
|
|
|
<>
|
2024-11-09 02:36:08 +00:00
|
|
|
<p className="error mt-6 text-center">{errorMsg}</p>
|
2022-08-17 03:20:19 +00:00
|
|
|
<button type="submit" className="btn btn-primary submit" onClick={this.handleSubmit}>{gettext('Leave Share')}</button>
|
2024-09-13 12:28:20 +00:00
|
|
|
</>
|
2022-11-28 01:27:17 +00:00
|
|
|
);
|
2022-08-17 03:20:19 +00:00
|
|
|
}
|
2024-11-09 02:36:08 +00:00
|
|
|
if (!currentRepoInfo) {
|
2019-02-21 09:37:04 +00:00
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
let enableDirPrivateShare = false;
|
2019-06-21 09:54:06 +00:00
|
|
|
let showShareBtn = Utils.isHasPermissionToShare(currentRepoInfo, userPerm);
|
2019-02-27 05:53:36 +00:00
|
|
|
let isRepoOwner = currentRepoInfo.owner_email === username;
|
2019-06-27 03:48:41 +00:00
|
|
|
let isVirtual = currentRepoInfo.is_virtual;
|
|
|
|
let isAdmin = currentRepoInfo.is_admin;
|
|
|
|
if (!isVirtual && (isRepoOwner || isAdmin)) {
|
|
|
|
enableDirPrivateShare = true;
|
|
|
|
}
|
2019-04-24 05:52:09 +00:00
|
|
|
let direntItemsList = this.state.direntList.filter((item, index) => {
|
|
|
|
return index < this.state.itemsShowLength;
|
2019-05-14 02:15:09 +00:00
|
|
|
});
|
2019-04-24 05:52:09 +00:00
|
|
|
|
2021-09-13 02:37:07 +00:00
|
|
|
let canUpload = true;
|
|
|
|
const { isCustomPermission, customPermission } = Utils.getUserPermission(userPerm);
|
|
|
|
if (isCustomPermission) {
|
|
|
|
const { upload } = customPermission.permission;
|
|
|
|
canUpload = upload;
|
|
|
|
}
|
|
|
|
|
2024-09-13 12:28:20 +00:00
|
|
|
const isDesktop = Utils.isDesktop();
|
|
|
|
let isRepoInfoBarShow = false;
|
|
|
|
if (path === '/') {
|
|
|
|
if (isDesktop && usedRepoTags.length !== 0) {
|
|
|
|
isRepoInfoBarShow = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
return (
|
2024-11-22 09:11:55 +00:00
|
|
|
<MetadataStatusProvider repoID={repoID} currentRepoInfo={currentRepoInfo} hideMetadataView={this.hideMetadataView}>
|
|
|
|
<TagsProvider repoID={repoID} repoInfo={currentRepoInfo} selectTagsView={this.onTreeNodeClick}>
|
|
|
|
<MetadataProvider repoID={repoID} repoInfo={currentRepoInfo} selectMetadataView={this.onTreeNodeClick} hideMetadataView={this.hideMetadataView} >
|
|
|
|
<CollaboratorsProvider repoID={repoID}>
|
|
|
|
<div className="main-panel-center flex-row">
|
|
|
|
<div className="cur-view-container">
|
|
|
|
{this.state.currentRepoInfo.status === 'read-only' &&
|
|
|
|
<div className="readonly-tip-message">
|
|
|
|
{gettext('This library has been set to read-only by admin and cannot be updated.')}
|
|
|
|
</div>
|
2024-09-13 12:28:20 +00:00
|
|
|
}
|
2024-11-22 09:11:55 +00:00
|
|
|
<div className="cur-view-path lib-cur-view-path">
|
|
|
|
<div className={classnames(
|
|
|
|
'cur-view-path-left', {
|
|
|
|
'w-100': !isDesktop,
|
|
|
|
'animation-children': isDirentSelected
|
|
|
|
})}>
|
|
|
|
{isDirentSelected ?
|
|
|
|
<SelectedDirentsToolbar
|
|
|
|
repoID={this.props.repoID}
|
|
|
|
path={this.state.path}
|
|
|
|
userPerm={userPerm}
|
|
|
|
repoEncrypted={this.state.repoEncrypted}
|
|
|
|
repoTags={this.state.repoTags}
|
|
|
|
selectedDirentList={this.state.selectedDirentList}
|
|
|
|
direntList={direntItemsList}
|
|
|
|
onItemsMove={this.onMoveItems}
|
|
|
|
onItemsCopy={this.onCopyItems}
|
|
|
|
onItemsDelete={this.onDeleteItems}
|
|
|
|
onItemRename={this.onMainPanelItemRename}
|
|
|
|
isRepoOwner={isRepoOwner}
|
|
|
|
currentRepoInfo={this.state.currentRepoInfo}
|
|
|
|
enableDirPrivateShare={enableDirPrivateShare}
|
|
|
|
updateDirent={this.updateDirent}
|
|
|
|
unSelectDirent={this.unSelectDirent}
|
|
|
|
onFilesTagChanged={this.onFileTagChanged}
|
|
|
|
showShareBtn={showShareBtn}
|
|
|
|
isGroupOwnedRepo={this.state.isGroupOwnedRepo}
|
|
|
|
showDirentDetail={this.showDirentDetail}
|
|
|
|
currentMode={this.state.currentMode}
|
|
|
|
switchViewMode={this.switchViewMode}
|
|
|
|
onItemConvert={this.onConvertItem}
|
|
|
|
onAddFolder={this.onAddFolder}
|
|
|
|
/>
|
|
|
|
:
|
|
|
|
<CurDirPath
|
|
|
|
currentRepoInfo={this.state.currentRepoInfo}
|
|
|
|
repoID={this.props.repoID}
|
|
|
|
repoName={this.state.currentRepoInfo.repo_name}
|
|
|
|
repoEncrypted={this.state.repoEncrypted}
|
|
|
|
isGroupOwnedRepo={this.state.isGroupOwnedRepo}
|
|
|
|
pathPrefix={this.props.pathPrefix}
|
|
|
|
currentPath={this.state.path}
|
|
|
|
userPerm={userPerm}
|
|
|
|
onTabNavClick={this.props.onTabNavClick}
|
|
|
|
onPathClick={this.onMainNavBarClick}
|
|
|
|
fileTags={this.state.fileTags}
|
|
|
|
direntList={direntItemsList}
|
|
|
|
sortBy={this.state.sortBy}
|
|
|
|
sortOrder={this.state.sortOrder}
|
|
|
|
sortItems={this.sortItems}
|
|
|
|
toggleTreePanel={this.toggleTreePanel}
|
|
|
|
enableDirPrivateShare={enableDirPrivateShare}
|
|
|
|
showShareBtn={showShareBtn}
|
|
|
|
onAddFolder={this.onAddFolder}
|
|
|
|
onAddFile={this.onAddFile}
|
|
|
|
onUploadFile={this.onUploadFile}
|
|
|
|
onUploadFolder={this.onUploadFolder}
|
|
|
|
fullDirentList={this.state.direntList}
|
|
|
|
filePermission={this.state.filePermission}
|
|
|
|
onFileTagChanged={this.onToolbarFileTagChanged}
|
|
|
|
repoTags={this.state.repoTags}
|
|
|
|
onItemMove={this.onMoveItem}
|
|
|
|
isDesktop={isDesktop}
|
|
|
|
loadDirentList={this.loadDirentList}
|
|
|
|
onAddFolderNode={this.onAddFolder}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
</div>
|
|
|
|
{isDesktop &&
|
|
|
|
<div className="cur-view-path-right py-1">
|
|
|
|
<DirTool
|
|
|
|
repoID={this.props.repoID}
|
|
|
|
repoName={this.state.currentRepoInfo.repo_name}
|
|
|
|
userPerm={userPerm}
|
|
|
|
currentPath={path}
|
|
|
|
updateUsedRepoTags={this.updateUsedRepoTags}
|
|
|
|
onDeleteRepoTag={this.onDeleteRepoTag}
|
|
|
|
currentMode={this.state.currentMode}
|
|
|
|
switchViewMode={this.switchViewMode}
|
|
|
|
isCustomPermission={isCustomPermission}
|
|
|
|
sortBy={this.state.sortBy}
|
|
|
|
sortOrder={this.state.sortOrder}
|
|
|
|
sortItems={this.sortItems}
|
|
|
|
viewId={this.state.viewId}
|
|
|
|
viewType={this.props.viewType}
|
|
|
|
onCloseDetail={this.closeDirentDetail}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
}
|
2024-09-13 12:28:20 +00:00
|
|
|
</div>
|
2024-11-22 09:11:55 +00:00
|
|
|
<div className='cur-view-content lib-content-container' onScroll={this.onItemsScroll}>
|
|
|
|
{this.state.pathExist ?
|
|
|
|
<DirColumnView
|
|
|
|
isSidePanelFolded={this.props.isSidePanelFolded}
|
|
|
|
isTreePanelShown={this.state.isTreePanelShown}
|
|
|
|
isDirentDetailShow={this.state.isDirentDetailShow}
|
|
|
|
currentMode={this.state.currentMode}
|
|
|
|
path={this.state.path}
|
|
|
|
repoID={this.props.repoID}
|
|
|
|
currentRepoInfo={this.state.currentRepoInfo}
|
|
|
|
isGroupOwnedRepo={this.state.isGroupOwnedRepo}
|
|
|
|
userPerm={userPerm}
|
|
|
|
enableDirPrivateShare={enableDirPrivateShare}
|
|
|
|
isTreeDataLoading={this.state.isTreeDataLoading}
|
|
|
|
treeData={this.state.treeData}
|
|
|
|
currentNode={this.state.currentNode}
|
|
|
|
onNodeClick={this.onTreeNodeClick}
|
|
|
|
onNodeCollapse={this.onTreeNodeCollapse}
|
|
|
|
onNodeExpanded={this.onTreeNodeExpanded}
|
|
|
|
onAddFolderNode={this.onAddFolder}
|
|
|
|
onAddFileNode={this.onAddFile}
|
|
|
|
onRenameNode={this.onRenameTreeNode}
|
|
|
|
onDeleteNode={this.onDeleteTreeNode}
|
|
|
|
isViewFile={this.state.isViewFile}
|
|
|
|
isFileLoading={this.state.isFileLoading}
|
|
|
|
filePermission={this.state.filePermission}
|
|
|
|
content={this.state.content}
|
|
|
|
viewId={this.state.viewId}
|
|
|
|
tagId={this.state.tagId}
|
|
|
|
lastModified={this.state.lastModified}
|
|
|
|
latestContributor={this.state.latestContributor}
|
|
|
|
onLinkClick={this.onLinkClick}
|
|
|
|
isRepoInfoBarShow={isRepoInfoBarShow}
|
|
|
|
repoTags={this.state.repoTags}
|
|
|
|
usedRepoTags={this.state.usedRepoTags}
|
|
|
|
updateUsedRepoTags={this.updateUsedRepoTags}
|
|
|
|
isDirentListLoading={this.state.isDirentListLoading}
|
|
|
|
direntList={direntItemsList}
|
|
|
|
fullDirentList={this.state.direntList}
|
|
|
|
sortBy={this.state.sortBy}
|
|
|
|
sortOrder={this.state.sortOrder}
|
|
|
|
sortItems={this.sortItems}
|
|
|
|
onAddFolder={this.onAddFolder}
|
|
|
|
onAddFile={this.onAddFile}
|
|
|
|
onItemClick={this.onItemClick}
|
|
|
|
onItemSelected={this.onDirentSelected}
|
|
|
|
onItemDelete={this.onMainPanelItemDelete}
|
|
|
|
onItemRename={this.onMainPanelItemRename}
|
|
|
|
deleteFilesCallback={this.deleteItemsAjaxCallback}
|
|
|
|
renameFileCallback={this.renameItemAjaxCallback}
|
|
|
|
onItemMove={this.onMoveItem}
|
|
|
|
onItemCopy={this.onCopyItem}
|
|
|
|
onItemConvert={this.onConvertItem}
|
|
|
|
onDirentClick={this.onDirentClick}
|
|
|
|
updateDirent={this.updateDirent}
|
|
|
|
isAllItemSelected={this.state.isAllDirentSelected}
|
|
|
|
onAllItemSelected={this.onAllDirentSelected}
|
|
|
|
selectedDirentList={this.state.selectedDirentList}
|
|
|
|
onSelectedDirentListUpdate={this.onSelectedDirentListUpdate}
|
|
|
|
onItemsMove={this.onMoveItems}
|
|
|
|
onItemsCopy={this.onCopyItems}
|
|
|
|
onItemsDelete={this.onDeleteItems}
|
|
|
|
onFileTagChanged={this.onFileTagChanged}
|
|
|
|
showDirentDetail={this.showDirentDetail}
|
|
|
|
onItemsScroll={this.onItemsScroll}
|
|
|
|
eventBus={this.props.eventBus}
|
|
|
|
updateCurrentDirent={this.updateCurrentDirent}
|
|
|
|
closeDirentDetail={this.closeDirentDetail}
|
|
|
|
/>
|
|
|
|
:
|
|
|
|
<div className="message err-tip">{gettext('Folder does not exist.')}</div>
|
|
|
|
}
|
|
|
|
{this.state.isDirentDetailShow && (
|
|
|
|
<Detail
|
|
|
|
path={this.state.path}
|
|
|
|
repoID={this.props.repoID}
|
|
|
|
currentRepoInfo={this.state.currentRepoInfo}
|
|
|
|
dirent={this.state.currentDirent}
|
|
|
|
repoTags={this.state.repoTags}
|
|
|
|
fileTags={this.state.isViewFile ? this.state.fileTags : []}
|
|
|
|
onFileTagChanged={this.onFileTagChanged}
|
|
|
|
onClose={this.closeDirentDetail}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
2024-11-23 02:28:46 +00:00
|
|
|
{canUpload && this.state.pathExist && !this.state.isViewFile && ![METADATA_MODE, TAGS_MODE].includes(this.state.currentMode) && (
|
2024-11-22 09:11:55 +00:00
|
|
|
<FileUploader
|
|
|
|
ref={uploader => this.uploader = uploader}
|
|
|
|
dragAndDrop={true}
|
2024-09-13 12:28:20 +00:00
|
|
|
path={this.state.path}
|
|
|
|
repoID={this.props.repoID}
|
2024-11-22 09:11:55 +00:00
|
|
|
direntList={this.state.direntList}
|
|
|
|
onFileUploadSuccess={this.onFileUploadSuccess}
|
|
|
|
isCustomPermission={isCustomPermission}
|
2024-09-13 12:28:20 +00:00
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</div>
|
2024-11-22 09:11:55 +00:00
|
|
|
{isCopyMoveProgressDialogShow && (
|
|
|
|
<CopyMoveDirentProgressDialog
|
|
|
|
type={this.state.asyncOperationType}
|
|
|
|
asyncOperatedFilesLength={this.state.asyncOperatedFilesLength}
|
|
|
|
asyncOperationProgress={this.state.asyncOperationProgress}
|
|
|
|
toggleDialog={this.onMoveProgressDialogToggle}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
{isDeleteFolderDialogOpen && (
|
|
|
|
<DeleteFolderDialog
|
|
|
|
repoID={this.props.repoID}
|
|
|
|
path={this.state.folderToDelete}
|
|
|
|
deleteFolder={this.deleteFolder}
|
|
|
|
toggleDialog={this.toggleDeleteFolderDialog}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
<MediaQuery query="(max-width: 767.8px)">
|
|
|
|
<Modal zIndex="1030" isOpen={!isDesktop && this.state.isTreePanelShown} toggle={this.toggleTreePanel} contentClassName="d-none"></Modal>
|
|
|
|
</MediaQuery>
|
|
|
|
</CollaboratorsProvider>
|
|
|
|
</MetadataProvider>
|
|
|
|
</TagsProvider>
|
|
|
|
</MetadataStatusProvider>
|
2019-02-20 03:54:25 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LibContentView.propTypes = propTypes;
|
|
|
|
|
2019-02-21 09:37:04 +00:00
|
|
|
export default LibContentView;
|