refactor: lint code

This commit is contained in:
fantasticit 2022-05-01 22:07:22 +08:00
parent 1dac849b61
commit 76cdcff589
48 changed files with 955 additions and 137 deletions

6
.eslintignore Normal file
View File

@ -0,0 +1,6 @@
node_modules
**/.next/**
**/_next/**
**/dist/**
./packages/client/src/tiptap/next.config.js
./packages/client/src/tiptap/wrappers/mind/mind-elixir/iconfont/iconfont.js

47
.eslintrc.client.js Normal file
View File

@ -0,0 +1,47 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 8,
sourceType: 'module',
ecmaFeatures: {
impliedStrict: true,
experimentalObjectRestSpread: true,
},
allowImportExportEverywhere: true,
project: ['./packages/client/tsconfig.json'],
},
plugins: ['@typescript-eslint', 'react-hooks'],
extends: ['eslint:recommended', 'plugin:react/recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
settings: {
react: {
version: 'detect',
},
},
env: {
es6: true,
browser: true,
node: true,
},
rules: {
'func-names': 0,
'no-shadow': 0,
'@typescript-eslint/no-shadow': 0,
'@typescript-eslint/explicit-function-return-type': 0,
'@typescript-eslint/no-unused-vars': [0, { argsIgnorePattern: '^_' }],
'@typescript-eslint/no-use-before-define': 0,
'@typescript-eslint/ban-ts-ignore': 0,
'@typescript-eslint/no-empty-function': 0,
'@typescript-eslint/ban-ts-comment': 0,
'@typescript-eslint/no-var-requires': 0,
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/no-this-alias': 0,
'@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/ban-types': 0,
'react-hooks/rules-of-hooks': 2,
'react-hooks/exhaustive-deps': 1,
'react/prop-types': 0,
'testing-library/no-unnecessary-act': 0,
'react/react-in-jsx-scope': 0,
},
ignorePatterns: ['dist/', 'node_modules', 'scripts', 'examples'],
};

19
.eslintrc.server.js Normal file
View File

@ -0,0 +1,19 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: './packages/server/tsconfig.json',
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: ['plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
root: true,
env: {
node: true,
jest: true,
},
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};

View File

@ -20,8 +20,10 @@
"pm2": "pnpm run pm2:server && pnpm run pm2:client", "pm2": "pnpm run pm2:server && pnpm run pm2:client",
"pm2:server": "pnpm run --dir packages/server pm2", "pm2:server": "pnpm run --dir packages/server pm2",
"pm2:client": "pnpm run --dir packages/client pm2", "pm2:client": "pnpm run --dir packages/client pm2",
"lint:client": "eslint --fix './packages/client/**/*.{ts,tsx,js,jsx}' -c '.eslintrc.client.js'",
"lint:server": "eslint --fix './packages/server/src/*.{ts,js}' -c '.eslintrc.server.js'",
"format": "concurrently \"pnpm:format:*\"", "format": "concurrently \"pnpm:format:*\"",
"format:ts": "prettier --write --parser typescript \"git status\"", "format:ts": "prettier --write --parser typescript \"packages/**/*.{ts,tsx,js,jsx}\"",
"format:css": "stylelint --fix --formatter verbose --allow-empty-input \"packages/**/*.{css,scss,sass}\"", "format:css": "stylelint --fix --formatter verbose --allow-empty-input \"packages/**/*.{css,scss,sass}\"",
"prepare": "husky install", "prepare": "husky install",
"precommit": "lint-staged" "precommit": "lint-staged"
@ -36,6 +38,16 @@
"node": ">=16.5.0" "node": ">=16.5.0"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.21.0",
"@typescript-eslint/parser": "^5.21.0",
"eslint": "^8.14.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.5.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"husky": "^7.0.4", "husky": "^7.0.4",
"lint-staged": "^12.4.1", "lint-staged": "^12.4.1",
"prettier": "^2.3.2", "prettier": "^2.3.2",

View File

