chore: fix error, improve perf

pull/194/head
fantasticit 2022-09-15 23:09:57 +08:00
parent cdd02c55a1
commit bb060b2729
18 changed files with 144 additions and 115 deletions

View File

@ -74,11 +74,14 @@ export const DocumentFullscreen: React.FC<IProps> = ({ data }) => {
const [isDrawing, toggleDrawing] = useToggle(false);
const [cover, setCover] = useState('');
const editor = useEditor({
editable: false,
extensions: CollaborationKit.filter((ext) => ['title', 'doc'].indexOf(ext.name) < 0).concat(Document),
content: { type: 'doc', content: [] },
});
const editor = useEditor(
{
editable: false,
extensions: CollaborationKit.filter((ext) => ['title', 'doc'].indexOf(ext.name) < 0).concat(Document),
content: { type: 'doc', content: [] },
},
[]
);
const startPowerpoint = useCallback(() => {
toggleVisible(true);

View File

@ -14,6 +14,8 @@ interface IProps {
const { Text } = Typography;
const style = { cursor: 'pointer' };
export const DocumentLinkCopyer: React.FC<IProps> = ({ organizationId, wikiId, documentId, render }) => {
const handle = useCallback(() => {
copy(buildUrl(`/app/org/${organizationId}/wiki/${wikiId}/doc/${documentId}`));
@ -29,7 +31,7 @@ export const DocumentLinkCopyer: React.FC<IProps> = ({ organizationId, wikiId, d
return render ? (
<>{render({ copy: handle, children: content })}</>
) : (
<Text onClick={handle} style={{ cursor: 'pointer' }}>
<Text onClick={handle} style={style}>
{content}
</Text>
);

View File

@ -8,18 +8,18 @@ interface IProps {
document: IDocument;
}
const style = {
borderTop: '1px solid var(--semi-color-border)',
marginTop: '0.75em',
padding: '16px 0',
fontSize: 13,
fontWeight: 'normal',
color: 'var(--semi-color-text-0)',
};
export const Author: React.FC<IProps> = ({ document }) => {
return (
<div
style={{
borderTop: '1px solid var(--semi-color-border)',
marginTop: '0.75em',
padding: '16px 0',
fontSize: 13,
fontWeight: 'normal',
color: 'var(--semi-color-text-0)',
}}
>
<div style={style}>
<Space>
<Avatar size="small" src={document && document.createUser && document.createUser.avatar}>
<IconUser />

View File

@ -29,6 +29,14 @@ interface IProps {
documentId: string;
}
const loadingStyle = {
minHeight: 240,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
margin: 'auto',
};
export const DocumentReader: React.FC<IProps> = ({ documentId }) => {
const { isMobile } = IsOnMobile.useHook();
const mounted = useMount();
@ -132,15 +140,7 @@ export const DocumentReader: React.FC<IProps> = ({ documentId }) => {
<DataRender
loading={docAuthLoading}
loadingContent={
<div
style={{
minHeight: 240,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
margin: 'auto',
}}
>
<div style={loadingStyle}>
<Spin />
</div>
}

View File

@ -9,7 +9,6 @@ import { Seo } from 'components/seo';
import { Theme } from 'components/theme';
import { User } from 'components/user';
import { usePublicDocumentDetail } from 'data/document';
import { useDocumentStyle } from 'hooks/use-document-style';
import { useMount } from 'hooks/use-mount';
import { IsOnMobile } from 'hooks/use-on-mobile';
import { SecureDocumentIllustration } from 'illustrations/secure-document';
@ -38,12 +37,8 @@ export const DocumentPublicReader: React.FC<IProps> = ({ documentId, hideLogo =
const mounted = useMount();
const { wikiId: currentWikiId } = useRouterQuery<{ wikiId: IWiki['id']; documentId: IDocument['id'] }>();
const { data, loading, error, query } = usePublicDocumentDetail(documentId);
const { width, fontSize } = useDocumentStyle();
const { isMobile } = IsOnMobile.useHook();
const editorWrapClassNames = useMemo(() => {
return width === 'standardWidth' ? styles.isStandardWidth : styles.isFullWidth;
}, [width]);
const renderAuthor = useCallback(
(element) => {
if (!document) return null;

View File

@ -33,6 +33,15 @@ export const DocumentStar: React.FC<IProps> = ({ organizationId, wikiId, documen
[toggleVisible]
);
const toggleStarAction = useCallback(
(e) => {
e.stopPropagation();
e.preventDefault();
toggleStar();
},
[toggleStar]
);
return (
<VisibilitySensor onChange={onViewportChange}>
{render ? (
@ -46,11 +55,7 @@ export const DocumentStar: React.FC<IProps> = ({ organizationId, wikiId, documen
color: data ? 'rgba(var(--semi-amber-4), 1)' : 'rgba(var(--semi-grey-3), 1)',
}}
disabled={disabled}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
toggleStar();
}}
onClick={toggleStarAction}
/>
</Tooltip>
)}

View File

@ -31,16 +31,19 @@ export const DocumentVersion: React.FC<Partial<IProps>> = ({ documentId, onSelec
const [selectedVersion, setSelectedVersion] = useState(null);
const [diffVersion, setDiffVersion] = useState(null);
const editor = useEditor({
editable: false,
editorProps: {
attributes: {
class: 'is-editable',
const editor = useEditor(
{
editable: false,
editorProps: {
attributes: {
class: 'is-editable',
},
},
extensions: CollaborationKit,
content: { type: 'doc', content: [] },
},
extensions: CollaborationKit,
content: { type: 'doc', content: [] },
});
[]
);
const close = useCallback(() => {
toggleVisible(false);

View File

@ -94,7 +94,7 @@ export const Import: React.FC<IProps> = ({ wikiId }) => {
<div style={{ marginTop: 16 }}>
<Upload
action=""
accept="text/markdown"
accept=".md,.MD,.Md,.mD"
draggable
multiple
ref={$upload}

View File

@ -22,10 +22,13 @@ interface IProps {
const { Text } = Typography;
const defaultGetDocLink = (document) =>
`/app/org/${document.organizationId}/wiki/${document.wikiId}/doc/${document.id}`;
export const WikiTocs: React.FC<IProps> = ({
wikiId,
docAsLink = '/app/org/[organizationId]/wiki/[wikiId]/doc/[documentId]',
getDocLink = (document) => `/app/org/${document.organizationId}/wiki/${document.wikiId}/doc/${document.id}`,
getDocLink = defaultGetDocLink,
}) => {
const { pathname, query } = useRouter();
const { data: wiki, loading: wikiLoading, error: wikiError } = useWikiDetail(wikiId);
@ -129,7 +132,9 @@ export const WikiTocs: React.FC<IProps> = ({
</Avatar>
<Text strong>{wiki.name}</Text>
</span>
<IconSmallTriangleDown />
<Text>
<IconSmallTriangleDown />
</Text>
</div>
</Dropdown>
) : (

View File

@ -1,6 +1,5 @@
import { Banner, Button, Toast, Tree, Typography } from '@douyinfe/semi-ui';
import { DataRender } from 'components/data-render';
import { Resizeable } from 'components/resizeable';
import { useWikiTocs } from 'data/wiki';
import React, { useCallback, useEffect, useState } from 'react';
@ -18,7 +17,7 @@ interface IDataNode {
children?: Array<IDataNode>;
}
const { Title, Text } = Typography;
const { Text } = Typography;
const extractRelation = (treeData: Array<IDataNode>) => {
const res = [];
@ -41,6 +40,8 @@ const extractRelation = (treeData: Array<IDataNode>) => {
return res;
};
const marginBottomStyle = { marginBottom: 16 };
export const WikiTocsManager: React.FC<IProps> = ({ wikiId }) => {
const { data: tocs, loading: tocsLoading, error: tocsError, update: updateTocs } = useWikiTocs(wikiId);
@ -105,6 +106,10 @@ export const WikiTocsManager: React.FC<IProps> = ({ wikiId }) => {
[treeData]
);
const renderNorContent = useCallback(() => {
return <Tree treeData={treeData} draggable onDrop={onDrop} expandAll />;
}, [treeData, onDrop]);
const submit = useCallback(() => {
const data = extractRelation(treeData);
updateTocs(data).then(() => {
@ -121,16 +126,10 @@ export const WikiTocsManager: React.FC<IProps> = ({ wikiId }) => {
icon={null}
closeIcon={null}
description={<Text></Text>}
style={{ marginBottom: 16 }}
style={marginBottomStyle}
/>
<div className={styles.tocsWrap}>
<DataRender
loading={tocsLoading}
error={tocsError}
normalContent={() => {
return <Tree treeData={treeData} draggable onDrop={onDrop} expandAll />;
}}
/>
<DataRender loading={tocsLoading} error={tocsError} normalContent={renderNorContent} />
</div>
<div className={styles.btnWrap}>
<Button disabled={!changed} onClick={submit} theme="solid">

View File

@ -18,6 +18,8 @@ interface IProps {
openNewTab?: boolean;
}
const marginTopStyle = { marginTop: 4 };
export const NavItem: React.FC<IProps> = ({
icon,
text,
@ -38,7 +40,7 @@ export const NavItem: React.FC<IProps> = ({
return (
<div
className={cls(styles.navItemWrap, isActive && styles.isActive, hoverable && styles.hoverable)}
style={{ marginTop: 4 }}
style={marginTopStyle}
>
{href ? (
<Link href={href as UrlObject}>

View File

@ -22,11 +22,13 @@ interface IProps {
const { Text } = Typography;
const defaultGetDocLink = (document) => `/share/wiki/${document.wikiId}/document/${document.id}`;
export const WikiPublicTocs: React.FC<IProps> = ({
pageTitle,
wikiId,
docAsLink = '/share/wiki/[wikiId]/document/[documentId]',
getDocLink = (document) => `/share/wiki/${document.wikiId}/document/${document.id}`,
getDocLink = defaultGetDocLink,
}) => {
const { pathname } = useRouter();
const { data: wiki, loading: wikiLoading, error: wikiError } = usePublicWikiDetail(wikiId);

View File

@ -14,6 +14,14 @@ import styles from './index.module.scss';
import { findParents } from './utils';
const Actions = ({ node }) => {
const createDocument = useCallback(
(e) => {
e.stopPropagation();
triggerCreateDocument({ wikiId: node.wikiId, documentId: node.id });
},
[node.wikiId, node.id]
);
return (
<span className={styles.right}>
<DocumentActions
@ -28,10 +36,7 @@ const Actions = ({ node }) => {
></DocumentActions>
<Button
className={styles.hoverVisible}
onClick={(e) => {
e.stopPropagation();
triggerCreateDocument({ wikiId: node.wikiId, documentId: node.id });
}}
onClick={createDocument}
type="tertiary"
theme="borderless"
icon={<IconPlus />}
@ -70,6 +75,8 @@ const AddDocument = () => {
let scrollTimer;
const inheritColorStyle = { color: 'inherit' };
export const _Tree = ({ data, docAsLink, getDocLink, isShareMode = false, needAddDocument = false }) => {
const { query } = useRouter();
const $container = useRef<HTMLDivElement>(null);
@ -92,7 +99,7 @@ export const _Tree = ({ data, docAsLink, getDocLink, isShareMode = false, needAd
ellipsis={{
showTooltip: { opts: { content: label, style: { wordBreak: 'break-all' }, position: 'right' } },
}}
style={{ color: 'inherit' }}
style={inheritColorStyle}
>
{label}
</Typography.Text>
@ -128,7 +135,7 @@ export const _Tree = ({ data, docAsLink, getDocLink, isShareMode = false, needAd
value={query.documentId}
defaultExpandedKeys={expandedKeys}
expandedKeys={expandedKeys}
onExpand={(expandedKeys) => setExpandedKeys(expandedKeys)}
onExpand={setExpandedKeys}
/>
{needAddDocument && <AddDocument />}
</div>

View File

@ -11,7 +11,7 @@ import { HttpClient } from 'services/http-client';
*/
export const useCreateOrganization = () => {
const [apiWithLoading, loading] = useAsyncLoading((data) =>
HttpClient.request({
HttpClient.request<IOrganization>({
method: OrganizationApiDefinition.createOrganization.method,
url: OrganizationApiDefinition.createOrganization.client(),
data,

View File

@ -319,7 +319,7 @@ export const useWikiDocuments = (wikiId) => {
export const getWikiMembers = (
wikiId,
page,
pageSize,
pageSize = 12,
cookie = null
): Promise<{ data: Array<{ auth: IAuth; user: IUser }>; total: number }> => {
return HttpClient.request({

View File

@ -4,7 +4,7 @@ import { DataRender } from 'components/data-render';
import deepEqual from 'deep-equal';
import { useToggle } from 'hooks/use-toggle';
import { SecureDocumentIllustration } from 'illustrations/secure-document';
import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { Editor } from 'tiptap/core';
import { IndexeddbPersistence } from 'tiptap/core/thritypart/y-indexeddb';
@ -18,6 +18,14 @@ export type ICollaborationRefProps = {
getEditor: () => Editor;
};
const errorContainerStyle = {
margin: '10%',
display: 'flex',
justifyContent: 'center',
flexDirection: 'column',
alignItems: 'center',
} as React.CSSProperties;
export const CollaborationEditor = forwardRef((props: ICollaborationEditorProps, ref) => {
const {
id: documentId,
@ -56,9 +64,9 @@ export const CollaborationEditor = forwardRef((props: ICollaborationEditorProps,
onAwarenessUpdate && onAwarenessUpdate(users);
lastAwarenessRef.current = users;
},
onAuthenticationFailed() {
onAuthenticationFailed(e) {
toggleLoading(false);
setError(new Error('鉴权失败!暂时无法提供服务'));
setError(e || new Error('鉴权失败!暂时无法提供服务'));
},
onSynced() {
toggleLoading(false);
@ -69,6 +77,34 @@ export const CollaborationEditor = forwardRef((props: ICollaborationEditorProps,
} as any);
}, [documentId, user, type, editable, onAwarenessUpdate, toggleLoading]);
const renderEditor = useCallback(
() => (
<EditorInstance
ref={$editor}
documentId={documentId}
editable={editable}
menubar={menubar}
hocuspocusProvider={hocuspocusProvider}
onTitleUpdate={onTitleUpdate}
user={user}
status={status}
hideComment={hideComment}
renderInEditorPortal={renderInEditorPortal}
/>
),
[documentId, editable, hideComment, hocuspocusProvider, menubar, onTitleUpdate, renderInEditorPortal, status, user]
);
const renderError = useCallback(
(error) => (
<div style={errorContainerStyle}>
<SecureDocumentIllustration />
<Text type="danger">{(error && error.message) || '未知错误'}</Text>
</div>
),
[]
);
useImperativeHandle(
ref,
() =>
@ -101,49 +137,19 @@ export const CollaborationEditor = forwardRef((props: ICollaborationEditorProps,
}, [hocuspocusProvider]);
return (
<>
<div className={styles.wrap}>
<DataRender
loading={loading}
loadingContent={
<div style={{ margin: 'auto' }}>
<Spin />
</div>
}
error={error}
errorContent={(error) => (
<div
style={{
margin: '10%',
display: 'flex',
justifyContent: 'center',
flexDirection: 'column',
alignItems: 'center',
}}
>
<SecureDocumentIllustration />
<Text style={{ marginTop: 12 }} type="danger">
{(error && error.message) || '未知错误'}
</Text>
</div>
)}
normalContent={() => (
<EditorInstance
ref={$editor}
documentId={documentId}
editable={editable}
menubar={menubar}
hocuspocusProvider={hocuspocusProvider}
onTitleUpdate={onTitleUpdate}
user={user}
status={status}
hideComment={hideComment}
renderInEditorPortal={renderInEditorPortal}
/>
)}
/>
</div>
</>
<div className={styles.wrap}>
<DataRender
loading={loading}
loadingContent={
<div style={{ margin: 'auto' }}>
<Spin />
</div>
}
error={error}
errorContent={renderError}
normalContent={renderEditor}
/>
</div>
);
});

View File

@ -3,7 +3,7 @@ import { IsNotEmpty, IsString, MinLength } from 'class-validator';
export class LoginUserDto {
@IsString({ message: '用户名称类型错误正确类型为String' })
@IsNotEmpty({ message: '用户账号不能为空' })
@MinLength(5, { message: '用户账号至少5个字符' })
@MinLength(1, { message: '用户账号至少1个字符' })
readonly name: string;
@IsString({ message: '用户密码类型错误正确类型为String' })

View File

@ -253,7 +253,7 @@ export class UserService {
const currentSystemConfig = await this.systemService.getConfigFromDatabase();
const oldData = await this.userRepo.findOne(user.id);
if (oldData.email !== dto.email) {
if (oldData && dto && oldData.email !== dto.email) {
if (await this.userRepo.findOne({ where: { email: dto.email } })) {
throw new HttpException('该邮箱已被注册', HttpStatus.BAD_REQUEST);
}