mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-16 23:29:49 +00:00
Feat view name (#6685)
* feat: view name * feat: update code * feat: updat code * feat: update code * feat: optimzie code --------- Co-authored-by: 杨国璇 <ygx@Hello-word.local>
This commit is contained in:
@@ -8,6 +8,7 @@ import { InternalLinkOperation } from '../operations';
|
|||||||
import DirOperationToolBar from '../../components/toolbar/dir-operation-toolbar';
|
import DirOperationToolBar from '../../components/toolbar/dir-operation-toolbar';
|
||||||
import ViewFileToolbar from '../../components/toolbar/view-file-toolbar';
|
import ViewFileToolbar from '../../components/toolbar/view-file-toolbar';
|
||||||
import { PRIVATE_FILE_TYPE } from '../../constants';
|
import { PRIVATE_FILE_TYPE } from '../../constants';
|
||||||
|
import MetadataViewId2Name from '../../metadata/metadata-view-id-2-name';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
currentRepoInfo: PropTypes.object.isRequired,
|
currentRepoInfo: PropTypes.object.isRequired,
|
||||||
@@ -137,7 +138,7 @@ class DirPath extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<Fragment key={index}>
|
<Fragment key={index}>
|
||||||
<span className="path-split">/</span>
|
<span className="path-split">/</span>
|
||||||
<span className="path-item">{item}</span>
|
<span className="path-item"><MetadataViewId2Name id={item} /></span>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@ import { PRIVATE_FILE_TYPE } from '../../constants';
|
|||||||
// This hook provides content related to seahub interaction, such as whether to enable extended attributes, views data, etc.
|
// This hook provides content related to seahub interaction, such as whether to enable extended attributes, views data, etc.
|
||||||
const MetadataContext = React.createContext(null);
|
const MetadataContext = React.createContext(null);
|
||||||
|
|
||||||
export const MetadataProvider = ({ repoID, hideMetadataView, selectMetadataView, renameMetadataView, children }) => {
|
export const MetadataProvider = ({ repoID, hideMetadataView, selectMetadataView, children }) => {
|
||||||
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
|
||||||
@@ -17,6 +17,7 @@ export const MetadataProvider = ({ repoID, hideMetadataView, selectMetadataView,
|
|||||||
const [enableMetadata, setEnableExtendedProperties] = useState(false);
|
const [enableMetadata, setEnableExtendedProperties] = useState(false);
|
||||||
const [showFirstView, setShowFirstView] = useState(false);
|
const [showFirstView, setShowFirstView] = useState(false);
|
||||||
const [navigation, setNavigation] = useState([]);
|
const [navigation, setNavigation] = useState([]);
|
||||||
|
const [, setCount] = useState(0);
|
||||||
const viewsMap = useRef({});
|
const viewsMap = useRef({});
|
||||||
|
|
||||||
const cancelURLView = useCallback(() => {
|
const cancelURLView = useCallback(() => {
|
||||||
@@ -87,7 +88,7 @@ export const MetadataProvider = ({ repoID, hideMetadataView, selectMetadataView,
|
|||||||
if (isSelected) return;
|
if (isSelected) return;
|
||||||
const node = {
|
const node = {
|
||||||
children: [],
|
children: [],
|
||||||
path: '/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/' + view.name,
|
path: '/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/' + view._id,
|
||||||
isExpanded: false,
|
isExpanded: false,
|
||||||
isLoaded: true,
|
isLoaded: true,
|
||||||
isPreload: true,
|
isPreload: true,
|
||||||
@@ -155,14 +156,12 @@ export const MetadataProvider = ({ repoID, hideMetadataView, selectMetadataView,
|
|||||||
metadataAPI.modifyView(repoID, viewId, update).then(res => {
|
metadataAPI.modifyView(repoID, viewId, update).then(res => {
|
||||||
const currentView = viewsMap.current[viewId];
|
const currentView = viewsMap.current[viewId];
|
||||||
viewsMap.current[viewId] = { ...currentView, ...update };
|
viewsMap.current[viewId] = { ...currentView, ...update };
|
||||||
if (Object.prototype.hasOwnProperty.call(update, 'name')) {
|
setCount(n => n + 1);
|
||||||
renameMetadataView(viewId, '/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/' + update['name']);
|
|
||||||
}
|
|
||||||
successCallback && successCallback();
|
successCallback && successCallback();
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
failCallback && failCallback(error);
|
failCallback && failCallback(error);
|
||||||
});
|
});
|
||||||
}, [repoID, viewsMap, renameMetadataView]);
|
}, [repoID, viewsMap]);
|
||||||
|
|
||||||
const moveView = useCallback((sourceViewId, targetViewId) => {
|
const moveView = useCallback((sourceViewId, targetViewId) => {
|
||||||
metadataAPI.moveView(repoID, sourceViewId, targetViewId).then(res => {
|
metadataAPI.moveView(repoID, sourceViewId, targetViewId).then(res => {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react';
|
||||||
import { Form, Input } from 'reactstrap';
|
import { Input } from 'reactstrap';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { CustomizeAddTool } from '@seafile/sf-metadata-ui-component';
|
import { CustomizeAddTool } from '@seafile/sf-metadata-ui-component';
|
||||||
import Icon from '../../components/icon';
|
import Icon from '../../components/icon';
|
||||||
@@ -8,7 +8,9 @@ import { PRIVATE_FILE_TYPE } from '../../constants';
|
|||||||
import ViewItem from './view-item';
|
import ViewItem from './view-item';
|
||||||
import { useMetadata } from '../hooks';
|
import { useMetadata } from '../hooks';
|
||||||
import { AddView } from '../metadata-view/components/popover/view-popover';
|
import { AddView } from '../metadata-view/components/popover/view-popover';
|
||||||
import { VIEW_TYPE_ICON } from '../metadata-view/_basic';
|
import { isValidViewName, VIEW_TYPE_ICON } from '../metadata-view/_basic';
|
||||||
|
import { isEnter } from '../metadata-view/_basic/utils/hotkey';
|
||||||
|
import toaster from '../../components/toast';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
@@ -34,7 +36,7 @@ const MetadataTreeView = ({ userPerm, currentPath }) => {
|
|||||||
const [showAddViewPopover, setShowAddViewPopover] = useState(false);
|
const [showAddViewPopover, setShowAddViewPopover] = useState(false);
|
||||||
const [showInput, setShowInput] = useState(false);
|
const [showInput, setShowInput] = useState(false);
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
const [inputValue, setInputValue] = useState('Untitled');
|
const [inputValue, setInputValue] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { origin, pathname, search } = window.location;
|
const { origin, pathname, search } = window.location;
|
||||||
@@ -74,37 +76,38 @@ const MetadataTreeView = ({ userPerm, currentPath }) => {
|
|||||||
setInputValue(event.target.value);
|
setInputValue(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePopoverOptionClick = (option) => {
|
const handlePopoverOptionClick = useCallback((option) => {
|
||||||
setNewView(option);
|
setNewView(option);
|
||||||
|
let newViewName = gettext('Untitled');
|
||||||
|
const otherViewsName = Object.values(viewsMap).map(v => v.name);
|
||||||
|
let i = 1;
|
||||||
|
while (otherViewsName.includes(newViewName)) {
|
||||||
|
newViewName = gettext('Untitled') + ' (' + (i++) + ')';
|
||||||
|
}
|
||||||
|
setInputValue(newViewName);
|
||||||
setShowInput(true);
|
setShowInput(true);
|
||||||
setShowAddViewPopover(false);
|
setShowAddViewPopover(false);
|
||||||
};
|
}, [viewsMap]);
|
||||||
|
|
||||||
const handleInputSubmit = useCallback((event) => {
|
const handleInputSubmit = useCallback((event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
addView(inputValue, newView.type);
|
const viewNames = Object.values(viewsMap).map(v => v.name);
|
||||||
|
const { isValid, message } = isValidViewName(inputValue, viewNames);
|
||||||
|
if (!isValid) {
|
||||||
|
toaster.danger(message);
|
||||||
|
inputRef.current.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addView(message, newView.type);
|
||||||
setShowInput(false);
|
setShowInput(false);
|
||||||
setInputValue('Untitled');
|
}, [inputValue, viewsMap, addView, newView]);
|
||||||
}, [inputValue, addView, newView]);
|
|
||||||
|
|
||||||
const handleClickOutsideInput = useCallback((event) => {
|
const onKeyDown = useCallback((event) => {
|
||||||
if (inputRef.current && !inputRef.current.contains(event.target)) {
|
if (isEnter(event)) {
|
||||||
setShowInput(false);
|
handleInputSubmit(event);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [handleInputSubmit]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (showInput) {
|
|
||||||
inputRef.current.select();
|
|
||||||
document.addEventListener('click', handleClickOutsideInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('click', handleClickOutsideInput);
|
|
||||||
};
|
|
||||||
}, [showInput, inputRef, handleClickOutsideInput]);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -113,7 +116,7 @@ const MetadataTreeView = ({ userPerm, currentPath }) => {
|
|||||||
<div className="children">
|
<div className="children">
|
||||||
{navigation.map((item, index) => {
|
{navigation.map((item, index) => {
|
||||||
const view = viewsMap[item._id];
|
const view = viewsMap[item._id];
|
||||||
const viewPath = '/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/' + view.name;
|
const viewPath = '/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/' + view._id;
|
||||||
const isSelected = currentPath === viewPath;
|
const isSelected = currentPath === viewPath;
|
||||||
return (
|
return (
|
||||||
<ViewItem
|
<ViewItem
|
||||||
@@ -131,21 +134,20 @@ const MetadataTreeView = ({ userPerm, currentPath }) => {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{showInput && (
|
{showInput && (
|
||||||
<Form onSubmit={handleInputSubmit} className='tree-view-inner sf-metadata-view-form'>
|
<div className="tree-view-inner sf-metadata-view-form">
|
||||||
<div className="left-icon">
|
<div className="left-icon">
|
||||||
<Icon symbol={VIEW_TYPE_ICON[newView.type] || 'table'} className="metadata-views-icon" />
|
<Icon symbol={VIEW_TYPE_ICON[newView.type] || 'table'} className="metadata-views-icon" />
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
className='sf-metadata-view-input'
|
className="sf-metadata-view-input"
|
||||||
innerRef={inputRef}
|
innerRef={inputRef}
|
||||||
type='text'
|
|
||||||
id='add-view-input'
|
|
||||||
name='add-view'
|
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
onBlur={handleInputSubmit}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
/>
|
/>
|
||||||
</Form>
|
</div>
|
||||||
)}
|
)}
|
||||||
{canAdd && (
|
{canAdd && (
|
||||||
<div id="sf-metadata-view-popover">
|
<div id="sf-metadata-view-popover">
|
||||||
|
@@ -9,6 +9,7 @@ import { Utils, isMobile } from '../../../utils/utils';
|
|||||||
import { VIEW_TYPE_ICON } from '../../metadata-view/_basic';
|
import { VIEW_TYPE_ICON } from '../../metadata-view/_basic';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
import { useMetadata } from '../../hooks';
|
||||||
|
|
||||||
const ViewItem = ({
|
const ViewItem = ({
|
||||||
canDelete,
|
canDelete,
|
||||||
@@ -25,6 +26,9 @@ const ViewItem = ({
|
|||||||
const [freeze, setFreeze] = useState(false);
|
const [freeze, setFreeze] = useState(false);
|
||||||
const [isDropShow, setDropShow] = useState(false);
|
const [isDropShow, setDropShow] = useState(false);
|
||||||
const [isShowRenamePopover, setRenamePopoverShow] = useState(false);
|
const [isShowRenamePopover, setRenamePopoverShow] = useState(false);
|
||||||
|
const { viewsMap } = useMetadata();
|
||||||
|
|
||||||
|
const otherViewsName = Object.values(viewsMap).filter(v => v._id !== view._id).map(v => v.name);
|
||||||
|
|
||||||
const canUpdate = useMemo(() => {
|
const canUpdate = useMemo(() => {
|
||||||
if (userPerm !== 'rw' && userPerm !== 'admin') return false;
|
if (userPerm !== 'rw' && userPerm !== 'admin') return false;
|
||||||
@@ -89,8 +93,7 @@ const ViewItem = ({
|
|||||||
}
|
}
|
||||||
}, [onDelete, onCopy]);
|
}, [onDelete, onCopy]);
|
||||||
|
|
||||||
const closeRenamePopover = useCallback((event) => {
|
const closeRenamePopover = useCallback(() => {
|
||||||
event.stopPropagation();
|
|
||||||
setRenamePopoverShow(false);
|
setRenamePopoverShow(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -177,7 +180,7 @@ const ViewItem = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isShowRenamePopover && (
|
{isShowRenamePopover && (
|
||||||
<Rename value={view.name} target={`metadata-view-dropdown-item-${view._id}`} toggle={closeRenamePopover} onSubmit={renameView} />
|
<Rename value={view.name} otherViewsName={otherViewsName} target={`metadata-view-dropdown-item-${view._id}`} toggle={closeRenamePopover} onSubmit={renameView} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
17
frontend/src/metadata/metadata-view-id-2-name.js
Normal file
17
frontend/src/metadata/metadata-view-id-2-name.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useMetadata } from './hooks';
|
||||||
|
|
||||||
|
const MetadataViewId2Name = ({ id }) => {
|
||||||
|
const { viewsMap } = useMetadata();
|
||||||
|
if (!id) return null;
|
||||||
|
const view = viewsMap[id];
|
||||||
|
if (!view) return null;
|
||||||
|
return (<>{view.name}</>);
|
||||||
|
};
|
||||||
|
|
||||||
|
MetadataViewId2Name.propTypes = {
|
||||||
|
id: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MetadataViewId2Name;
|
@@ -106,4 +106,5 @@ export {
|
|||||||
getColumnOptionIdsByNames,
|
getColumnOptionIdsByNames,
|
||||||
isLongTextValueExceedLimit,
|
isLongTextValueExceedLimit,
|
||||||
getValidLongTextValue,
|
getValidLongTextValue,
|
||||||
|
isValidViewName,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
@@ -96,6 +96,7 @@ export {
|
|||||||
isValidEmail,
|
isValidEmail,
|
||||||
ValidateFilter,
|
ValidateFilter,
|
||||||
DATE_MODIFIERS_REQUIRE_TERM,
|
DATE_MODIFIERS_REQUIRE_TERM,
|
||||||
|
isValidViewName,
|
||||||
} from './validate';
|
} from './validate';
|
||||||
export {
|
export {
|
||||||
getViewById,
|
getViewById,
|
||||||
|
@@ -6,3 +6,4 @@ export {
|
|||||||
export {
|
export {
|
||||||
isValidPosition,
|
isValidPosition,
|
||||||
} from './geolocation';
|
} from './geolocation';
|
||||||
|
export { isValidViewName } from './view';
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
import { gettext } from '../../../utils';
|
||||||
|
|
||||||
|
export const isValidViewName = (name, names) => {
|
||||||
|
if (typeof name !== 'string') {
|
||||||
|
return { isValid: false, message: gettext('Name should be string') };
|
||||||
|
}
|
||||||
|
name = name.trim();
|
||||||
|
if (name === '') {
|
||||||
|
return { isValid: false, message: gettext('Name is required') };
|
||||||
|
}
|
||||||
|
if (name.includes('/')) {
|
||||||
|
return { isValid: false, message: gettext('Name cannot contain slash') };
|
||||||
|
}
|
||||||
|
if (name.includes('\\')) {
|
||||||
|
return { isValid: false, message: gettext('Name cannot contain backslash') };
|
||||||
|
}
|
||||||
|
if (names.includes(name)) {
|
||||||
|
return { isValid: false, message: gettext('Name already exists') };
|
||||||
|
}
|
||||||
|
return { isValid: true, message: name };
|
||||||
|
};
|
@@ -1,76 +1,84 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
import { Form, Input, UncontrolledPopover } from 'reactstrap';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { Alert, Input } from 'reactstrap';
|
||||||
|
import { CustomizePopover } from '@seafile/sf-metadata-ui-component';
|
||||||
import { gettext } from '../../../../utils';
|
import { gettext } from '../../../../utils';
|
||||||
|
import { isValidViewName } from '../../../../_basic';
|
||||||
|
import { isEnter } from '../../../../_basic/utils/hotkey';
|
||||||
|
|
||||||
import '../index.css';
|
import '../index.css';
|
||||||
|
|
||||||
const Rename = ({ value, target, toggle, onSubmit }) => {
|
const Rename = ({ value, target, otherViewsName, toggle, onSubmit }) => {
|
||||||
const [inputValue, setInputValue] = useState(value || '');
|
const [inputValue, setInputValue] = useState(value || '');
|
||||||
|
const [errorMessage, setErrorMessage] = useState('');
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
|
|
||||||
const onChange = useCallback((e) => {
|
const onChange = useCallback((e) => {
|
||||||
setInputValue(e.target.value);
|
setInputValue(e.target.value);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSubmit = useCallback((e) => {
|
const onToggle = useCallback(() => {
|
||||||
e.preventDefault();
|
toggle();
|
||||||
e.stopPropagation();
|
|
||||||
onSubmit(inputValue);
|
|
||||||
}, [inputValue, onSubmit]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleClickOutSide = (event) => {
|
|
||||||
if (inputRef.current && !inputRef.current.contains(event.target)) {
|
|
||||||
toggle(event);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (inputRef.current) {
|
|
||||||
inputRef.current.select();
|
|
||||||
document.addEventListener('mousedown', handleClickOutSide);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('mousedown', handleClickOutSide);
|
|
||||||
};
|
|
||||||
}, [toggle]);
|
}, [toggle]);
|
||||||
|
|
||||||
|
const handleSubmit = useCallback((event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
const { isValid, message } = isValidViewName(inputValue, otherViewsName);
|
||||||
|
if (!isValid) {
|
||||||
|
setErrorMessage(message);
|
||||||
|
inputRef.current.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message === value) {
|
||||||
|
onToggle();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onSubmit(message);
|
||||||
|
}, [value, inputValue, otherViewsName, onSubmit, onToggle]);
|
||||||
|
|
||||||
|
const onKeyDown = useCallback((event) => {
|
||||||
|
if (isEnter(event)) {
|
||||||
|
handleSubmit(event);
|
||||||
|
}
|
||||||
|
}, [handleSubmit]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UncontrolledPopover
|
<CustomizePopover
|
||||||
className='sf-metadata-rename-view-popover'
|
className='sf-metadata-rename-view-popover'
|
||||||
isOpen={true}
|
|
||||||
toggle={toggle}
|
|
||||||
target={target}
|
target={target}
|
||||||
placement='right-start'
|
placement='right-start'
|
||||||
hideArrow={true}
|
hideArrow={true}
|
||||||
fade={false}
|
fade={false}
|
||||||
boundariesElement={document.body}
|
modifiers={{ preventOverflow: { boundariesElement: document.body } }}
|
||||||
|
canHide={!errorMessage}
|
||||||
|
hide={onToggle}
|
||||||
|
hideWithEsc={onToggle}
|
||||||
>
|
>
|
||||||
<div className='sf-metadata-rename-view-popover-header'>
|
<div className='sf-metadata-rename-view-popover-header'>
|
||||||
{gettext('Rename view')}
|
{gettext('Rename view')}
|
||||||
</div>
|
</div>
|
||||||
<div className="seafile-divider dropdown-divider"></div>
|
<div className="seafile-divider dropdown-divider"></div>
|
||||||
<div className='sf-metadata-rename-view-popover-body'>
|
<div className='sf-metadata-rename-view-popover-body'>
|
||||||
<Form onSubmit={handleSubmit}>
|
<Input
|
||||||
<Input
|
innerRef={inputRef}
|
||||||
innerRef={inputRef}
|
className="sf-metadata-view-rename-input"
|
||||||
className='sf-metadata-view-input'
|
value={inputValue}
|
||||||
type='text'
|
onChange={onChange}
|
||||||
id="rename-input"
|
autoFocus={true}
|
||||||
name='rename'
|
onBlur={handleSubmit}
|
||||||
value={inputValue}
|
onKeyDown={onKeyDown}
|
||||||
onChange={onChange}
|
/>
|
||||||
autoFocus={true}
|
{errorMessage && (<Alert color="danger" className="mt-2 mb-0">{errorMessage}</Alert>)}
|
||||||
/>
|
|
||||||
</Form>
|
|
||||||
</div>
|
</div>
|
||||||
</UncontrolledPopover>
|
</CustomizePopover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Rename.propTypes = {
|
Rename.propTypes = {
|
||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
target: PropTypes.string.isRequired,
|
target: PropTypes.string.isRequired,
|
||||||
|
otherViewsName: PropTypes.array,
|
||||||
toggle: PropTypes.func.isRequired,
|
toggle: PropTypes.func.isRequired,
|
||||||
onSubmit: PropTypes.func.isRequired,
|
onSubmit: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
@@ -550,13 +550,6 @@ class LibContentView extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
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 => {
|
||||||
@@ -1860,6 +1853,9 @@ class LibContentView extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (node.object.isDir()) { // isDir
|
if (node.object.isDir()) { // isDir
|
||||||
|
if (this.state.path.includes(PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES)) {
|
||||||
|
this.isNeedUpdateHistoryState = true;
|
||||||
|
}
|
||||||
this.showDir(node.path);
|
this.showDir(node.path);
|
||||||
} else {
|
} else {
|
||||||
if (Utils.isMarkdownFile(node.path)) {
|
if (Utils.isMarkdownFile(node.path)) {
|
||||||
@@ -2207,7 +2203,6 @@ class LibContentView extends React.Component {
|
|||||||
<MetadataProvider
|
<MetadataProvider
|
||||||
repoID={this.props.repoID}
|
repoID={this.props.repoID}
|
||||||
selectMetadataView={this.onTreeNodeClick}
|
selectMetadataView={this.onTreeNodeClick}
|
||||||
renameMetadataView={this.renameMetadataView}
|
|
||||||
hideMetadataView={this.hideFileMetadata}
|
hideMetadataView={this.hideFileMetadata}
|
||||||
>
|
>
|
||||||
<CollaboratorsProvider repoID={this.props.repoID}>
|
<CollaboratorsProvider repoID={this.props.repoID}>
|
||||||
|
Reference in New Issue
Block a user