@ -1,10 +1,9 @@
const semi = require('@douyinfe/semi-next').default({}); /* eslint-disable */
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const { getConfig } = require('@think/config'); const { getConfig } = require('@think/config');
const config = getConfig(); const config = getConfig();
/** @type {import('next').NextConfig} */ const nextConfig = require('@douyinfe/semi-next').default({})({
const nextConfig = semi({
assetPrefix: config.assetPrefix, assetPrefix: config.assetPrefix,
env: { env: {
SERVER_API_URL: config?.client?.apiUrl, SERVER_API_URL: config?.client?.apiUrl,

View File

@ -22,7 +22,7 @@ export const Banner: React.FC<IProps> = ({ type, description, duration = 0 }) =>
return () => { return () => {
clearTimeout(timer.current); clearTimeout(timer.current);
}; };
}, [duration]); }, [duration, toggleVisible]);
if (!visible) return null; if (!visible) return null;

View File

@ -47,7 +47,7 @@ const defaultEmpty = () => {
); );
}; };
const runRender = (fn, ...args) => (typeof fn === 'function' ? fn.apply(null, args) : fn); const runRender = (fn, ...args) => (typeof fn === 'function' ? fn(...args) : fn);
export const DataRender: React.FC<IProps> = ({ export const DataRender: React.FC<IProps> = ({
loading, loading,

View File

@ -28,7 +28,7 @@ export const LoadingWrap: React.FC<IProps> = ({ loading, delay = 200, loadingCon
return () => { return () => {
clearTimeout(timer.current); clearTimeout(timer.current);
}; };
}, [delay, loading]); }, [delay, loading, toggleShowLoading]);
if (loading) { if (loading) {
return showLoading ? loadingContent : null; return showLoading ? loadingContent : null;

View File

@ -31,6 +31,7 @@ interface IProps {
const { Paragraph } = Typography; const { Paragraph } = Typography;
const { Column } = Table; const { Column } = Table;
// eslint-disable-next-line react/display-name
const renderChecked = (onChange, authKey: 'readable' | 'editable') => (checked, docAuth) => { const renderChecked = (onChange, authKey: 'readable' | 'editable') => (checked, docAuth) => {
const handle = (evt) => { const handle = (evt) => {
const data = { const data = {
@ -67,8 +68,8 @@ export const DocumentCollaboration: React.FC<IProps> = ({ wikiId, documentId })
}; };
useEffect(() => { useEffect(() => {
const handler = (users) => { const handler = (mentionUsers) => {
const newCollaborationUsers = users const newCollaborationUsers = mentionUsers
.filter(Boolean) .filter(Boolean)
.filter((state) => state.user) .filter((state) => state.user)
.map((state) => ({ ...state.user, clientId: state.clientId })); .map((state) => ({ ...state.user, clientId: state.clientId }));
@ -90,6 +91,7 @@ export const DocumentCollaboration: React.FC<IProps> = ({ wikiId, documentId })
setCollaborationUsers(newCollaborationUsers); setCollaborationUsers(newCollaborationUsers);
}; };
event.on(JOIN_USER, handler); event.on(JOIN_USER, handler);
return () => { return () => {

View File

@ -16,10 +16,11 @@ interface IProps {
const { Text } = Typography; const { Text } = Typography;
export const CommentItem: React.FC<IProps> = ({ comment, replyComment, editComment, deleteComment }) => { export const CommentItem: React.FC<IProps> = ({ comment, replyComment, editComment, deleteComment }) => {
if (!comment) return null;
const { user } = useUser(); const { user } = useUser();
const { createUser = {} } = comment; const { createUser = {} } = comment;
if (!comment) return null;
return ( return (
<div className={styles.wrap}> <div className={styles.wrap}>
<div className={styles.leftWrap}> <div className={styles.leftWrap}>

View File

@ -33,7 +33,7 @@ export const DocumentDeletor: React.FC<IProps> = ({ wikiId, documentId, onDelete
okButtonProps: { loading, type: 'danger' }, okButtonProps: { loading, type: 'danger' },
style: { maxWidth: '96vw' }, style: { maxWidth: '96vw' },
}); });
}, [wikiId, documentId, api, loading, onDelete]); }, [wikiId, api, loading, onDelete]);
return ( return (
<Text type="danger" onClick={deleteAction}> <Text type="danger" onClick={deleteAction}>

View File

@ -15,6 +15,7 @@ interface IProps {
const { Text } = Typography; const { Text } = Typography;
const { Column } = Table; const { Column } = Table;
// eslint-disable-next-line react/display-name
const renderChecked = (onChange, authKey: 'readable' | 'editable') => (checked, data) => { const renderChecked = (onChange, authKey: 'readable' | 'editable') => (checked, data) => {
const handle = (evt) => { const handle = (evt) => {
const ret = { const ret = {

View File

@ -27,8 +27,6 @@ interface IProps {
} }
export const DocumentReader: React.FC<IProps> = ({ documentId }) => { export const DocumentReader: React.FC<IProps> = ({ documentId }) => {
if (!documentId) return null;
const [container, setContainer] = useState<HTMLDivElement>(); const [container, setContainer] = useState<HTMLDivElement>();
const { width: windowWidth } = useWindowSize(); const { width: windowWidth } = useWindowSize();
const { width, fontSize } = useDocumentStyle(); const { width, fontSize } = useDocumentStyle();
@ -43,6 +41,8 @@ export const DocumentReader: React.FC<IProps> = ({ documentId }) => {
Router.push(`/wiki/${document.wikiId}/document/${document.id}/edit`); Router.push(`/wiki/${document.wikiId}/document/${document.id}/edit`);
}, [document]); }, [document]);
if (!documentId) return null;
return ( return (
<div className={styles.wrap}> <div className={styles.wrap}>
<Header className={styles.headerWrap}> <Header className={styles.headerWrap}>

View File

@ -12,7 +12,7 @@ interface IProps {
export const DocumentContent: React.FC<IProps> = ({ document, createUserContainerSelector }) => { export const DocumentContent: React.FC<IProps> = ({ document, createUserContainerSelector }) => {
const c = safeJSONParse(document.content); const c = safeJSONParse(document.content);
let json = c.default || c; const json = c.default || c;
const editor = useEditor({ const editor = useEditor({
editable: false, editable: false,

View File

@ -37,8 +37,6 @@ interface IProps {
} }
export const DocumentPublicReader: React.FC<IProps> = ({ documentId, hideLogo = true }) => { export const DocumentPublicReader: React.FC<IProps> = ({ documentId, hideLogo = true }) => {
if (!documentId) return null;
const { data, loading, error, query } = usePublicDocument(documentId); const { data, loading, error, query } = usePublicDocument(documentId);
const { width, fontSize } = useDocumentStyle(); const { width, fontSize } = useDocumentStyle();
const editorWrapClassNames = useMemo(() => { const editorWrapClassNames = useMemo(() => {
@ -72,6 +70,8 @@ export const DocumentPublicReader: React.FC<IProps> = ({ documentId, hideLogo =
}); });
}, [error, query]); }, [error, query]);
if (!documentId) return null;
return ( return (
<Layout className={styles.wrap}> <Layout className={styles.wrap}>
<Header className={styles.headerWrap}> <Header className={styles.headerWrap}>

View File

@ -32,7 +32,7 @@ export const DocumentVersion: React.FC<IProps> = ({ documentId, onSelect }) => {
const close = useCallback(() => { const close = useCallback(() => {
toggleVisible(false); toggleVisible(false);
setSelectedVersion(null); setSelectedVersion(null);
}, []); }, [toggleVisible]);
const select = useCallback( const select = useCallback(
(version) => { (version) => {
@ -46,20 +46,20 @@ export const DocumentVersion: React.FC<IProps> = ({ documentId, onSelect }) => {
if (!selectedVersion || !onSelect) return; if (!selectedVersion || !onSelect) return;
onSelect(safeJSONParse(selectedVersion.data, { default: {} }).default); onSelect(safeJSONParse(selectedVersion.data, { default: {} }).default);
close(); close();
}, [selectedVersion]); }, [selectedVersion, close, onSelect]);
useEffect(() => { useEffect(() => {
if (visible) { if (visible) {
refresh(); refresh();
} }
}, [visible]); }, [visible, refresh]);
useEffect(() => { useEffect(() => {
if (!editor) return; if (!editor) return;
if (!data.length) return; if (!data.length) return;
if (selectedVersion) return; if (selectedVersion) return;
select(data[0]); select(data[0]);
}, [editor, data, selectedVersion]); }, [editor, data, selectedVersion, select]);
return ( return (
<> <>

View File

@ -60,15 +60,12 @@ export const GridSelect = ({
[onSelect] [onSelect]
); );
const onHover = useCallback( const onHover = useCallback(({ x, y, isCellDisabled }) => {
debounce(({ x, y, isCellDisabled }) => { if (isCellDisabled) {
if (isCellDisabled) { return setHoverCell(null);
return setHoverCell(null); }
} setHoverCell({ x, y });
setHoverCell({ x, y }); }, []);
}, 5),
[disabled]
);
const cells = useMemo(() => { const cells = useMemo(() => {
const cells = []; const cells = [];

View File

@ -40,7 +40,7 @@ const MessagesRender = ({ messageData, loading, error, onClick = null, page = 1,
<> <>
{messages.map((msg) => { {messages.map((msg) => {
return ( return (
<div className={styles.itemWrap} onClick={() => handleRead(msg.id)}> <div key={msg.id} className={styles.itemWrap} onClick={() => handleRead(msg.id)}>
<Link href={msg.url}> <Link href={msg.url}>
<a className={styles.item}> <a className={styles.item}>
<div className={styles.leftWrap}> <div className={styles.leftWrap}>
@ -140,7 +140,7 @@ const MessageBox = () => {
readMessage(msg.id); readMessage(msg.id);
}, },
}); });
}, [unreadMsgs]); }, [unreadMsgs, readMessage]);
return ( return (
<Dropdown <Dropdown

View File

@ -112,7 +112,7 @@ export const Resizeable: React.FC<IProps> = ({
}, },
}, },
}); });
}, [maxWidth, isEditable]); }, [maxWidth, isEditable, onChange, onChangeEnd]);
useEffect(() => { useEffect(() => {
Object.assign($container.current.style, { Object.assign($container.current.style, {

View File

@ -40,7 +40,6 @@ interface IProps {
} }
export const Editor: React.FC<IProps> = ({ user, data, loading, error, updateTemplate, deleteTemplate }) => { export const Editor: React.FC<IProps> = ({ user, data, loading, error, updateTemplate, deleteTemplate }) => {
if (!user) return null;
const { width: windowWidth } = useWindowSize(); const { width: windowWidth } = useWindowSize();
const [title, setTitle] = useState(data.title); const [title, setTitle] = useState(data.title);
const provider = useMemo(() => { const provider = useMemo(() => {
@ -51,7 +50,7 @@ export const Editor: React.FC<IProps> = ({ user, data, loading, error, updateTem
user, user,
docType: 'template', docType: 'template',
}); });
}, [data, user.token]); }, [data, user]);
const editor = useEditor({ const editor = useEditor({
editable: true, editable: true,
extensions: [...BaseKit, DocumentWithTitle, getCollaborationExtension(provider)], extensions: [...BaseKit, DocumentWithTitle, getCollaborationExtension(provider)],
@ -59,7 +58,9 @@ export const Editor: React.FC<IProps> = ({ user, data, loading, error, updateTem
try { try {
const title = transaction.doc.content.firstChild.content.firstChild.textContent; const title = transaction.doc.content.firstChild.content.firstChild.textContent;
setTitle(title); setTitle(title);
} catch (e) {} } catch (e) {
//
}
}, },
}); });
const [isPublic, setPublic] = useState(false); const [isPublic, setPublic] = useState(false);
@ -76,7 +77,7 @@ export const Editor: React.FC<IProps> = ({ user, data, loading, error, updateTem
deleteTemplate().then(() => { deleteTemplate().then(() => {
goback(); goback();
}); });
}, [deleteTemplate]); }, [deleteTemplate, goback]);
useEffect(() => { useEffect(() => {
if (!data) return; if (!data) return;
@ -99,6 +100,8 @@ export const Editor: React.FC<IProps> = ({ user, data, loading, error, updateTem
}; };
}, []); }, []);
if (!user) return null;
return ( return (
<div className={styles.wrap}> <div className={styles.wrap}>
<header> <header>

View File

@ -38,7 +38,7 @@ export const TemplateList: React.FC<IProps> = ({
const start = (page - 1) * pageSize; const start = (page - 1) * pageSize;
const end = page * pageSize; const end = page * pageSize;
return arr.slice(start, end); return arr.slice(start, end);
}, [data, page]); }, [data, page, pageSize]);
return ( return (
<DataRender <DataRender

View File

@ -21,8 +21,6 @@ interface IProps {
} }
export const Editor: React.FC<IProps> = ({ user, data, loading, error }) => { export const Editor: React.FC<IProps> = ({ user, data, loading, error }) => {
if (!user) return null;
const c = safeJSONParse(data.content); const c = safeJSONParse(data.content);
let json = c.default || c; let json = c.default || c;
@ -44,6 +42,8 @@ export const Editor: React.FC<IProps> = ({ user, data, loading, error }) => {
return width === 'standardWidth' ? styles.isStandardWidth : styles.isFullWidth; return width === 'standardWidth' ? styles.isStandardWidth : styles.isFullWidth;
}, [width]); }, [width]);
if (!user) return null;
return ( return (
<div className={styles.wrap}> <div className={styles.wrap}>
<Layout className={styles.contentWrap}> <Layout className={styles.contentWrap}>

View File

@ -26,7 +26,7 @@ export const Documents: React.FC<IProps> = ({ wikiId }) => {
const [publicDocumentIds, setPublicDocumentIds] = useState([]); // 公开的 const [publicDocumentIds, setPublicDocumentIds] = useState([]); // 公开的
const privateDocumentIds = useMemo(() => { const privateDocumentIds = useMemo(() => {
return documents.filter((doc) => !publicDocumentIds.includes(doc.id)).map((doc) => doc.id); return documents.filter((doc) => !publicDocumentIds.includes(doc.id)).map((doc) => doc.id);
}, [tocs, publicDocumentIds]); }, [documents, publicDocumentIds]);
const submit = () => { const submit = () => {
const data = { nextStatus, publicDocumentIds, privateDocumentIds }; const data = { nextStatus, publicDocumentIds, privateDocumentIds };
@ -77,7 +77,7 @@ export const Documents: React.FC<IProps> = ({ wikiId }) => {
if (!documents.length) return; if (!documents.length) return;
const activeIds = documents.filter((doc) => isPublicDocument(doc.status)).map((doc) => doc.id); const activeIds = documents.filter((doc) => isPublicDocument(doc.status)).map((doc) => doc.id);
setPublicDocumentIds(activeIds); setPublicDocumentIds(activeIds);
}, [tocs]); }, [tocs, documents]);
return ( return (
<div className={styles.wrap}> <div className={styles.wrap}>
@ -115,7 +115,11 @@ export const Documents: React.FC<IProps> = ({ wikiId }) => {
</Title> </Title>
<RadioGroup direction="vertical" value={nextStatus} onChange={(e) => setNextStatus(e.target.value)}> <RadioGroup direction="vertical" value={nextStatus} onChange={(e) => setNextStatus(e.target.value)}>
{WIKI_STATUS_LIST.map((status) => { {WIKI_STATUS_LIST.map((status) => {
return <Radio value={status.value}>{status.label}</Radio>; return (
<Radio key={status.value} value={status.value}>
{status.label}
</Radio>
);
})} })}
</RadioGroup> </RadioGroup>
</div> </div>

View File

@ -20,7 +20,7 @@ export const AddUser: React.FC<IProps> = ({ visible, toggleVisible, onOk }) => {
setUserName(''); setUserName('');
toggleVisible(false); toggleVisible(false);
}); });
}, [onOk, userName, userRole]); }, [onOk, userName, userRole, toggleVisible]);
return ( return (
<Modal <Modal
@ -40,7 +40,11 @@ export const AddUser: React.FC<IProps> = ({ visible, toggleVisible, onOk }) => {
<Space> <Space>
<Select value={userRole} onChange={setUserRole} style={{ width: 120 }}> <Select value={userRole} onChange={setUserRole} style={{ width: 120 }}>
{WIKI_USER_ROLES.map((wikiStatus) => { {WIKI_USER_ROLES.map((wikiStatus) => {
return <Select.Option value={wikiStatus.value}>{wikiStatus.label}</Select.Option>; return (
<Select.Option key={wikiStatus.value} value={wikiStatus.value}>
{wikiStatus.label}
</Select.Option>
);
})} })}
</Select> </Select>
<Input <Input

View File

@ -15,7 +15,7 @@ export const EditUser: React.FC<IProps> = ({ visible, toggleVisible, onOk }) =>
setUserRole(WikiUserRole.normal); setUserRole(WikiUserRole.normal);
toggleVisible(false); toggleVisible(false);
}); });
}, [onOk, userRole]); }, [onOk, userRole, toggleVisible]);
return ( return (
<Modal <Modal
@ -33,7 +33,11 @@ export const EditUser: React.FC<IProps> = ({ visible, toggleVisible, onOk }) =>
) : null} ) : null}
<Select value={userRole} onChange={setUserRole} style={{ width: '100%' }}> <Select value={userRole} onChange={setUserRole} style={{ width: '100%' }}>
{WIKI_USER_ROLES.map((wikiStatus) => { {WIKI_USER_ROLES.map((wikiStatus) => {
return <Select.Option value={wikiStatus.value}>{wikiStatus.label}</Select.Option>; return (
<Select.Option key={wikiStatus.value} value={wikiStatus.value}>
{wikiStatus.label}
</Select.Option>
);
})} })}
</Select> </Select>
<Button theme="solid" block style={{ margin: '24px 0' }} onClick={handleOk}> <Button theme="solid" block style={{ margin: '24px 0' }} onClick={handleOk}>

View File

@ -47,7 +47,7 @@ export const WikiTocs: React.FC<IProps> = ({
return () => { return () => {
event.off(REFRESH_TOCS, handler); event.off(REFRESH_TOCS, handler);
}; };
}, []); }, [refresh]);
return ( return (
<div className={styles.wrap}> <div className={styles.wrap}>

View File

@ -56,7 +56,7 @@ const AddDocument = () => {
return () => { return () => {
event.off(CREATE_DOCUMENT, handler); event.off(CREATE_DOCUMENT, handler);
}; };
}, []); }, [toggleVisible]);
return ( return (
<DocumenCreatorForm wikiId={wikiId} parentDocumentId={documentId} visible={visible} toggleVisible={toggleVisible} /> <DocumenCreatorForm wikiId={wikiId} parentDocumentId={documentId} visible={visible} toggleVisible={toggleVisible} />
@ -85,7 +85,7 @@ export const Tree = ({ data, docAsLink, getDocLink, parentIds, activeId, isShare
{isShareMode ? null : renderBtn(item)} {isShareMode ? null : renderBtn(item)}
</div> </div>
), ),
[isShareMode] [isShareMode, docAsLink, getDocLink, renderBtn]
); );
useEffect(() => { useEffect(() => {

View File

@ -148,12 +148,12 @@ export const usePublicDocument = (documentId: string) => {
}) })
.catch(setError); .catch(setError);
}, },
[documentId, error] [fetch, documentId]
); );
useEffect(() => { useEffect(() => {
queryData(); queryData();
}, [documentId]); }, [documentId, queryData]);
return { return {
data: document, data: document,

View File

@ -14,18 +14,21 @@ export const useUser = () => {
window.localStorage.removeItem('token'); window.localStorage.removeItem('token');
mutate(null); mutate(null);
Router.replace('/login'); Router.replace('/login');
}, []); }, [mutate]);
const login = useCallback((data) => { const login = useCallback(
HttpClient.post<IUser>('/user/login', data).then((res) => { (data) => {
const user = res as unknown as ILoginUser; HttpClient.post<IUser>('/user/login', data).then((res) => {
mutate(user); const user = res as unknown as ILoginUser;
setStorage('user', JSON.stringify(user)); mutate(user);
user.token && setStorage('token', user.token); setStorage('user', JSON.stringify(user));
const next = router.query?.redirect || '/'; user.token && setStorage('token', user.token);
Router.replace(next as string); const next = router.query?.redirect || '/';
}); Router.replace(next as string);
}, []); });
},
[mutate, router.query?.redirect]
);
const updateUser = async (patch: Pick<IUser, 'email' | 'avatar'>) => { const updateUser = async (patch: Pick<IUser, 'email' | 'avatar'>) => {
const res = await HttpClient.patch('/user/update', patch); const res = await HttpClient.patch('/user/update', patch);
@ -36,7 +39,7 @@ export const useUser = () => {
useEffect(() => { useEffect(() => {
mutate(); mutate();
}, []); }, [mutate]);
return { return {
user: data, user: data,

View File

@ -1,3 +1,4 @@
/* eslint-disable */
'use strict'; 'use strict';
var deselectCurrent = require('toggle-selection'); var deselectCurrent = require('toggle-selection');
@ -59,7 +60,7 @@ function copy(text, options) {
window.clipboardData.clearData(); window.clipboardData.clearData();
var format = clipboardToIE11Formatting[options.format] || clipboardToIE11Formatting['default']; var format = clipboardToIE11Formatting[options.format] || clipboardToIE11Formatting['default'];
if (Array.isArray(text)) { if (Array.isArray(text)) {
text.forEach((item) => { text.forEach(function (item) {
if (typeof item === 'string') { if (typeof item === 'string') {
window.clipboardData.setData(item, item); window.clipboardData.setData(item, item);
} else { } else {
@ -73,7 +74,7 @@ function copy(text, options) {
// all other browsers // all other browsers
e.clipboardData.clearData(); e.clipboardData.clearData();
if (Array.isArray(text)) { if (Array.isArray(text)) {
text.forEach((item) => { text.forEach(function (item) {
if (typeof item === 'string') { if (typeof item === 'string') {
e.clipboardData.setData(item, item); e.clipboardData.setData(item, item);
} else { } else {
@ -106,7 +107,7 @@ function copy(text, options) {
debug && console.warn('trying IE specific stuff'); debug && console.warn('trying IE specific stuff');
try { try {
if (Array.isArray(text)) { if (Array.isArray(text)) {
text.forEach((item) => { text.forEach(function (item) {
if (typeof item === 'string') { if (typeof item === 'string') {
window.clipboardData.setData(item, item); window.clipboardData.setData(item, item);
} else { } else {

View File

@ -39,7 +39,7 @@ export const useDocumentStyle = () => {
useEffect(() => { useEffect(() => {
mutate(); mutate();
}, []); }, [mutate]);
return { return {
width: (data && data.width) || DEFAULT_WIDTH, width: (data && data.width) || DEFAULT_WIDTH,

View File

@ -1,4 +1,4 @@
import { useEffect, useRef } from 'react'; import { useCallback, useEffect, useRef } from 'react';
import useSWR from 'swr'; import useSWR from 'swr';
import { useWindowSize } from 'hooks/use-window-size'; import { useWindowSize } from 'hooks/use-window-size';
import { setStorage, getStorage } from 'helpers/storage'; import { setStorage, getStorage } from 'helpers/storage';
@ -21,14 +21,17 @@ export const useDragableWidth = () => {
runTimeWidthRef.current = size; runTimeWidthRef.current = size;
}; };
const toggleCollapsed = (collapsed = null) => { const toggleCollapsed = useCallback(
const isBool = typeof collapsed === 'boolean'; (collapsed = null) => {
const nextCollapsed = isBool ? collapsed : !isCollapsed; const isBool = typeof collapsed === 'boolean';
let nextWidth = nextCollapsed ? COLLAPSED_WIDTH : MIN_WIDTH; const nextCollapsed = isBool ? collapsed : !isCollapsed;
setStorage(key, nextWidth); const nextWidth = nextCollapsed ? COLLAPSED_WIDTH : MIN_WIDTH;
mutate(); setStorage(key, nextWidth);
runTimeWidthRef.current = nextWidth; mutate();
}; runTimeWidthRef.current = nextWidth;
},
[isCollapsed, mutate]
);
useEffect(() => { useEffect(() => {
mutate(); mutate();
@ -36,14 +39,14 @@ export const useDragableWidth = () => {
return () => { return () => {
runTimeWidthRef.current = null; runTimeWidthRef.current = null;
}; };
}, []); }, [mutate]);
useEffect(() => { useEffect(() => {
if (!windowSize.width) return; if (!windowSize.width) return;
if (windowSize.width <= 765) { if (windowSize.width <= 765) {
toggleCollapsed(true); toggleCollapsed(true);
} }
}, [windowSize.width]); }, [windowSize.width, toggleCollapsed]);
return { return {
width: data, width: data,

View File

@ -34,7 +34,7 @@ export const Recent = () => {
{recentDocs.length ? ( {recentDocs.length ? (
recentDocs.map((doc) => { recentDocs.map((doc) => {
return ( return (
<div className={styles.itemWrap}> <div className={styles.itemWrap} key={doc.id}>
<Link <Link
href={{ href={{
pathname: '/wiki/[wikiId]/document/[documentId]', pathname: '/wiki/[wikiId]/document/[documentId]',

View File

@ -88,7 +88,7 @@ export const Wiki = () => {
{starWikis.length ? ( {starWikis.length ? (
starWikis.map((wiki) => { starWikis.map((wiki) => {
return ( return (
<div className={styles.itemWrap}> <div className={styles.itemWrap} key={wiki.id}>
<Link <Link
href={{ href={{
pathname: '/wiki/[wikiId]', pathname: '/wiki/[wikiId]',

View File

@ -34,7 +34,7 @@ export const Recent = () => {
{recentDocs.length ? ( {recentDocs.length ? (
recentDocs.map((doc) => { recentDocs.map((doc) => {
return ( return (
<div className={styles.itemWrap}> <div className={styles.itemWrap} key={doc.id}>
<Link <Link
href={{ href={{
pathname: '/wiki/[wikiId]/document/[documentId]', pathname: '/wiki/[wikiId]/document/[documentId]',

View File

@ -88,7 +88,7 @@ export const Wiki = () => {
{starWikis.length ? ( {starWikis.length ? (
starWikis.map((wiki) => { starWikis.map((wiki) => {
return ( return (
<div className={styles.itemWrap}> <div className={styles.itemWrap} key={wiki.id}>
<Link <Link
href={{ href={{
pathname: '/wiki/[wikiId]', pathname: '/wiki/[wikiId]',

View File

@ -1,5 +1,6 @@
import type { AppProps } from 'next/app'; import type { AppProps } from 'next/app';
import Head from 'next/head'; import Head from 'next/head';
import React from 'react';
import { useTheme } from 'hooks/use-theme'; import { useTheme } from 'hooks/use-theme';
import 'viewerjs/dist/viewer.css'; import 'viewerjs/dist/viewer.css';
import 'styles/globals.scss'; import 'styles/globals.scss';

View File

@ -67,7 +67,7 @@ const RecentDocs = () => {
)} )}
/>, />,
], ],
[] [refresh]
); );
return ( return (

View File

@ -1,4 +1,5 @@
import Link from 'next/link'; import Link from 'next/link';
import React from 'react';
import { Form, Button, Layout, Space, Typography } from '@douyinfe/semi-ui'; import { Form, Button, Layout, Space, Typography } from '@douyinfe/semi-ui';
import { useUser } from 'data/user'; import { useUser } from 'data/user';
import { Seo } from 'components/seo'; import { Seo } from 'components/seo';

View File

@ -1,3 +1,4 @@
import React from 'react';
import Router from 'next/router'; import Router from 'next/router';
import Link from 'next/link'; import Link from 'next/link';
import { Form, Button, Layout, Space, Typography, Modal } from '@douyinfe/semi-ui'; import { Form, Button, Layout, Space, Typography, Modal } from '@douyinfe/semi-ui';

View File

@ -1,3 +1,4 @@
import React from 'react';
import { NextPage } from 'next'; import { NextPage } from 'next';
import { DocumentPublicReader } from 'components/document/reader/public'; import { DocumentPublicReader } from 'components/document/reader/public';

View File

@ -1,3 +1,4 @@
import React from 'react';
import { NextPage } from 'next'; import { NextPage } from 'next';
import { PublicDoubleColumnLayout } from 'layouts/public-double-column'; import { PublicDoubleColumnLayout } from 'layouts/public-double-column';
import { WikiPublicTocs } from 'components/wiki/tocs/public'; import { WikiPublicTocs } from 'components/wiki/tocs/public';

View File

@ -1,3 +1,4 @@
import React from 'react';
import { NextPage } from 'next'; import { NextPage } from 'next';
import { PublicDoubleColumnLayout } from 'layouts/public-double-column'; import { PublicDoubleColumnLayout } from 'layouts/public-double-column';
import { usePublicWikiHomeDoc } from 'data/wiki'; import { usePublicWikiHomeDoc } from 'data/wiki';

View File

@ -1,4 +1,5 @@
import { NextPage } from 'next'; import { NextPage } from 'next';
import React from 'react';
import { DocumentEditor } from 'components/document/editor'; import { DocumentEditor } from 'components/document/editor';
interface IProps { interface IProps {

View File

@ -1,4 +1,5 @@
import { NextPage } from 'next'; import { NextPage } from 'next';
import React from 'react';
import { DoubleColumnLayout } from 'layouts/double-column'; import { DoubleColumnLayout } from 'layouts/double-column';
import { WikiTocs } from 'components/wiki/tocs'; import { WikiTocs } from 'components/wiki/tocs';
import { DocumentReader } from 'components/document/reader'; import { DocumentReader } from 'components/document/reader';

View File

@ -1,4 +1,5 @@
import { NextPage } from 'next'; import { NextPage } from 'next';
import React from 'react';
import { DoubleColumnLayout } from 'layouts/double-column'; import { DoubleColumnLayout } from 'layouts/double-column';
import { useWikiHomeDoc } from 'data/wiki'; import { useWikiHomeDoc } from 'data/wiki';
import { DataRender } from 'components/data-render'; import { DataRender } from 'components/data-render';
@ -14,7 +15,7 @@ const Page: NextPage<IProps> = ({ wikiId }) => {
return ( return (
<DoubleColumnLayout <DoubleColumnLayout
leftNode={<WikiTocs pageTitle="概览" wikiId={wikiId} />} leftNode={<WikiTocs wikiId={wikiId} />}
rightNode={ rightNode={
<DataRender <DataRender
loading={loading} loading={loading}

View File

@ -1,4 +1,4 @@
import { useCallback } from 'react'; import React, { useCallback } from 'react';
import { NextPage } from 'next'; import { NextPage } from 'next';
import Router, { useRouter } from 'next/router'; import Router, { useRouter } from 'next/router';
import { DoubleColumnLayout } from 'layouts/double-column'; import { DoubleColumnLayout } from 'layouts/double-column';
@ -15,14 +15,17 @@ const Page: NextPage<IProps> = ({ wikiId }) => {
tab?: string; tab?: string;
}; };
const navigate = useCallback((tab = 'base') => { const navigate = useCallback(
return () => { (tab = 'base') => {
Router.push({ return () => {
pathname: `/wiki/${wikiId}/setting`, Router.push({
query: { tab }, pathname: `/wiki/${wikiId}/setting`,
}); query: { tab },
}; });
}, []); };
},
[wikiId]
);
return ( return (
<DoubleColumnLayout <DoubleColumnLayout

File diff suppressed because it is too large Load Diff