mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-06 01:12:03 +00:00
fix: metadata status toggle (#6530)
* fix: metadata status toggle * feat: optimize code --------- Co-authored-by: 杨国璇 <ygx@Hello-word.local>
This commit is contained in:
@@ -23,7 +23,7 @@ const propTypes = {
|
|||||||
sortBy: PropTypes.string,
|
sortBy: PropTypes.string,
|
||||||
sortOrder: PropTypes.string,
|
sortOrder: PropTypes.string,
|
||||||
sortItems: PropTypes.func,
|
sortItems: PropTypes.func,
|
||||||
metadataViewId: PropTypes.string,
|
viewId: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirTool extends React.Component {
|
class DirTool extends React.Component {
|
||||||
@@ -100,7 +100,7 @@ class DirTool extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
const menuItems = this.getMenu();
|
const menuItems = this.getMenu();
|
||||||
const { isDropdownMenuOpen } = this.state;
|
const { isDropdownMenuOpen } = this.state;
|
||||||
const { repoID, currentMode, currentPath, sortBy, sortOrder, metadataViewId } = this.props;
|
const { repoID, currentMode, currentPath, sortBy, sortOrder, viewId } = this.props;
|
||||||
const propertiesText = TextTranslation.PROPERTIES.value;
|
const propertiesText = TextTranslation.PROPERTIES.value;
|
||||||
const isFileExtended = currentPath.startsWith('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/');
|
const isFileExtended = currentPath.startsWith('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/');
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ class DirTool extends React.Component {
|
|||||||
if (isFileExtended) {
|
if (isFileExtended) {
|
||||||
return (
|
return (
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
<MetadataViewToolBar metadataViewId={metadataViewId} />
|
<MetadataViewToolBar viewId={viewId} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -38,7 +38,7 @@ const propTypes = {
|
|||||||
filePermission: PropTypes.string,
|
filePermission: PropTypes.string,
|
||||||
repoTags: PropTypes.array.isRequired,
|
repoTags: PropTypes.array.isRequired,
|
||||||
onFileTagChanged: PropTypes.func.isRequired,
|
onFileTagChanged: PropTypes.func.isRequired,
|
||||||
metadataViewId: PropTypes.string,
|
viewId: PropTypes.string,
|
||||||
onItemMove: PropTypes.func.isRequired,
|
onItemMove: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ class CurDirPath extends React.Component {
|
|||||||
sortBy={this.props.sortBy}
|
sortBy={this.props.sortBy}
|
||||||
sortOrder={this.props.sortOrder}
|
sortOrder={this.props.sortOrder}
|
||||||
sortItems={this.props.sortItems}
|
sortItems={this.props.sortItems}
|
||||||
metadataViewId={this.props.metadataViewId}
|
viewId={this.props.viewId}
|
||||||
/>}
|
/>}
|
||||||
{!isDesktop && this.props.direntList.length > 0 &&
|
{!isDesktop && this.props.direntList.length > 0 &&
|
||||||
<span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>}
|
<span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>}
|
||||||
|
@@ -13,7 +13,7 @@ const propTypes = {
|
|||||||
isFileLoadedErr: PropTypes.bool.isRequired,
|
isFileLoadedErr: PropTypes.bool.isRequired,
|
||||||
filePermission: PropTypes.string,
|
filePermission: PropTypes.string,
|
||||||
content: PropTypes.string,
|
content: PropTypes.string,
|
||||||
metadataViewId: PropTypes.string,
|
viewId: PropTypes.string,
|
||||||
lastModified: PropTypes.string,
|
lastModified: PropTypes.string,
|
||||||
latestContributor: PropTypes.string,
|
latestContributor: PropTypes.string,
|
||||||
onLinkClick: PropTypes.func.isRequired,
|
onLinkClick: PropTypes.func.isRequired,
|
||||||
@@ -53,9 +53,9 @@ class DirColumnFile extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.content === '__sf-metadata') {
|
if (this.props.content === '__sf-metadata') {
|
||||||
const { repoID, currentRepoInfo, metadataViewId } = this.props;
|
const { repoID, currentRepoInfo, viewId } = this.props;
|
||||||
|
|
||||||
return (<SeafileMetadata mediaUrl={mediaUrl} repoID={repoID} repoInfo={currentRepoInfo} viewID={metadataViewId} />);
|
return (<SeafileMetadata mediaUrl={mediaUrl} repoID={repoID} repoInfo={currentRepoInfo} viewID={viewId} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -312,12 +312,7 @@ class DirColumnNav extends React.Component {
|
|||||||
getMenuContainerSize={getMenuContainerSize}
|
getMenuContainerSize={getMenuContainerSize}
|
||||||
/>
|
/>
|
||||||
</TreeSection>
|
</TreeSection>
|
||||||
<DirViews
|
<DirViews repoID={repoID} currentPath={currentPath} userPerm={userPerm} />
|
||||||
repoID={repoID}
|
|
||||||
currentPath={currentPath}
|
|
||||||
userPerm={userPerm}
|
|
||||||
onNodeClick={this.onNodeClick}
|
|
||||||
/>
|
|
||||||
<DirOthers
|
<DirOthers
|
||||||
repoID={repoID}
|
repoID={repoID}
|
||||||
userPerm={userPerm}
|
userPerm={userPerm}
|
||||||
|
@@ -37,7 +37,7 @@ const propTypes = {
|
|||||||
hash: PropTypes.string,
|
hash: PropTypes.string,
|
||||||
filePermission: PropTypes.string,
|
filePermission: PropTypes.string,
|
||||||
content: PropTypes.string,
|
content: PropTypes.string,
|
||||||
metadataViewId: PropTypes.string,
|
viewId: PropTypes.string,
|
||||||
lastModified: PropTypes.string,
|
lastModified: PropTypes.string,
|
||||||
latestContributor: PropTypes.string,
|
latestContributor: PropTypes.string,
|
||||||
onLinkClick: PropTypes.func.isRequired,
|
onLinkClick: PropTypes.func.isRequired,
|
||||||
@@ -194,7 +194,7 @@ class DirColumnView extends React.Component {
|
|||||||
isFileLoadedErr={this.props.isFileLoadedErr}
|
isFileLoadedErr={this.props.isFileLoadedErr}
|
||||||
filePermission={this.props.filePermission}
|
filePermission={this.props.filePermission}
|
||||||
content={this.props.content}
|
content={this.props.content}
|
||||||
metadataViewId={this.props.metadataViewId}
|
viewId={this.props.viewId}
|
||||||
currentRepoInfo={this.props.currentRepoInfo}
|
currentRepoInfo={this.props.currentRepoInfo}
|
||||||
lastModified={this.props.lastModified}
|
lastModified={this.props.lastModified}
|
||||||
latestContributor={this.props.latestContributor}
|
latestContributor={this.props.latestContributor}
|
||||||
|
@@ -2,16 +2,16 @@ import React, { useCallback, useMemo, useState } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
import TreeSection from '../tree-section';
|
import TreeSection from '../tree-section';
|
||||||
import { MetadataStatusManagementDialog, MetadataTreeView, useMetadataStatus } from '../../metadata';
|
import { MetadataStatusManagementDialog, MetadataTreeView, useMetadata } from '../../metadata';
|
||||||
|
|
||||||
const DirViews = ({ userPerm, repoID, currentPath, onNodeClick }) => {
|
const DirViews = ({ userPerm, repoID, currentPath }) => {
|
||||||
const enableMetadataManagement = useMemo(() => {
|
const enableMetadataManagement = useMemo(() => {
|
||||||
return window.app.pageOptions.enableMetadataManagement;
|
return window.app.pageOptions.enableMetadataManagement;
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [window.app.pageOptions.enableMetadataManagement]);
|
}, [window.app.pageOptions.enableMetadataManagement]);
|
||||||
|
|
||||||
const [showMetadataStatusManagementDialog, setShowMetadataStatusManagementDialog] = useState(false);
|
const [showMetadataStatusManagementDialog, setShowMetadataStatusManagementDialog] = useState(false);
|
||||||
const { enableExtendedProperties, updateEnableExtendedProperties } = useMetadataStatus();
|
const { enableMetadata, updateEnableMetadata, navigation } = useMetadata();
|
||||||
const moreOperations = useMemo(() => {
|
const moreOperations = useMemo(() => {
|
||||||
if (!enableMetadataManagement) return [];
|
if (!enableMetadataManagement) return [];
|
||||||
if (userPerm !== 'rw' && userPerm !== 'admin') return [];
|
if (userPerm !== 'rw' && userPerm !== 'admin') return [];
|
||||||
@@ -32,8 +32,8 @@ const DirViews = ({ userPerm, repoID, currentPath, onNodeClick }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toggleMetadataStatus = useCallback((value) => {
|
const toggleMetadataStatus = useCallback((value) => {
|
||||||
updateEnableExtendedProperties(value);
|
updateEnableMetadata(value);
|
||||||
}, [updateEnableExtendedProperties]);
|
}, [updateEnableMetadata]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -43,11 +43,13 @@ const DirViews = ({ userPerm, repoID, currentPath, onNodeClick }) => {
|
|||||||
moreOperations={moreOperations}
|
moreOperations={moreOperations}
|
||||||
moreOperationClick={moreOperationClick}
|
moreOperationClick={moreOperationClick}
|
||||||
>
|
>
|
||||||
{enableExtendedProperties && (<MetadataTreeView userPerm={userPerm} repoID={repoID} currentPath={currentPath} onNodeClick={onNodeClick} />)}
|
{enableMetadata && Array.isArray(navigation) && navigation.length > 0 && (
|
||||||
|
<MetadataTreeView userPerm={userPerm} currentPath={currentPath} />
|
||||||
|
)}
|
||||||
</TreeSection>
|
</TreeSection>
|
||||||
{showMetadataStatusManagementDialog && (
|
{showMetadataStatusManagementDialog && (
|
||||||
<MetadataStatusManagementDialog
|
<MetadataStatusManagementDialog
|
||||||
value={enableExtendedProperties}
|
value={enableMetadata}
|
||||||
repoID={repoID}
|
repoID={repoID}
|
||||||
toggle={closeMetadataManagementDialog}
|
toggle={closeMetadataManagementDialog}
|
||||||
submit={toggleMetadataStatus}
|
submit={toggleMetadataStatus}
|
||||||
|
@@ -4,16 +4,16 @@ import { getDirentPath } from './utils';
|
|||||||
import DetailItem from '../detail-item';
|
import DetailItem from '../detail-item';
|
||||||
import { CellType } from '../../../metadata/metadata-view/_basic';
|
import { CellType } from '../../../metadata/metadata-view/_basic';
|
||||||
import { gettext } from '../../../utils/constants';
|
import { gettext } from '../../../utils/constants';
|
||||||
import { MetadataDetails, useMetadataStatus } from '../../../metadata';
|
import { MetadataDetails, useMetadata } from '../../../metadata';
|
||||||
|
|
||||||
const DirDetails = ({ repoID, repoInfo, dirent, path, direntDetail, ...params }) => {
|
const DirDetails = ({ repoID, repoInfo, dirent, path, direntDetail, ...params }) => {
|
||||||
const direntPath = useMemo(() => getDirentPath(dirent, path), [dirent, path]);
|
const direntPath = useMemo(() => getDirentPath(dirent, path), [dirent, path]);
|
||||||
const { enableExtendedProperties } = useMetadataStatus();
|
const { enableMetadata } = useMetadata();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DetailItem field={{ type: CellType.MTIME, name: gettext('Last modified time') }} value={direntDetail.mtime} />
|
<DetailItem field={{ type: CellType.MTIME, name: gettext('Last modified time') }} value={direntDetail.mtime} />
|
||||||
{window.app.pageOptions.enableMetadataManagement && enableExtendedProperties && (
|
{window.app.pageOptions.enableMetadataManagement && enableMetadata && (
|
||||||
<MetadataDetails repoID={repoID} filePath={direntPath} direntType="dir" { ...params } />
|
<MetadataDetails repoID={repoID} filePath={direntPath} direntType="dir" { ...params } />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@@ -8,12 +8,12 @@ import { gettext } from '../../../utils/constants';
|
|||||||
import EditFileTagPopover from '../../popover/edit-filetag-popover';
|
import EditFileTagPopover from '../../popover/edit-filetag-popover';
|
||||||
import FileTagList from '../../file-tag-list';
|
import FileTagList from '../../file-tag-list';
|
||||||
import { Utils } from '../../../utils/utils';
|
import { Utils } from '../../../utils/utils';
|
||||||
import { MetadataDetails, useMetadataStatus } from '../../../metadata';
|
import { MetadataDetails, useMetadata } from '../../../metadata';
|
||||||
import ObjectUtils from '../../../metadata/metadata-view/utils/object-utils';
|
import ObjectUtils from '../../../metadata/metadata-view/utils/object-utils';
|
||||||
|
|
||||||
const FileDetails = React.memo(({ repoID, repoInfo, dirent, path, direntDetail, onFileTagChanged, repoTags, fileTagList, ...params }) => {
|
const FileDetails = React.memo(({ repoID, repoInfo, dirent, path, direntDetail, onFileTagChanged, repoTags, fileTagList, ...params }) => {
|
||||||
const [isEditFileTagShow, setEditFileTagShow] = useState(false);
|
const [isEditFileTagShow, setEditFileTagShow] = useState(false);
|
||||||
const { enableExtendedProperties } = useMetadataStatus();
|
const { enableMetadata } = useMetadata();
|
||||||
|
|
||||||
const direntPath = useMemo(() => getDirentPath(dirent, path), [dirent, path]);
|
const direntPath = useMemo(() => getDirentPath(dirent, path), [dirent, path]);
|
||||||
const tagListTitleID = useMemo(() => `detail-list-view-tags-${uuidV4()}`, []);
|
const tagListTitleID = useMemo(() => `detail-list-view-tags-${uuidV4()}`, []);
|
||||||
@@ -36,7 +36,7 @@ const FileDetails = React.memo(({ repoID, repoInfo, dirent, path, direntDetail,
|
|||||||
avatar_url: direntDetail.last_modifier_avatar,
|
avatar_url: direntDetail.last_modifier_avatar,
|
||||||
}]} />
|
}]} />
|
||||||
<DetailItem field={{ type: CellType.MTIME, name: gettext('Last modified time') }} value={direntDetail.last_modified} />
|
<DetailItem field={{ type: CellType.MTIME, name: gettext('Last modified time') }} value={direntDetail.last_modified} />
|
||||||
{!window.app.pageOptions.enableMetadataManagement && enableExtendedProperties && (
|
{!window.app.pageOptions.enableMetadataManagement && enableMetadata && (
|
||||||
<DetailItem field={{ type: CellType.SINGLE_SELECT, name: gettext('Tags') }} valueId={tagListTitleID} valueClick={onEditFileTagToggle} >
|
<DetailItem field={{ type: CellType.SINGLE_SELECT, name: gettext('Tags') }} valueId={tagListTitleID} valueClick={onEditFileTagToggle} >
|
||||||
{Array.isArray(fileTagList) && fileTagList.length > 0 ? (
|
{Array.isArray(fileTagList) && fileTagList.length > 0 ? (
|
||||||
<FileTagList fileTagList={fileTagList} />
|
<FileTagList fileTagList={fileTagList} />
|
||||||
|
@@ -1 +1 @@
|
|||||||
export { MetadataStatusProvider, useMetadataStatus } from './metadata-status';
|
export { MetadataProvider, useMetadata } from './metadata';
|
||||||
|
@@ -1,50 +0,0 @@
|
|||||||
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
||||||
import metadataAPI from '../api';
|
|
||||||
import { Utils } from '../../utils/utils';
|
|
||||||
import toaster from '../../components/toast';
|
|
||||||
|
|
||||||
const MetadataStatusContext = React.createContext(null);
|
|
||||||
|
|
||||||
export const MetadataStatusProvider = ({ repoID, children }) => {
|
|
||||||
const enableMetadataManagement = useMemo(() => {
|
|
||||||
return window.app.pageOptions.enableMetadataManagement;
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [window.app.pageOptions.enableMetadataManagement]);
|
|
||||||
|
|
||||||
|
|
||||||
const [enableExtendedProperties, setEnableExtendedProperties] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!enableMetadataManagement) {
|
|
||||||
setEnableExtendedProperties(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
metadataAPI.getMetadataStatus(repoID).then(res => {
|
|
||||||
setEnableExtendedProperties(res.data.enabled);
|
|
||||||
}).catch(error => {
|
|
||||||
const errorMsg = Utils.getErrorMsg(error, true);
|
|
||||||
toaster.danger(errorMsg);
|
|
||||||
setEnableExtendedProperties(false);
|
|
||||||
});
|
|
||||||
}, [repoID, enableMetadataManagement]);
|
|
||||||
|
|
||||||
const updateEnableExtendedProperties = useCallback((newValue) => {
|
|
||||||
if (newValue === enableExtendedProperties) return;
|
|
||||||
setEnableExtendedProperties(newValue);
|
|
||||||
}, [enableExtendedProperties]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MetadataStatusContext.Provider value={{ enableExtendedProperties, updateEnableExtendedProperties }}>
|
|
||||||
{children}
|
|
||||||
</MetadataStatusContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useMetadataStatus = () => {
|
|
||||||
const context = useContext(MetadataStatusContext);
|
|
||||||
if (!context) {
|
|
||||||
throw new Error('\'MetadataStatusContext\' is null');
|
|
||||||
}
|
|
||||||
const { enableExtendedProperties, updateEnableExtendedProperties } = context;
|
|
||||||
return { enableExtendedProperties, updateEnableExtendedProperties };
|
|
||||||
};
|
|
179
frontend/src/metadata/hooks/metadata.js
Normal file
179
frontend/src/metadata/hooks/metadata.js
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import metadataAPI from '../api';
|
||||||
|
import { Utils } from '../../utils/utils';
|
||||||
|
import toaster from '../../components/toast';
|
||||||
|
import { gettext } from '../../utils/constants';
|
||||||
|
import { PRIVATE_FILE_TYPE } from '../../constants';
|
||||||
|
|
||||||
|
// This hook provides content related to seahub interaction, such as whether to enable extended attributes, views data, etc.
|
||||||
|
const MetadataContext = React.createContext(null);
|
||||||
|
|
||||||
|
export const MetadataProvider = ({ repoID, hideMetadataView, selectMetadataView, renameMetadataView, children }) => {
|
||||||
|
const enableMetadataManagement = useMemo(() => {
|
||||||
|
return window.app.pageOptions.enableMetadataManagement;
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [window.app.pageOptions.enableMetadataManagement]);
|
||||||
|
|
||||||
|
const [enableMetadata, setEnableExtendedProperties] = useState(false);
|
||||||
|
const [showFirstView, setShowFirstView] = useState(false);
|
||||||
|
const [navigation, setNavigation] = useState([]);
|
||||||
|
const viewsMap = useRef({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!enableMetadataManagement) {
|
||||||
|
setEnableExtendedProperties(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
metadataAPI.getMetadataStatus(repoID).then(res => {
|
||||||
|
setEnableExtendedProperties(res.data.enabled);
|
||||||
|
}).catch(error => {
|
||||||
|
const errorMsg = Utils.getErrorMsg(error, true);
|
||||||
|
toaster.danger(errorMsg);
|
||||||
|
setEnableExtendedProperties(false);
|
||||||
|
});
|
||||||
|
}, [repoID, enableMetadataManagement]);
|
||||||
|
|
||||||
|
const updateEnableMetadata = useCallback((newValue) => {
|
||||||
|
if (newValue === enableMetadata) return;
|
||||||
|
if (!newValue) {
|
||||||
|
hideMetadataView && hideMetadataView();
|
||||||
|
} else {
|
||||||
|
setShowFirstView(true);
|
||||||
|
}
|
||||||
|
setEnableExtendedProperties(newValue);
|
||||||
|
}, [enableMetadata, hideMetadataView]);
|
||||||
|
|
||||||
|
// views
|
||||||
|
useEffect(() => {
|
||||||
|
if (enableMetadata) {
|
||||||
|
metadataAPI.listViews(repoID).then(res => {
|
||||||
|
const { navigation, views } = res.data;
|
||||||
|
if (Array.isArray(views)) {
|
||||||
|
views.forEach(view => {
|
||||||
|
viewsMap.current[view._id] = view;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setNavigation(navigation);
|
||||||
|
}).catch(error => {
|
||||||
|
const errorMsg = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errorMsg);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If attribute extension is turned off, unmark the URL
|
||||||
|
const { origin, pathname, search } = window.location;
|
||||||
|
const urlParams = new URLSearchParams(search);
|
||||||
|
const viewID = urlParams.get('view');
|
||||||
|
if (viewID) {
|
||||||
|
const url = `${origin}${pathname}`;
|
||||||
|
window.history.pushState({ url: url, path: '' }, '', url);
|
||||||
|
}
|
||||||
|
viewsMap.current = {};
|
||||||
|
setNavigation([]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [repoID, enableMetadata]);
|
||||||
|
|
||||||
|
const selectView = useCallback((view, isSelected) => {
|
||||||
|
if (isSelected) return;
|
||||||
|
const node = {
|
||||||
|
children: [],
|
||||||
|
path: '/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/' + view.name,
|
||||||
|
isExpanded: false,
|
||||||
|
isLoaded: true,
|
||||||
|
isPreload: true,
|
||||||
|
object: {
|
||||||
|
file_tags: [],
|
||||||
|
id: PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES,
|
||||||
|
name: gettext('File extended properties'),
|
||||||
|
type: PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES,
|
||||||
|
isDir: () => false,
|
||||||
|
},
|
||||||
|
parentNode: {},
|
||||||
|
key: repoID,
|
||||||
|
view_id: view._id,
|
||||||
|
};
|
||||||
|
selectMetadataView(node);
|
||||||
|
setShowFirstView(false);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [repoID, selectMetadataView]);
|
||||||
|
|
||||||
|
const addView = useCallback((name, successCallback, failCallback) => {
|
||||||
|
metadataAPI.addView(repoID, name).then(res => {
|
||||||
|
const view = res.data.view;
|
||||||
|
let newNavigation = navigation.slice(0);
|
||||||
|
newNavigation.push({ _id: view._id, type: 'view' });
|
||||||
|
viewsMap.current[view._id] = view;
|
||||||
|
setNavigation(newNavigation);
|
||||||
|
selectView(view);
|
||||||
|
successCallback && successCallback();
|
||||||
|
}).catch(error => {
|
||||||
|
failCallback && failCallback(error);
|
||||||
|
});
|
||||||
|
}, [navigation, repoID, viewsMap, selectView]);
|
||||||
|
|
||||||
|
const deleteView = useCallback((viewId, isSelected) => {
|
||||||
|
metadataAPI.deleteView(repoID, viewId).then(res => {
|
||||||
|
const newNavigation = navigation.filter(item => item._id !== viewId);
|
||||||
|
delete viewsMap.current[viewId];
|
||||||
|
setNavigation(newNavigation);
|
||||||
|
if (isSelected) {
|
||||||
|
const currentViewIndex = navigation.findIndex(item => item._id === viewId);
|
||||||
|
const lastViewId = navigation[currentViewIndex - 1]._id;
|
||||||
|
const lastView = viewsMap.current[lastViewId];
|
||||||
|
selectView(lastView);
|
||||||
|
}
|
||||||
|
}).catch((error => {
|
||||||
|
const errorMsg = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errorMsg);
|
||||||
|
}));
|
||||||
|
}, [repoID, navigation, selectView, viewsMap]);
|
||||||
|
|
||||||
|
const updateView = useCallback((viewId, update, successCallback, failCallback) => {
|
||||||
|
metadataAPI.modifyView(repoID, viewId, update).then(res => {
|
||||||
|
const currentView = viewsMap.current[viewId];
|
||||||
|
viewsMap.current[viewId] = { ...currentView, ...update };
|
||||||
|
if (Object.prototype.hasOwnProperty.call(update, 'name')) {
|
||||||
|
renameMetadataView(viewId, '/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/' + update['name']);
|
||||||
|
}
|
||||||
|
successCallback && successCallback();
|
||||||
|
}).catch(error => {
|
||||||
|
failCallback && failCallback(error);
|
||||||
|
});
|
||||||
|
}, [repoID, viewsMap, renameMetadataView]);
|
||||||
|
|
||||||
|
const moveView = useCallback((sourceViewId, targetViewId) => {
|
||||||
|
metadataAPI.moveView(repoID, sourceViewId, targetViewId).then(res => {
|
||||||
|
const { navigation } = res.data;
|
||||||
|
setNavigation(navigation);
|
||||||
|
}).catch(error => {
|
||||||
|
const errorMsg = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errorMsg);
|
||||||
|
});
|
||||||
|
}, [repoID]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MetadataContext.Provider value={{
|
||||||
|
enableMetadata,
|
||||||
|
updateEnableMetadata,
|
||||||
|
showFirstView,
|
||||||
|
navigation,
|
||||||
|
viewsMap: viewsMap.current,
|
||||||
|
selectView,
|
||||||
|
addView,
|
||||||
|
deleteView,
|
||||||
|
updateView,
|
||||||
|
moveView,
|
||||||
|
}}>
|
||||||
|
{children}
|
||||||
|
</MetadataContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMetadata = () => {
|
||||||
|
const context = useContext(MetadataContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('\'MetadataContext\' is null');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
@@ -1,65 +1,54 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { CustomizeAddTool } from '@seafile/sf-metadata-ui-component';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
import { PRIVATE_FILE_TYPE } from '../../constants';
|
import { PRIVATE_FILE_TYPE } from '../../constants';
|
||||||
import metadataAPI from '../api';
|
|
||||||
import { Utils } from '../../utils/utils';
|
|
||||||
import toaster from '../../components/toast';
|
|
||||||
import ViewItem from './view-item';
|
import ViewItem from './view-item';
|
||||||
import NameDialog from './name-dialog';
|
import NameDialog from './name-dialog';
|
||||||
|
import { useMetadata } from '../hooks';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import { CustomizeAddTool } from '@seafile/sf-metadata-ui-component';
|
|
||||||
|
|
||||||
const MetadataTreeView = ({ userPerm, repoID, currentPath, onNodeClick }) => {
|
const MetadataTreeView = ({ userPerm, currentPath }) => {
|
||||||
const canAdd = useMemo(() => {
|
const canAdd = useMemo(() => {
|
||||||
if (userPerm !== 'rw' && userPerm !== 'admin') return false;
|
if (userPerm !== 'rw' && userPerm !== 'admin') return false;
|
||||||
return true;
|
return true;
|
||||||
}, [userPerm]);
|
}, [userPerm]);
|
||||||
const [views, setViews] = useState([]);
|
|
||||||
const [showAddViewDialog, setSowAddViewDialog] = useState(false);
|
const [showAddViewDialog, setSowAddViewDialog] = useState(false);
|
||||||
const [, setState] = useState(0);
|
const [, setState] = useState(0);
|
||||||
const viewsMap = useRef({});
|
const {
|
||||||
|
showFirstView,
|
||||||
|
navigation,
|
||||||
|
viewsMap,
|
||||||
|
selectView,
|
||||||
|
addView,
|
||||||
|
deleteView,
|
||||||
|
updateView,
|
||||||
|
moveView
|
||||||
|
} = useMetadata();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
metadataAPI.listViews(repoID).then(res => {
|
const { origin, pathname, search } = window.location;
|
||||||
const { navigation, views } = res.data;
|
const urlParams = new URLSearchParams(search);
|
||||||
if (Array.isArray(views)) {
|
const viewID = urlParams.get('view');
|
||||||
views.forEach(view => {
|
if (viewID) {
|
||||||
viewsMap.current[view._id] = view;
|
const lastOpenedView = viewsMap[viewID] || '';
|
||||||
});
|
if (lastOpenedView) {
|
||||||
|
selectView(lastOpenedView);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
setViews(navigation);
|
const url = `${origin}${pathname}`;
|
||||||
}).catch(error => {
|
window.history.pushState({ url: url, path: '' }, '', url);
|
||||||
const errorMsg = Utils.getErrorMsg(error);
|
}
|
||||||
toaster.danger(errorMsg);
|
|
||||||
});
|
const firstViewObject = navigation.find(item => item.type === 'view');
|
||||||
|
const firstView = firstViewObject ? viewsMap[firstViewObject._id] : '';
|
||||||
|
if (showFirstView && firstView) {
|
||||||
|
selectView(firstView);
|
||||||
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onClick = useCallback((view, isSelected) => {
|
|
||||||
if (isSelected) return;
|
|
||||||
const node = {
|
|
||||||
children: [],
|
|
||||||
path: '/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/' + view.name,
|
|
||||||
isExpanded: false,
|
|
||||||
isLoaded: true,
|
|
||||||
isPreload: true,
|
|
||||||
object: {
|
|
||||||
file_tags: [],
|
|
||||||
id: PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES,
|
|
||||||
name: gettext('File extended properties'),
|
|
||||||
type: PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES,
|
|
||||||
isDir: () => false,
|
|
||||||
},
|
|
||||||
parentNode: {},
|
|
||||||
key: repoID,
|
|
||||||
view_id: view._id,
|
|
||||||
view_name: view.name,
|
|
||||||
};
|
|
||||||
onNodeClick(node);
|
|
||||||
}, [repoID, onNodeClick]);
|
|
||||||
|
|
||||||
const openAddView = useCallback(() => {
|
const openAddView = useCallback(() => {
|
||||||
setSowAddViewDialog(true);
|
setSowAddViewDialog(true);
|
||||||
}, []);
|
}, []);
|
||||||
@@ -68,66 +57,20 @@ const MetadataTreeView = ({ userPerm, repoID, currentPath, onNodeClick }) => {
|
|||||||
setSowAddViewDialog(false);
|
setSowAddViewDialog(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addView = useCallback((name, failCallback) => {
|
|
||||||
metadataAPI.addView(repoID, name).then(res => {
|
|
||||||
const view = res.data.view;
|
|
||||||
let newViews = views.slice(0);
|
|
||||||
newViews.push({ _id: view._id, type: 'view' });
|
|
||||||
viewsMap.current[view._id] = view;
|
|
||||||
setSowAddViewDialog(false);
|
|
||||||
setViews(newViews);
|
|
||||||
onClick(view);
|
|
||||||
}).catch(error => {
|
|
||||||
failCallback && failCallback(error);
|
|
||||||
});
|
|
||||||
}, [views, repoID, viewsMap, onClick]);
|
|
||||||
|
|
||||||
const onDeleteView = useCallback((viewId, isSelected) => {
|
|
||||||
metadataAPI.deleteView(repoID, viewId).then(res => {
|
|
||||||
const newViews = views.filter(item => item._id !== viewId);
|
|
||||||
delete viewsMap.current[viewId];
|
|
||||||
setViews(newViews);
|
|
||||||
if (isSelected) {
|
|
||||||
const currentViewIndex = views.findIndex(item => item._id === viewId);
|
|
||||||
const lastViewId = views[currentViewIndex - 1]._id;
|
|
||||||
const lastView = viewsMap.current[lastViewId];
|
|
||||||
onClick(lastView);
|
|
||||||
}
|
|
||||||
}).catch((error => {
|
|
||||||
const errorMsg = Utils.getErrorMsg(error);
|
|
||||||
toaster.danger(errorMsg);
|
|
||||||
}));
|
|
||||||
}, [repoID, views, onClick, viewsMap]);
|
|
||||||
|
|
||||||
const onUpdateView = useCallback((viewId, update, successCallback, failCallback) => {
|
const onUpdateView = useCallback((viewId, update, successCallback, failCallback) => {
|
||||||
metadataAPI.modifyView(repoID, viewId, update).then(res => {
|
updateView(viewId, update, () => {
|
||||||
successCallback && successCallback();
|
|
||||||
const currentView = viewsMap.current[viewId];
|
|
||||||
viewsMap.current[viewId] = { ...currentView, ...update };
|
|
||||||
setState(n => n + 1);
|
setState(n => n + 1);
|
||||||
}).catch(error => {
|
successCallback && successCallback();
|
||||||
failCallback && failCallback(error);
|
}, failCallback);
|
||||||
});
|
}, [updateView]);
|
||||||
}, [repoID, viewsMap]);
|
|
||||||
|
|
||||||
const onMoveView = useCallback((sourceViewId, targetViewId) => {
|
|
||||||
metadataAPI.moveView(repoID, sourceViewId, targetViewId).then(res => {
|
|
||||||
const { navigation } = res.data;
|
|
||||||
setViews(navigation);
|
|
||||||
}).catch(error => {
|
|
||||||
const errorMsg = Utils.getErrorMsg(error);
|
|
||||||
toaster.danger(errorMsg);
|
|
||||||
});
|
|
||||||
}, [repoID]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="tree-view tree metadata-tree-view">
|
<div className="tree-view tree metadata-tree-view">
|
||||||
<div className="tree-node">
|
<div className="tree-node">
|
||||||
<div className="children">
|
<div className="children">
|
||||||
{views.map((item, index) => {
|
{navigation.map((item, index) => {
|
||||||
if (item.type !== 'view') return null;
|
const view = viewsMap[item._id];
|
||||||
const view = viewsMap.current[item._id];
|
|
||||||
const viewPath = '/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/' + view.name;
|
const viewPath = '/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/' + view.name;
|
||||||
const isSelected = currentPath === viewPath;
|
const isSelected = currentPath === viewPath;
|
||||||
return (
|
return (
|
||||||
@@ -137,10 +80,10 @@ const MetadataTreeView = ({ userPerm, repoID, currentPath, onNodeClick }) => {
|
|||||||
isSelected={isSelected}
|
isSelected={isSelected}
|
||||||
userPerm={userPerm}
|
userPerm={userPerm}
|
||||||
view={view}
|
view={view}
|
||||||
onClick={(view) => onClick(view, isSelected)}
|
onClick={(view) => selectView(view, isSelected)}
|
||||||
onDelete={() => onDeleteView(view._id, isSelected)}
|
onDelete={() => deleteView(view._id, isSelected)}
|
||||||
onUpdate={(update, successCallback, failCallback) => onUpdateView(view._id, update, successCallback, failCallback)}
|
onUpdate={(update, successCallback, failCallback) => onUpdateView(view._id, update, successCallback, failCallback)}
|
||||||
onMove={onMoveView}
|
onMove={moveView}
|
||||||
/>);
|
/>);
|
||||||
})}
|
})}
|
||||||
{canAdd &&
|
{canAdd &&
|
||||||
@@ -161,9 +104,7 @@ const MetadataTreeView = ({ userPerm, repoID, currentPath, onNodeClick }) => {
|
|||||||
|
|
||||||
MetadataTreeView.propTypes = {
|
MetadataTreeView.propTypes = {
|
||||||
userPerm: PropTypes.string,
|
userPerm: PropTypes.string,
|
||||||
repoID: PropTypes.string.isRequired,
|
|
||||||
currentPath: PropTypes.string,
|
currentPath: PropTypes.string,
|
||||||
onNodeClick: PropTypes.func,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MetadataTreeView;
|
export default MetadataTreeView;
|
||||||
|
@@ -45,7 +45,9 @@ const NameDialog = ({ value: oldName, title, onSubmit, onToggle }) => {
|
|||||||
onToggle();
|
onToggle();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onSubmit(message, (error) => {
|
onSubmit(message, () => {
|
||||||
|
onToggle();
|
||||||
|
}, (error) => {
|
||||||
const errorMsg = Utils.getErrorMsg(error);
|
const errorMsg = Utils.getErrorMsg(error);
|
||||||
setErrorMessage(errorMsg);
|
setErrorMessage(errorMsg);
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
@@ -92,7 +92,7 @@ const ViewItem = ({
|
|||||||
if (!canDrop) return false;
|
if (!canDrop) return false;
|
||||||
const dragData = JSON.stringify({ type: 'sf-metadata-view', view_id: view._id });
|
const dragData = JSON.stringify({ type: 'sf-metadata-view', view_id: view._id });
|
||||||
event.dataTransfer.effectAllowed = 'move';
|
event.dataTransfer.effectAllowed = 'move';
|
||||||
event.dataTransfer.setData('application/drag-sf-metadata-view-info', dragData);
|
event.dataTransfer.setData('application/drag-sf-metadata-view', dragData);
|
||||||
}, [canDrop, view]);
|
}, [canDrop, view]);
|
||||||
|
|
||||||
const onDragEnter = useCallback((event) => {
|
const onDragEnter = useCallback((event) => {
|
||||||
@@ -105,8 +105,10 @@ const ViewItem = ({
|
|||||||
setDropShow(false);
|
setDropShow(false);
|
||||||
}, [canDrop]);
|
}, [canDrop]);
|
||||||
|
|
||||||
const onDragMove = useCallback(() => {
|
const onDragMove = useCallback((event) => {
|
||||||
if (!canDrop) return false;
|
if (!canDrop) return false;
|
||||||
|
event.preventDefault();
|
||||||
|
event.dataTransfer.dropEffect = 'move';
|
||||||
}, [canDrop]);
|
}, [canDrop]);
|
||||||
|
|
||||||
const onDrop = useCallback((event) => {
|
const onDrop = useCallback((event) => {
|
||||||
@@ -114,7 +116,7 @@ const ViewItem = ({
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
setDropShow(false);
|
setDropShow(false);
|
||||||
|
|
||||||
let dragData = event.dataTransfer.getData('application/drag-sf-metadata-view-info');
|
let dragData = event.dataTransfer.getData('application/drag-sf-metadata-view');
|
||||||
if (!dragData) return;
|
if (!dragData) return;
|
||||||
dragData = JSON.parse(dragData);
|
dragData = JSON.parse(dragData);
|
||||||
if (dragData.type !== 'sf-metadata-view') return false;
|
if (dragData.type !== 'sf-metadata-view') return false;
|
||||||
|
@@ -5,7 +5,7 @@ import { EVENT_BUS_TYPE } from '../../constants';
|
|||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
const ViewToolBar = ({ metadataViewId }) => {
|
const ViewToolBar = ({ viewId }) => {
|
||||||
const [view, setView] = useState(null);
|
const [view, setView] = useState(null);
|
||||||
const [collaborators, setCollaborators] = useState([]);
|
const [collaborators, setCollaborators] = useState([]);
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ const ViewToolBar = ({ metadataViewId }) => {
|
|||||||
unsubscribeViewChange && unsubscribeViewChange();
|
unsubscribeViewChange && unsubscribeViewChange();
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [metadataViewId]);
|
}, [viewId]);
|
||||||
|
|
||||||
if (!view) return null;
|
if (!view) return null;
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ const ViewToolBar = ({ metadataViewId }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ViewToolBar.propTypes = {
|
ViewToolBar.propTypes = {
|
||||||
metadataViewId: PropTypes.string,
|
viewId: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ViewToolBar;
|
export default ViewToolBar;
|
||||||
|
@@ -6,7 +6,6 @@ import CurDirPath from '../../components/cur-dir-path';
|
|||||||
import Detail from '../../components/dirent-detail';
|
import Detail from '../../components/dirent-detail';
|
||||||
import DirColumnView from '../../components/dir-view-mode/dir-column-view';
|
import DirColumnView from '../../components/dir-view-mode/dir-column-view';
|
||||||
import ToolbarForSelectedDirents from '../../components/toolbar/selected-dirents-toolbar';
|
import ToolbarForSelectedDirents from '../../components/toolbar/selected-dirents-toolbar';
|
||||||
import { MetadataStatusProvider } from '../../metadata/hooks';
|
|
||||||
|
|
||||||
import '../../css/lib-content-view.css';
|
import '../../css/lib-content-view.css';
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ const propTypes = {
|
|||||||
isFileLoading: PropTypes.bool.isRequired,
|
isFileLoading: PropTypes.bool.isRequired,
|
||||||
filePermission: PropTypes.string,
|
filePermission: PropTypes.string,
|
||||||
content: PropTypes.string,
|
content: PropTypes.string,
|
||||||
metadataViewId: PropTypes.string,
|
viewId: PropTypes.string,
|
||||||
lastModified: PropTypes.string,
|
lastModified: PropTypes.string,
|
||||||
latestContributor: PropTypes.string,
|
latestContributor: PropTypes.string,
|
||||||
onLinkClick: PropTypes.func.isRequired,
|
onLinkClick: PropTypes.func.isRequired,
|
||||||
@@ -183,160 +182,158 @@ class LibContentContainer extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MetadataStatusProvider repoID={repoID}>
|
<div className="cur-view-container">
|
||||||
<div className="cur-view-container">
|
{this.props.currentRepoInfo.status === 'read-only' &&
|
||||||
{this.props.currentRepoInfo.status === 'read-only' &&
|
<div className="readonly-tip-message">
|
||||||
<div className="readonly-tip-message">
|
{gettext('This library has been set to read-only by admin and cannot be updated.')}
|
||||||
{gettext('This library has been set to read-only by admin and cannot be updated.')}
|
</div>
|
||||||
</div>
|
}
|
||||||
}
|
<div className="cur-view-path d-block" style={curViewPathStyle}>
|
||||||
<div className="cur-view-path d-block" style={curViewPathStyle}>
|
<CurDirPath
|
||||||
<CurDirPath
|
currentRepoInfo={this.props.currentRepoInfo}
|
||||||
currentRepoInfo={this.props.currentRepoInfo}
|
repoID={repoID}
|
||||||
|
repoName={this.props.currentRepoInfo.repo_name}
|
||||||
|
repoEncrypted={this.props.repoEncrypted}
|
||||||
|
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
|
||||||
|
pathPrefix={this.props.pathPrefix}
|
||||||
|
currentPath={this.props.path}
|
||||||
|
userPerm={this.props.userPerm}
|
||||||
|
isViewFile={this.props.isViewFile}
|
||||||
|
onTabNavClick={this.props.onTabNavClick}
|
||||||
|
onPathClick={this.onPathClick}
|
||||||
|
updateUsedRepoTags={this.props.updateUsedRepoTags}
|
||||||
|
fileTags={this.props.fileTags}
|
||||||
|
onDeleteRepoTag={this.props.onDeleteRepoTag}
|
||||||
|
direntList={this.props.direntList}
|
||||||
|
sortBy={this.props.sortBy}
|
||||||
|
sortOrder={this.props.sortOrder}
|
||||||
|
sortItems={this.props.sortItems}
|
||||||
|
toggleTreePanel={this.props.toggleTreePanel}
|
||||||
|
currentMode={this.props.currentMode}
|
||||||
|
switchViewMode={this.props.switchViewMode}
|
||||||
|
isCustomPermission={this.props.isCustomPermission}
|
||||||
|
enableDirPrivateShare={this.props.enableDirPrivateShare}
|
||||||
|
showShareBtn={this.props.showShareBtn}
|
||||||
|
onAddFolder={this.props.onAddFolder}
|
||||||
|
onAddFile={this.props.onAddFile}
|
||||||
|
onUploadFile={this.props.onUploadFile}
|
||||||
|
onUploadFolder={this.props.onUploadFolder}
|
||||||
|
fullDirentList={this.props.fullDirentList}
|
||||||
|
filePermission={this.props.filePermission}
|
||||||
|
onFileTagChanged={this.props.onToolbarFileTagChanged}
|
||||||
|
repoTags={this.props.repoTags}
|
||||||
|
viewId={this.props.viewId}
|
||||||
|
onItemMove={this.props.onItemMove}
|
||||||
|
/>
|
||||||
|
<ToolbarForSelectedDirents
|
||||||
|
repoID={this.props.repoID}
|
||||||
|
path={this.props.path}
|
||||||
|
userPerm={this.props.userPerm}
|
||||||
|
repoEncrypted={this.props.repoEncrypted}
|
||||||
|
repoTags={this.props.repoTags}
|
||||||
|
selectedDirentList={this.props.selectedDirentList}
|
||||||
|
direntList={this.props.direntList}
|
||||||
|
onItemsMove={this.props.onItemsMove}
|
||||||
|
onItemsCopy={this.props.onItemsCopy}
|
||||||
|
onItemsDelete={this.props.onItemsDelete}
|
||||||
|
onItemRename={this.props.onItemRename}
|
||||||
|
isRepoOwner={this.props.isRepoOwner}
|
||||||
|
currentRepoInfo={this.props.currentRepoInfo}
|
||||||
|
enableDirPrivateShare={this.props.enableDirPrivateShare}
|
||||||
|
updateDirent={this.props.updateDirent}
|
||||||
|
unSelectDirent={this.props.unSelectDirent}
|
||||||
|
onFilesTagChanged={this.props.onFilesTagChanged}
|
||||||
|
showShareBtn={this.props.showShareBtn}
|
||||||
|
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
|
||||||
|
showDirentDetail={this.props.showDirentDetail}
|
||||||
|
currentMode={this.props.currentMode}
|
||||||
|
switchViewMode={this.props.switchViewMode}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`cur-view-content lib-content-container ${this.props.isTreePanelShown ? 'view-mode-container' : ''}`}
|
||||||
|
onScroll={this.onItemsScroll}
|
||||||
|
>
|
||||||
|
{!this.props.pathExist && this.errMessage}
|
||||||
|
{this.props.pathExist && (
|
||||||
|
<DirColumnView
|
||||||
|
isSidePanelFolded={this.props.isSidePanelFolded}
|
||||||
|
isTreePanelShown={this.props.isTreePanelShown}
|
||||||
|
currentMode={this.props.currentMode}
|
||||||
|
path={this.props.path}
|
||||||
repoID={repoID}
|
repoID={repoID}
|
||||||
repoName={this.props.currentRepoInfo.repo_name}
|
currentRepoInfo={this.props.currentRepoInfo}
|
||||||
repoEncrypted={this.props.repoEncrypted}
|
|
||||||
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
|
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
|
||||||
pathPrefix={this.props.pathPrefix}
|
|
||||||
currentPath={this.props.path}
|
|
||||||
userPerm={this.props.userPerm}
|
userPerm={this.props.userPerm}
|
||||||
|
enableDirPrivateShare={this.props.enableDirPrivateShare}
|
||||||
|
isTreeDataLoading={this.props.isTreeDataLoading}
|
||||||
|
treeData={this.props.treeData}
|
||||||
|
currentNode={this.props.currentNode}
|
||||||
|
onNodeClick={this.props.onNodeClick}
|
||||||
|
onNodeCollapse={this.props.onNodeCollapse}
|
||||||
|
onNodeExpanded={this.props.onNodeExpanded}
|
||||||
|
onAddFolderNode={this.props.onAddFolder}
|
||||||
|
onAddFileNode={this.props.onAddFile}
|
||||||
|
onRenameNode={this.props.onRenameNode}
|
||||||
|
onDeleteNode={this.props.onDeleteNode}
|
||||||
isViewFile={this.props.isViewFile}
|
isViewFile={this.props.isViewFile}
|
||||||
onTabNavClick={this.props.onTabNavClick}
|
isFileLoading={this.props.isFileLoading}
|
||||||
onPathClick={this.onPathClick}
|
isFileLoadedErr={this.props.isFileLoadedErr}
|
||||||
|
hash={this.props.hash}
|
||||||
|
filePermission={this.props.filePermission}
|
||||||
|
content={this.props.content}
|
||||||
|
viewId={this.props.viewId}
|
||||||
|
lastModified={this.props.lastModified}
|
||||||
|
latestContributor={this.props.latestContributor}
|
||||||
|
onLinkClick={this.props.onLinkClick}
|
||||||
|
isRepoInfoBarShow={isRepoInfoBarShow}
|
||||||
|
repoTags={this.props.repoTags}
|
||||||
|
usedRepoTags={this.props.usedRepoTags}
|
||||||
updateUsedRepoTags={this.props.updateUsedRepoTags}
|
updateUsedRepoTags={this.props.updateUsedRepoTags}
|
||||||
fileTags={this.props.fileTags}
|
isDirentListLoading={this.props.isDirentListLoading}
|
||||||
onDeleteRepoTag={this.props.onDeleteRepoTag}
|
|
||||||
direntList={this.props.direntList}
|
direntList={this.props.direntList}
|
||||||
|
fullDirentList={this.props.fullDirentList}
|
||||||
sortBy={this.props.sortBy}
|
sortBy={this.props.sortBy}
|
||||||
sortOrder={this.props.sortOrder}
|
sortOrder={this.props.sortOrder}
|
||||||
sortItems={this.props.sortItems}
|
sortItems={this.props.sortItems}
|
||||||
toggleTreePanel={this.props.toggleTreePanel}
|
|
||||||
currentMode={this.props.currentMode}
|
|
||||||
switchViewMode={this.props.switchViewMode}
|
|
||||||
isCustomPermission={this.props.isCustomPermission}
|
|
||||||
enableDirPrivateShare={this.props.enableDirPrivateShare}
|
|
||||||
showShareBtn={this.props.showShareBtn}
|
|
||||||
onAddFolder={this.props.onAddFolder}
|
onAddFolder={this.props.onAddFolder}
|
||||||
onAddFile={this.props.onAddFile}
|
onAddFile={this.props.onAddFile}
|
||||||
onUploadFile={this.props.onUploadFile}
|
onItemClick={this.onItemClick}
|
||||||
onUploadFolder={this.props.onUploadFolder}
|
onItemSelected={this.onItemSelected}
|
||||||
fullDirentList={this.props.fullDirentList}
|
onItemDelete={this.onItemDelete}
|
||||||
filePermission={this.props.filePermission}
|
onItemRename={this.props.onItemRename}
|
||||||
onFileTagChanged={this.props.onToolbarFileTagChanged}
|
onItemMove={this.onItemMove}
|
||||||
repoTags={this.props.repoTags}
|
onItemCopy={this.props.onItemCopy}
|
||||||
metadataViewId={this.props.metadataViewId}
|
onItemConvert={this.props.onItemConvert}
|
||||||
onItemMove={this.props.onItemMove}
|
onDirentClick={this.onDirentClick}
|
||||||
/>
|
updateDirent={this.props.updateDirent}
|
||||||
<ToolbarForSelectedDirents
|
isAllItemSelected={this.props.isAllDirentSelected}
|
||||||
repoID={this.props.repoID}
|
onAllItemSelected={this.props.onAllDirentSelected}
|
||||||
path={this.props.path}
|
|
||||||
userPerm={this.props.userPerm}
|
|
||||||
repoEncrypted={this.props.repoEncrypted}
|
|
||||||
repoTags={this.props.repoTags}
|
|
||||||
selectedDirentList={this.props.selectedDirentList}
|
selectedDirentList={this.props.selectedDirentList}
|
||||||
direntList={this.props.direntList}
|
onSelectedDirentListUpdate={this.props.onSelectedDirentListUpdate}
|
||||||
onItemsMove={this.props.onItemsMove}
|
onItemsMove={this.props.onItemsMove}
|
||||||
onItemsCopy={this.props.onItemsCopy}
|
onItemsCopy={this.props.onItemsCopy}
|
||||||
onItemsDelete={this.props.onItemsDelete}
|
onItemsDelete={this.props.onItemsDelete}
|
||||||
onItemRename={this.props.onItemRename}
|
onFileTagChanged={this.props.onFileTagChanged}
|
||||||
isRepoOwner={this.props.isRepoOwner}
|
|
||||||
currentRepoInfo={this.props.currentRepoInfo}
|
|
||||||
enableDirPrivateShare={this.props.enableDirPrivateShare}
|
|
||||||
updateDirent={this.props.updateDirent}
|
|
||||||
unSelectDirent={this.props.unSelectDirent}
|
|
||||||
onFilesTagChanged={this.props.onFilesTagChanged}
|
|
||||||
showShareBtn={this.props.showShareBtn}
|
|
||||||
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
|
|
||||||
showDirentDetail={this.props.showDirentDetail}
|
showDirentDetail={this.props.showDirentDetail}
|
||||||
currentMode={this.props.currentMode}
|
onItemsScroll={this.onItemsScroll}
|
||||||
switchViewMode={this.props.switchViewMode}
|
isDirentDetailShow={this.props.isDirentDetailShow}
|
||||||
/>
|
/>
|
||||||
</div>
|
)}
|
||||||
<div
|
{this.props.isDirentDetailShow && (
|
||||||
className={`cur-view-content lib-content-container ${this.props.isTreePanelShown ? 'view-mode-container' : ''}`}
|
<Detail
|
||||||
onScroll={this.onItemsScroll}
|
path={path}
|
||||||
>
|
repoID={repoID}
|
||||||
{!this.props.pathExist && this.errMessage}
|
currentRepoInfo={this.props.currentRepoInfo}
|
||||||
{this.props.pathExist && (
|
dirent={this.state.currentDirent}
|
||||||
<DirColumnView
|
repoTags={this.props.repoTags}
|
||||||
isSidePanelFolded={this.props.isSidePanelFolded}
|
fileTags={this.props.isViewFile ? this.props.fileTags : []}
|
||||||
isTreePanelShown={this.props.isTreePanelShown}
|
onFileTagChanged={this.props.onFileTagChanged}
|
||||||
currentMode={this.props.currentMode}
|
onClose={this.props.closeDirentDetail}
|
||||||
path={this.props.path}
|
/>
|
||||||
repoID={repoID}
|
)}
|
||||||
currentRepoInfo={this.props.currentRepoInfo}
|
|
||||||
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
|
|
||||||
userPerm={this.props.userPerm}
|
|
||||||
enableDirPrivateShare={this.props.enableDirPrivateShare}
|
|
||||||
isTreeDataLoading={this.props.isTreeDataLoading}
|
|
||||||
treeData={this.props.treeData}
|
|
||||||
currentNode={this.props.currentNode}
|
|
||||||
onNodeClick={this.props.onNodeClick}
|
|
||||||
onNodeCollapse={this.props.onNodeCollapse}
|
|
||||||
onNodeExpanded={this.props.onNodeExpanded}
|
|
||||||
onAddFolderNode={this.props.onAddFolder}
|
|
||||||
onAddFileNode={this.props.onAddFile}
|
|
||||||
onRenameNode={this.props.onRenameNode}
|
|
||||||
onDeleteNode={this.props.onDeleteNode}
|
|
||||||
isViewFile={this.props.isViewFile}
|
|
||||||
isFileLoading={this.props.isFileLoading}
|
|
||||||
isFileLoadedErr={this.props.isFileLoadedErr}
|
|
||||||
hash={this.props.hash}
|
|
||||||
filePermission={this.props.filePermission}
|
|
||||||
content={this.props.content}
|
|
||||||
metadataViewId={this.props.metadataViewId}
|
|
||||||
lastModified={this.props.lastModified}
|
|
||||||
latestContributor={this.props.latestContributor}
|
|
||||||
onLinkClick={this.props.onLinkClick}
|
|
||||||
isRepoInfoBarShow={isRepoInfoBarShow}
|
|
||||||
repoTags={this.props.repoTags}
|
|
||||||
usedRepoTags={this.props.usedRepoTags}
|
|
||||||
updateUsedRepoTags={this.props.updateUsedRepoTags}
|
|
||||||
isDirentListLoading={this.props.isDirentListLoading}
|
|
||||||
direntList={this.props.direntList}
|
|
||||||
fullDirentList={this.props.fullDirentList}
|
|
||||||
sortBy={this.props.sortBy}
|
|
||||||
sortOrder={this.props.sortOrder}
|
|
||||||
sortItems={this.props.sortItems}
|
|
||||||
onAddFolder={this.props.onAddFolder}
|
|
||||||
onAddFile={this.props.onAddFile}
|
|
||||||
onItemClick={this.onItemClick}
|
|
||||||
onItemSelected={this.onItemSelected}
|
|
||||||
onItemDelete={this.onItemDelete}
|
|
||||||
onItemRename={this.props.onItemRename}
|
|
||||||
onItemMove={this.onItemMove}
|
|
||||||
onItemCopy={this.props.onItemCopy}
|
|
||||||
onItemConvert={this.props.onItemConvert}
|
|
||||||
onDirentClick={this.onDirentClick}
|
|
||||||
updateDirent={this.props.updateDirent}
|
|
||||||
isAllItemSelected={this.props.isAllDirentSelected}
|
|
||||||
onAllItemSelected={this.props.onAllDirentSelected}
|
|
||||||
selectedDirentList={this.props.selectedDirentList}
|
|
||||||
onSelectedDirentListUpdate={this.props.onSelectedDirentListUpdate}
|
|
||||||
onItemsMove={this.props.onItemsMove}
|
|
||||||
onItemsCopy={this.props.onItemsCopy}
|
|
||||||
onItemsDelete={this.props.onItemsDelete}
|
|
||||||
onFileTagChanged={this.props.onFileTagChanged}
|
|
||||||
showDirentDetail={this.props.showDirentDetail}
|
|
||||||
onItemsScroll={this.onItemsScroll}
|
|
||||||
isDirentDetailShow={this.props.isDirentDetailShow}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{this.props.isDirentDetailShow && (
|
|
||||||
<Detail
|
|
||||||
path={path}
|
|
||||||
repoID={repoID}
|
|
||||||
currentRepoInfo={this.props.currentRepoInfo}
|
|
||||||
dirent={this.state.currentDirent}
|
|
||||||
repoTags={this.props.repoTags}
|
|
||||||
fileTags={this.props.isViewFile ? this.props.fileTags : []}
|
|
||||||
onFileTagChanged={this.props.onFileTagChanged}
|
|
||||||
onClose={this.props.closeDirentDetail}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</MetadataStatusProvider>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,7 @@ import CopyMoveDirentProgressDialog from '../../components/dialog/copy-move-dire
|
|||||||
import DeleteFolderDialog from '../../components/dialog/delete-folder-dialog';
|
import DeleteFolderDialog from '../../components/dialog/delete-folder-dialog';
|
||||||
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
|
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
|
||||||
import { PRIVATE_FILE_TYPE } from '../../constants';
|
import { PRIVATE_FILE_TYPE } from '../../constants';
|
||||||
|
import { MetadataProvider } from '../../metadata/hooks';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
eventBus: PropTypes.object,
|
eventBus: PropTypes.object,
|
||||||
@@ -89,7 +90,7 @@ class LibContentView extends React.Component {
|
|||||||
asyncOperationType: 'move',
|
asyncOperationType: 'move',
|
||||||
asyncOperationProgress: 0,
|
asyncOperationProgress: 0,
|
||||||
asyncOperatedFilesLength: 0,
|
asyncOperatedFilesLength: 0,
|
||||||
metadataViewId: '0000',
|
viewId: '0000',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.oldonpopstate = window.onpopstate;
|
this.oldonpopstate = window.onpopstate;
|
||||||
@@ -148,12 +149,7 @@ class LibContentView extends React.Component {
|
|||||||
const { repoID, eventBus } = props;
|
const { repoID, eventBus } = props;
|
||||||
this.unsubscribeEvent = eventBus.subscribe(EVENT_BUS_TYPE.SEARCH_LIBRARY_CONTENT, this.onSearchedClick);
|
this.unsubscribeEvent = eventBus.subscribe(EVENT_BUS_TYPE.SEARCH_LIBRARY_CONTENT, this.onSearchedClick);
|
||||||
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const path = this.getPathFromLocation(repoID);
|
||||||
const viewID = urlParams.get('view');
|
|
||||||
const viewName = JSON.parse(localStorage.getItem('last_visited_view'))?.viewName;
|
|
||||||
const path = viewID
|
|
||||||
? `/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${viewName}`
|
|
||||||
: this.getPathFromLocation(repoID);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const repoInfo = await this.fetchRepoInfo(repoID);
|
const repoInfo = await this.fetchRepoInfo(repoID);
|
||||||
@@ -410,19 +406,11 @@ class LibContentView extends React.Component {
|
|||||||
this.loadSidePanel(path);
|
this.loadSidePanel(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.includes(PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES)) {
|
if (!path.includes(PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES)) {
|
||||||
this.handleFileExtendedProperties(path);
|
|
||||||
} else {
|
|
||||||
this.showDir(path);
|
this.showDir(path);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleFileExtendedProperties = (path) => {
|
|
||||||
const lastVisitedView = localStorage.getItem('last_visited_view');
|
|
||||||
const { viewID, viewName } = lastVisitedView ? JSON.parse(lastVisitedView) : {};
|
|
||||||
this.showFileMetadata(path, viewID, viewName);
|
|
||||||
};
|
|
||||||
|
|
||||||
loadSidePanel = (path) => {
|
loadSidePanel = (path) => {
|
||||||
let repoID = this.props.repoID;
|
let repoID = this.props.repoID;
|
||||||
if (path === '/' || path.includes(PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES)) {
|
if (path === '/' || path.includes(PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES)) {
|
||||||
@@ -523,7 +511,7 @@ class LibContentView extends React.Component {
|
|||||||
window.history.pushState({ url: url, path: filePath }, filePath, url);
|
window.history.pushState({ url: url, path: filePath }, filePath, url);
|
||||||
};
|
};
|
||||||
|
|
||||||
showFileMetadata = (filePath, viewId, viewName) => {
|
showFileMetadata = (filePath, viewId) => {
|
||||||
const repoID = this.props.repoID;
|
const repoID = this.props.repoID;
|
||||||
const repoInfo = this.state.currentRepoInfo;
|
const repoInfo = this.state.currentRepoInfo;
|
||||||
|
|
||||||
@@ -533,16 +521,33 @@ class LibContentView extends React.Component {
|
|||||||
isFileLoading: false,
|
isFileLoading: false,
|
||||||
isFileLoadedErr: false,
|
isFileLoadedErr: false,
|
||||||
content: '__sf-metadata',
|
content: '__sf-metadata',
|
||||||
metadataViewId: viewId,
|
viewId: viewId,
|
||||||
isDirentDetailShow: false
|
isDirentDetailShow: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const url = `${siteRoot}library/${repoID}/${encodeURIComponent(repoInfo.repo_name)}?view=${encodeURIComponent(viewId)}`;
|
const url = `${siteRoot}library/${repoID}/${encodeURIComponent(repoInfo.repo_name)}?view=${encodeURIComponent(viewId)}`;
|
||||||
|
|
||||||
localStorage.setItem('last_visited_view', JSON.stringify({ viewID: viewId, viewName: viewName }));
|
|
||||||
window.history.pushState({ url: url, path: '' }, '', url);
|
window.history.pushState({ url: url, path: '' }, '', url);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
hideFileMetadata = () => {
|
||||||
|
this.setState({
|
||||||
|
path: '',
|
||||||
|
isViewFile: false,
|
||||||
|
isFileLoading: false,
|
||||||
|
isFileLoadedErr: false,
|
||||||
|
content: '',
|
||||||
|
viewId: '',
|
||||||
|
isDirentDetailShow: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
renameMetadataView = (renamedViewId, newPath) => {
|
||||||
|
const { viewId, content } = this.state;
|
||||||
|
if (content !== '__sf-metadata') return;
|
||||||
|
if (viewId !== renamedViewId) return;
|
||||||
|
this.setState({ path: newPath });
|
||||||
|
};
|
||||||
|
|
||||||
loadDirentList = (path) => {
|
loadDirentList = (path) => {
|
||||||
let repoID = this.props.repoID;
|
let repoID = this.props.repoID;
|
||||||
seafileAPI.listDir(repoID, path, { 'with_thumbnail': true }).then(res => {
|
seafileAPI.listDir(repoID, path, { 'with_thumbnail': true }).then(res => {
|
||||||
@@ -1827,7 +1832,7 @@ class LibContentView extends React.Component {
|
|||||||
}
|
}
|
||||||
} else if (Utils.isFileMetadata(node?.object?.type)) {
|
} else if (Utils.isFileMetadata(node?.object?.type)) {
|
||||||
if (node.path !== this.state.path) {
|
if (node.path !== this.state.path) {
|
||||||
this.showFileMetadata(node.path, node.view_id || '0000', node.view_name);
|
this.showFileMetadata(node.path, node.view_id || '0000');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(node.path);
|
let url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(node.path);
|
||||||
@@ -2163,7 +2168,12 @@ class LibContentView extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<MetadataProvider
|
||||||
|
repoID={this.props.repoID}
|
||||||
|
selectMetadataView={this.onTreeNodeClick}
|
||||||
|
renameMetadataView={this.renameMetadataView}
|
||||||
|
hideMetadataView={this.hideFileMetadata}
|
||||||
|
>
|
||||||
<div className="main-panel-center flex-row">
|
<div className="main-panel-center flex-row">
|
||||||
<LibContentContainer
|
<LibContentContainer
|
||||||
isSidePanelFolded={this.props.isSidePanelFolded}
|
isSidePanelFolded={this.props.isSidePanelFolded}
|
||||||
@@ -2193,7 +2203,7 @@ class LibContentView extends React.Component {
|
|||||||
isFileLoadedErr={this.state.isFileLoadedErr}
|
isFileLoadedErr={this.state.isFileLoadedErr}
|
||||||
filePermission={this.state.filePermission}
|
filePermission={this.state.filePermission}
|
||||||
content={this.state.content}
|
content={this.state.content}
|
||||||
metadataViewId={this.state.metadataViewId}
|
viewId={this.state.viewId}
|
||||||
lastModified={this.state.lastModified}
|
lastModified={this.state.lastModified}
|
||||||
latestContributor={this.state.latestContributor}
|
latestContributor={this.state.latestContributor}
|
||||||
onLinkClick={this.onLinkClick}
|
onLinkClick={this.onLinkClick}
|
||||||
@@ -2281,7 +2291,7 @@ class LibContentView extends React.Component {
|
|||||||
<MediaQuery query="(max-width: 767.8px)">
|
<MediaQuery query="(max-width: 767.8px)">
|
||||||
<Modal zIndex="1030" isOpen={!Utils.isDesktop() && this.state.isTreePanelShown} toggle={this.toggleTreePanel} contentClassName="d-none"></Modal>
|
<Modal zIndex="1030" isOpen={!Utils.isDesktop() && this.state.isTreePanelShown} toggle={this.toggleTreePanel} contentClassName="d-none"></Modal>
|
||||||
</MediaQuery>
|
</MediaQuery>
|
||||||
</Fragment>
|
</MetadataProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user