1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-12 13:24:52 +00:00

optimize code (#6517)

This commit is contained in:
杨顺强
2024-08-08 18:08:09 +08:00
committed by GitHub
parent e2bb71e8c9
commit 33bfffa6c1
12 changed files with 163 additions and 90 deletions

View File

@@ -39,6 +39,7 @@ class Wiki extends Component {
seadoc_access_token: '', seadoc_access_token: '',
assets_url: '', assets_url: '',
wikiRepoId: null, wikiRepoId: null,
isUpdateBySide: false,
}; };
} }
@@ -85,19 +86,23 @@ class Wiki extends Component {
}; };
updateWikiConfig = (wikiConfig) => { updateWikiConfig = (wikiConfig) => {
this.setState({ this.setState({ config: new WikiConfig(wikiConfig || {}) });
config: new WikiConfig(wikiConfig || {}),
});
}; };
saveWikiConfig = (wikiConfig, onSuccess, onError) => { saveWikiConfig = (wikiConfig, isUpdateBySide = false) => {
wikiAPI.updateWiki2Config(wikiId, JSON.stringify(wikiConfig)).then(res => { wikiAPI.updateWiki2Config(wikiId, JSON.stringify(wikiConfig)).then(res => {
this.updateWikiConfig(wikiConfig); this.setState({
onSuccess && onSuccess(); config: new WikiConfig(wikiConfig),
isUpdateBySide,
});
if (isUpdateBySide) {
setTimeout(() => {
this.setState({ isUpdateBySide: false });
}, 300);
}
}).catch((error) => { }).catch((error) => {
let errorMsg = Utils.getErrorMsg(error); let errorMsg = Utils.getErrorMsg(error);
toaster.danger(errorMsg); toaster.danger(errorMsg);
onError && onError();
}); });
}; };
@@ -201,7 +206,7 @@ class Wiki extends Component {
this.updateDocumentTitle(name); this.updateDocumentTitle(name);
}; };
onUpdatePage = (pageId, newPage) => { onUpdatePage = (pageId, newPage, isUpdateBySide) => {
if (newPage.name === '') { if (newPage.name === '') {
toaster.danger(gettext('Page name cannot be empty')); toaster.danger(gettext('Page name cannot be empty'));
return; return;
@@ -218,7 +223,7 @@ class Wiki extends Component {
return page; return page;
}); });
const newConfig = { ...config, pages: newPages }; const newConfig = { ...config, pages: newPages };
this.saveWikiConfig(newConfig); this.saveWikiConfig(newConfig, isUpdateBySide);
}; };
updateDocumentTitle = (newTitle) => { updateDocumentTitle = (newTitle) => {
@@ -233,7 +238,6 @@ class Wiki extends Component {
closeSideBar={this.state.closeSideBar} closeSideBar={this.state.closeSideBar}
onCloseSide={this.onCloseSide} onCloseSide={this.onCloseSide}
config={this.state.config} config={this.state.config}
saveWikiConfig={this.saveWikiConfig}
updateWikiConfig={this.updateWikiConfig} updateWikiConfig={this.updateWikiConfig}
setCurrentPage={this.setCurrentPage} setCurrentPage={this.setCurrentPage}
currentPageId={this.state.currentPageId} currentPageId={this.state.currentPageId}
@@ -251,6 +255,7 @@ class Wiki extends Component {
seadoc_access_token={this.state.seadoc_access_token} seadoc_access_token={this.state.seadoc_access_token}
assets_url={this.state.assets_url} assets_url={this.state.assets_url}
onUpdatePage={this.onUpdatePage} onUpdatePage={this.onUpdatePage}
isUpdateBySide={this.state.isUpdateBySide}
/> />
<MediaQuery query="(max-width: 767.8px)"> <MediaQuery query="(max-width: 767.8px)">
<Modal isOpen={!this.state.closeSideBar} toggle={this.onCloseSide} contentClassName="d-none"></Modal> <Modal isOpen={!this.state.closeSideBar} toggle={this.onCloseSide} contentClassName="d-none"></Modal>

View File

@@ -20,6 +20,7 @@ const propTypes = {
assets_url: PropTypes.string, assets_url: PropTypes.string,
config: PropTypes.object, config: PropTypes.object,
currentPageId: PropTypes.string, currentPageId: PropTypes.string,
isUpdateBySide: PropTypes.bool,
onUpdatePage: PropTypes.func, onUpdatePage: PropTypes.func,
onAddWikiPage: PropTypes.func, onAddWikiPage: PropTypes.func,
}; };
@@ -35,16 +36,6 @@ class MainPanel extends Component {
this.scrollRef = React.createRef(); this.scrollRef = React.createRef();
} }
getHeaderHeight = () => {
const pageCover = document.getElementById('wiki-page-cover');
const pageCoverHeight = pageCover?.offsetHeight || 0;
const pageTitle = document.getElementById('wiki-page-title');
const pageTitleHeight = pageTitle?.offsetHeight || 0;
const topNavHeight = 44;
return pageCoverHeight + pageTitleHeight + topNavHeight;
};
static getDerivedStateFromProps(props, state) { static getDerivedStateFromProps(props, state) {
const { seadoc_access_token, currentPageId, config } = props; const { seadoc_access_token, currentPageId, config } = props;
const appConfig = window.app.config; const appConfig = window.app.config;
@@ -66,7 +57,7 @@ class MainPanel extends Component {
} }
render() { render() {
const { permission, pathExist, isDataLoading, isViewFile, config, onUpdatePage } = this.props; const { permission, pathExist, isDataLoading, isViewFile, config, onUpdatePage, isUpdateBySide } = this.props;
const { currentPageConfig = {} } = this.state; const { currentPageConfig = {} } = this.state;
const isViewingFile = pathExist && !isDataLoading && isViewFile; const isViewingFile = pathExist && !isDataLoading && isViewFile;
const isReadOnly = !(permission === 'rw'); const isReadOnly = !(permission === 'rw');
@@ -90,13 +81,12 @@ class MainPanel extends Component {
{isViewingFile && Utils.isSdocFile(this.props.path) && ( {isViewingFile && Utils.isSdocFile(this.props.path) && (
<div className='sdoc-scroll-container' id='sdoc-scroll-container' ref={this.scrollRef}> <div className='sdoc-scroll-container' id='sdoc-scroll-container' ref={this.scrollRef}>
<div className='wiki-editor-container'> <div className='wiki-editor-container'>
<RightHeader onUpdatePage={onUpdatePage} currentPageConfig={currentPageConfig} /> <RightHeader isUpdateBySide={isUpdateBySide} currentPageConfig={currentPageConfig} onUpdatePage={onUpdatePage} />
<SdocWikiEditor <SdocWikiEditor
document={this.props.editorContent} document={this.props.editorContent}
docUuid={this.state.docUuid} docUuid={this.state.docUuid}
isWikiReadOnly={isReadOnly} isWikiReadOnly={isReadOnly}
scrollRef={this.scrollRef} scrollRef={this.scrollRef}
getHeaderHeight={this.getHeaderHeight}
/> />
</div> </div>
</div> </div>

View File

@@ -21,7 +21,6 @@ const propTypes = {
closeSideBar: PropTypes.bool.isRequired, closeSideBar: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired, isLoading: PropTypes.bool.isRequired,
config: PropTypes.object.isRequired, config: PropTypes.object.isRequired,
saveWikiConfig: PropTypes.func.isRequired,
updateWikiConfig: PropTypes.func.isRequired, updateWikiConfig: PropTypes.func.isRequired,
setCurrentPage: PropTypes.func.isRequired, setCurrentPage: PropTypes.func.isRequired,
currentPageId: PropTypes.string, currentPageId: PropTypes.string,

View File

@@ -1,6 +1,7 @@
.wiki2-top-nav .wiki2-top-nav-item { .wiki2-top-nav .wiki2-top-nav-item {
align-items: center; align-items: center;
padding: 2px 6px; padding: 2px 6px;
max-width: 300px;
} }
.wiki2-top-nav>div:not(.wiki2-top-nav-item) { .wiki2-top-nav>div:not(.wiki2-top-nav-item) {

View File

@@ -50,7 +50,7 @@ function WikiTopNav({ config, currentPageId }) {
<Fragment key={item.id}> <Fragment key={item.id}>
<div className='wiki2-top-nav-item d-flex'> <div className='wiki2-top-nav-item d-flex'>
{item.icon ? <CustomIcon icon={item.icon} /> : <NavItemIcon symbol={'file'} disable={true} />} {item.icon ? <CustomIcon icon={item.icon} /> : <NavItemIcon symbol={'file'} disable={true} />}
{item.name} <span className='text-truncate'>{item.name}</span>
</div> </div>
{index !== paths.length - 1 && <div>/</div>} {index !== paths.length - 1 && <div>/</div>}
</Fragment> </Fragment>

View File

@@ -53,6 +53,22 @@ const getWikPageLink = (pageId) => {
return `${origin}${pathname}?page_id=${pageId}`; return `${origin}${pathname}?page_id=${pageId}`;
}; };
const throttle = (fn, delay) => {
let timer;
return function () {
let _this = this;
let args = arguments;
if (timer) {
return;
}
timer = setTimeout(function () {
fn.apply(_this, args);
timer = null;
}, delay);
};
};
export { export {
generatorBase64Code, generatorBase64Code,
generateUniqueId, generateUniqueId,
@@ -60,4 +76,5 @@ export {
getIconURL, getIconURL,
getCurrentPageConfig, getCurrentPageConfig,
getWikPageLink, getWikPageLink,
throttle,
}; };

View File

@@ -73,7 +73,8 @@ class PageItem extends Component {
const { name, id } = this.props.page; const { name, id } = this.props.page;
const pageName = this.state.pageName.trim(); const pageName = this.state.pageName.trim();
if (pageName !== name) { if (pageName !== name) {
this.props.onUpdatePage(id, { name: pageName }); const isUpdateBySide = true;
this.props.onUpdatePage(id, { name: pageName }, isUpdateBySide);
} }
}; };

View File

@@ -3,9 +3,9 @@ import PropTypes from 'prop-types';
import PageCover from './page-cover'; import PageCover from './page-cover';
import PageTitle from './page-title'; import PageTitle from './page-title';
function RightHeader({ currentPageConfig, onUpdatePage }) { function RightHeader({ isUpdateBySide, currentPageConfig, onUpdatePage }) {
const props = { currentPageConfig, onUpdatePage }; const props = { isUpdateBySide, currentPageConfig, onUpdatePage };
return ( return (
<> <>
<PageCover {...props} /> <PageCover {...props} />
@@ -15,6 +15,7 @@ function RightHeader({ currentPageConfig, onUpdatePage }) {
} }
RightHeader.propTypes = { RightHeader.propTypes = {
isUpdateBySide: PropTypes.bool,
currentPageConfig: PropTypes.object, currentPageConfig: PropTypes.object,
onUpdatePage: PropTypes.func, onUpdatePage: PropTypes.func,
}; };

View File

@@ -0,0 +1,111 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { throttle } from '../utils';
function PageTitleEditor({ isUpdateBySide, currentPageConfig, onUpdatePage }) {
const [pageName, setPageName] = useState(currentPageConfig.name);
const isChineseInput = useRef(false);
const contentEditableRef = useRef(null);
const selectionRef = useRef(null);
const saveSelection = () => {
const sel = window.getSelection();
if (sel.rangeCount > 0) {
const range = sel.getRangeAt(0);
selectionRef.current = {
startContainer: range.startContainer,
startOffset: range.startOffset,
endContainer: range.endContainer,
endOffset: range.endOffset
};
}
};
const restoreSelection = () => {
if (selectionRef.current) {
const { startContainer, startOffset, endContainer, endOffset } = selectionRef.current;
const range = window.document.createRange();
range.setStart(startContainer, startOffset);
range.setEnd(endContainer, endOffset);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
};
const onKeyDown = (event) => {
if (event.keyCode === 13) {
event.preventDefault();
event.stopPropagation();
}
};
const onCompositionStart = useCallback(() => {
isChineseInput.current = true;
}, []);
const onCompositionEnd = useCallback((e) => {
isChineseInput.current = false;
setPageName(e.target.innerText);
saveSelection();
const newName = e.target.innerText.trim();
const { id, icon } = currentPageConfig;
const pageConfig = { name: newName, icon };
const delayUpdate = throttle(onUpdatePage, 500);
delayUpdate(id, pageConfig);
}, [currentPageConfig, onUpdatePage]);
const handleInput = useCallback((e) => {
saveSelection();
if (isChineseInput.current === false) {
setPageName(e.target.innerText);
const newName = e.target.innerText.trim();
if (newName === pageName) return;
const { id, icon } = currentPageConfig;
const pageConfig = { name: newName, icon };
const delayUpdate = throttle(onUpdatePage, 500);
delayUpdate(id, pageConfig);
}
}, [currentPageConfig, onUpdatePage, pageName]);
useEffect(() => {
if (pageName !== currentPageConfig.name && isUpdateBySide) {
setPageName(currentPageConfig.name);
selectionRef.current = null;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentPageConfig.name, isUpdateBySide]);
useEffect(() => {
restoreSelection();
}, [pageName]);
return (
<div
className='wiki-sdoc-title'
contentEditable
suppressContentEditableWarning
ref={contentEditableRef}
onInput={handleInput}
onKeyDown={onKeyDown}
onCompositionStart={onCompositionStart}
onCompositionEnd={onCompositionEnd}
>
{pageName}
</div>
);
}
PageTitleEditor.propTypes = {
isUpdateBySide: PropTypes.bool,
currentPageConfig: PropTypes.object,
onUpdatePage: PropTypes.func,
};
export default PageTitleEditor;

View File

@@ -97,3 +97,7 @@
.main-panel-center .cur-view-content .wiki-sdoc-title:focus { .main-panel-center .cur-view-content .wiki-sdoc-title:focus {
box-shadow: none; box-shadow: none;
} }
.main-panel-center .cur-view-content .wiki-sdoc-title:focus-visible {
outline: none;
}

View File

@@ -1,69 +1,22 @@
import React, { useCallback, useEffect, useState, useRef } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classnames from 'classnames'; import classnames from 'classnames';
import { Input } from 'reactstrap';
import { gettext } from '../../../utils/constants'; import { gettext } from '../../../utils/constants';
import { WIKI_COVER_LIST } from '../constant'; import { WIKI_COVER_LIST } from '../constant';
import PageIcon from './page-icon'; import PageIcon from './page-icon';
import { generateARandomEmoji, generateEmojiIcon } from '../utils/emoji-utils'; import { generateARandomEmoji, generateEmojiIcon } from '../utils/emoji-utils';
import PageTitleEditor from './page-title-editor';
import './page-title.css'; import './page-title.css';
const propTypes = { const propTypes = {
isUpdateBySide: PropTypes.bool,
currentPageConfig: PropTypes.object.isRequired, currentPageConfig: PropTypes.object.isRequired,
onUpdatePage: PropTypes.func.isRequired, onUpdatePage: PropTypes.func.isRequired,
}; };
const PageTitle = ({ currentPageConfig, onUpdatePage }) => { const PageTitle = ({ isUpdateBySide, currentPageConfig, onUpdatePage }) => {
const [isShowController, setIsShowController] = useState(false); const [isShowController, setIsShowController] = useState(false);
const [pageName, setPageName] = useState(currentPageConfig.name);
const isChineseInput = useRef(false);
const isTyping = useRef(false);
const timer = useRef(null);
useEffect(() => {
if (pageName !== currentPageConfig.name && isTyping.current === false) {
setPageName(currentPageConfig.name);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentPageConfig.name]);
const onKeyDown = useCallback(() => {
isTyping.current = true;
if (timer.current) {
clearTimeout(timer.current);
timer.current = null;
}
}, []);
const onKeyUp = useCallback(() => {
timer.current = setTimeout(() => {
isTyping.current = false;
}, 2000);
}, []);
const onCompositionStart = useCallback(() => {
isChineseInput.current = true;
}, []);
const onCompositionEnd = useCallback((e) => {
isChineseInput.current = false;
const newName = e.target.value.trim();
const { id, icon } = currentPageConfig;
const pageConfig = { name: newName, icon };
onUpdatePage && onUpdatePage(id, pageConfig);
}, [currentPageConfig, onUpdatePage]);
const onChange = useCallback((e) => {
setPageName(e.target.value);
if (isChineseInput.current === false) {
const newName = e.target.value.trim();
if (newName === pageName) return;
const { id, icon } = currentPageConfig;
const pageConfig = { name: newName, icon };
onUpdatePage && onUpdatePage(id, pageConfig);
}
}, [currentPageConfig, onUpdatePage, pageName]);
const onMouseEnter = useCallback(() => { const onMouseEnter = useCallback(() => {
setIsShowController(true); setIsShowController(true);
@@ -116,16 +69,7 @@ const PageTitle = ({ currentPageConfig, onUpdatePage }) => {
</div> </div>
)} )}
</div> </div>
<Input <PageTitleEditor isUpdateBySide={isUpdateBySide} currentPageConfig={currentPageConfig} onUpdatePage={onUpdatePage} />
className='wiki-sdoc-title'
bsSize="lg"
onCompositionStart={onCompositionStart}
onCompositionEnd={onCompositionEnd}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
onChange={onChange}
value={pageName}
/>
</div> </div>
); );
}; };

View File

@@ -74,7 +74,7 @@ body {
justify-content: center; justify-content: center;
min-height: 800px; min-height: 800px;
width: 100%; width: 100%;
padding: 0 142px; padding: 0 142px 60px;
box-shadow: none; box-shadow: none;
border: none; border: none;
} }