client: imporve reader

pull/60/head
fantasticit 2022-05-26 00:15:32 +08:00
parent 3337ea5593
commit cfadcf22a0
7 changed files with 61 additions and 38 deletions

View File

@ -15,17 +15,19 @@ import {
Tooltip, Tooltip,
Typography, Typography,
} from '@douyinfe/semi-ui'; } from '@douyinfe/semi-ui';
import { IUser } from '@think/domains';
import { DataRender } from 'components/data-render'; import { DataRender } from 'components/data-render';
import { DocumentLinkCopyer } from 'components/document/link'; import { DocumentLinkCopyer } from 'components/document/link';
import { useDoumentMembers } from 'data/document'; import { useDoumentMembers } from 'data/document';
import { useUser } from 'data/user'; import { useUser } from 'data/user';
import { event, JOIN_USER } from 'event'; import { event, JOIN_USER } from 'event';
import { useToggle } from 'hooks/use-toggle'; import { useToggle } from 'hooks/use-toggle';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
interface IProps { interface IProps {
wikiId: string; wikiId: string;
documentId: string; documentId: string;
disabled?: boolean;
} }
const { Paragraph } = Typography; const { Paragraph } = Typography;
@ -45,7 +47,8 @@ const renderChecked = (onChange, authKey: 'readable' | 'editable') => (checked,
return <Checkbox style={{ display: 'inline-block' }} checked={checked} onChange={handle} />; return <Checkbox style={{ display: 'inline-block' }} checked={checked} onChange={handle} />;
}; };
export const DocumentCollaboration: React.FC<IProps> = ({ wikiId, documentId }) => { export const DocumentCollaboration: React.FC<IProps> = ({ wikiId, documentId, disabled = false }) => {
const toastedUsersRef = useRef<Array<IUser['id']>>([]);
const { user: currentUser } = useUser(); const { user: currentUser } = useUser();
const [visible, toggleVisible] = useToggle(false); const [visible, toggleVisible] = useToggle(false);
const { users, loading, error, addUser, updateUser, deleteUser } = useDoumentMembers(documentId); const { users, loading, error, addUser, updateUser, deleteUser } = useDoumentMembers(documentId);
@ -84,8 +87,9 @@ export const DocumentCollaboration: React.FC<IProps> = ({ wikiId, documentId })
} }
newCollaborationUsers.forEach((newUser) => { newCollaborationUsers.forEach((newUser) => {
if (currentUser && newUser.name !== currentUser.name) { if (currentUser && newUser.name !== currentUser.name && !toastedUsersRef.current.includes(newUser.id)) {
Toast.info(`${newUser.name}加入文档`); Toast.info(`${newUser.name}加入文档`);
toastedUsersRef.current.push(newUser.id);
} }
}); });
@ -95,6 +99,7 @@ export const DocumentCollaboration: React.FC<IProps> = ({ wikiId, documentId })
event.on(JOIN_USER, handler); event.on(JOIN_USER, handler);
return () => { return () => {
toastedUsersRef.current = [];
event.off(JOIN_USER, handler); event.off(JOIN_USER, handler);
}; };
}, [collaborationUsers, currentUser]); }, [collaborationUsers, currentUser]);
@ -120,7 +125,13 @@ export const DocumentCollaboration: React.FC<IProps> = ({ wikiId, documentId })
})} })}
</AvatarGroup> </AvatarGroup>
<Tooltip content="邀请他人协作" position="bottom"> <Tooltip content="邀请他人协作" position="bottom">
<Button theme="borderless" type="tertiary" icon={<IconUserAdd />} onClick={toggleVisible}></Button> <Button
theme="borderless"
type="tertiary"
disabled={disabled}
icon={<IconUserAdd />}
onClick={toggleVisible}
></Button>
</Tooltip> </Tooltip>
<Modal <Modal
title={'文档协作'} title={'文档协作'}

View File

@ -1,5 +1,5 @@
import { IconArticle, IconEdit } from '@douyinfe/semi-icons'; import { IconEdit } from '@douyinfe/semi-icons';
import { BackTop, Button, Layout, Nav, Popover, Skeleton, Space, Spin, Tooltip, Typography } from '@douyinfe/semi-ui'; import { BackTop, Button, Layout, Nav, Skeleton, Space, Tooltip, Typography } from '@douyinfe/semi-ui';
import cls from 'classnames'; import cls from 'classnames';
import { DataRender } from 'components/data-render'; import { DataRender } from 'components/data-render';
import { DocumentCollaboration } from 'components/document/collaboration'; import { DocumentCollaboration } from 'components/document/collaboration';
@ -55,11 +55,14 @@ export const DocumentReader: React.FC<IProps> = ({ documentId }) => {
const { user } = useUser(); const { user } = useUser();
const { data: documentAndAuth, loading: docAuthLoading, error: docAuthError } = useDocumentDetail(documentId); const { data: documentAndAuth, loading: docAuthLoading, error: docAuthError } = useDocumentDetail(documentId);
const { document, authority } = documentAndAuth || {}; const { document, authority } = documentAndAuth || {};
const [readable, editable] = useMemo(() => {
if (!authority) return [false, false];
return [authority.readable, authority.editable];
}, [authority]);
const renderAuthor = useCallback( const renderAuthor = useCallback(
(element) => { (element) => {
if (!document) return null; if (!document) return null;
const target = element && element.querySelector('.ProseMirror .title'); const target = element && element.querySelector('.ProseMirror .title');
if (target) { if (target) {
@ -75,29 +78,28 @@ 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]);
const actions = useMemo( const actions = useMemo(() => {
() => ( console.log({ readable, editable });
return (
<Space> <Space>
{document && authority.readable && ( {document && (
<DocumentCollaboration key="collaboration" wikiId={document.wikiId} documentId={documentId} /> <DocumentCollaboration
)} disabled={!readable}
{authority && authority.editable && ( key="collaboration"
<Tooltip key="edit" content="编辑" position="bottom"> wikiId={document.wikiId}
<Button icon={<IconEdit />} onMouseDown={gotoEdit} /> documentId={documentId}
</Tooltip> />
)}
{authority && authority.readable && (
<>
<DocumentShare key="share" documentId={documentId} />
<DocumentVersion key="version" documentId={documentId} />
<DocumentStar key="star" documentId={documentId} />
</>
)} )}
<Tooltip key="edit" content="编辑" position="bottom">
<Button disabled={!editable} icon={<IconEdit />} onMouseDown={gotoEdit} />
</Tooltip>
<DocumentShare disabled={!readable} key="share" documentId={documentId} />
<DocumentVersion disabled={!readable} key="version" documentId={documentId} />
<DocumentStar disabled={!readable} key="star" documentId={documentId} />
<DocumentStyle /> <DocumentStyle />
</Space> </Space>
), );
[document, documentId, authority, gotoEdit] }, [document, documentId, readable, editable, gotoEdit]);
);
const editBtnStyle = useMemo(() => getEditBtnStyle(isMobile ? 16 : 100), [isMobile]); const editBtnStyle = useMemo(() => getEditBtnStyle(isMobile ? 16 : 100), [isMobile]);
@ -133,7 +135,7 @@ export const DocumentReader: React.FC<IProps> = ({ documentId }) => {
<div className={cls(styles.editorWrap, editorWrapClassNames)} style={{ fontSize }}> <div className={cls(styles.editorWrap, editorWrapClassNames)} style={{ fontSize }}>
<div id="js-reader-container"> <div id="js-reader-container">
{document && <Seo title={document.title} />} {document && <Seo title={document.title} />}
{user && ( {user && readable && (
<CollaborationEditor <CollaborationEditor
editable={false} editable={false}
user={user} user={user}

View File

@ -10,12 +10,13 @@ import React, { useEffect, useMemo, useState } from 'react';
interface IProps { interface IProps {
documentId: string; documentId: string;
render?: (arg: { isPublic: boolean; toggleVisible: (arg: boolean) => void }) => React.ReactNode; disabled?: boolean;
render?: (arg: { isPublic: boolean; disabled: boolean; toggleVisible: (arg: boolean) => void }) => React.ReactNode;
} }
const { Text } = Typography; const { Text } = Typography;
export const DocumentShare: React.FC<IProps> = ({ documentId, render }) => { export const DocumentShare: React.FC<IProps> = ({ documentId, disabled = false, render }) => {
const [visible, toggleVisible] = useToggle(false); const [visible, toggleVisible] = useToggle(false);
const { data, loading, error, toggleStatus } = useDocumentDetail(documentId); const { data, loading, error, toggleStatus } = useDocumentDetail(documentId);
const [sharePassword, setSharePassword] = useState(''); const [sharePassword, setSharePassword] = useState('');
@ -34,9 +35,9 @@ export const DocumentShare: React.FC<IProps> = ({ documentId, render }) => {
return ( return (
<> <>
{render ? ( {render ? (
render({ isPublic, toggleVisible }) render({ isPublic, disabled, toggleVisible })
) : ( ) : (
<Button type="primary" theme="light" onClick={toggleVisible}> <Button disabled={disabled} type="primary" theme="light" onClick={toggleVisible}>
{isPublic ? '分享中' : '分享'} {isPublic ? '分享中' : '分享'}
</Button> </Button>
)} )}

View File

@ -5,17 +5,23 @@ import React from 'react';
interface IProps { interface IProps {
documentId: string; documentId: string;
render?: (arg: { star: boolean; text: string; toggleStar: () => Promise<void> }) => React.ReactNode; disabled?: boolean;
render?: (arg: {
star: boolean;
disabled: boolean;
text: string;
toggleStar: () => Promise<void>;
}) => React.ReactNode;
} }
export const DocumentStar: React.FC<IProps> = ({ documentId, render }) => { export const DocumentStar: React.FC<IProps> = ({ documentId, disabled = false, render }) => {
const { data, toggle: toggleStar } = useDocumentCollectToggle(documentId); const { data, toggle: toggleStar } = useDocumentCollectToggle(documentId);
const text = data ? '取消收藏' : '收藏文档'; const text = data ? '取消收藏' : '收藏文档';
return ( return (
<> <>
{render ? ( {render ? (
render({ star: data, toggleStar, text }) render({ star: data, disabled, toggleStar, text })
) : ( ) : (
<Tooltip content={text} position="bottom"> <Tooltip content={text} position="bottom">
<Button <Button
@ -24,6 +30,7 @@ export const DocumentStar: React.FC<IProps> = ({ documentId, render }) => {
style={{ style={{
color: data ? 'rgba(var(--semi-amber-4), 1)' : 'rgba(var(--semi-grey-3), 1)', color: data ? 'rgba(var(--semi-amber-4), 1)' : 'rgba(var(--semi-grey-3), 1)',
}} }}
disabled={disabled}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();

View File

@ -14,13 +14,14 @@ import styles from './index.module.scss';
interface IProps { interface IProps {
documentId: string; documentId: string;
disabled?: boolean;
onSelect?: (data) => void; onSelect?: (data) => void;
} }
const { Sider, Content } = Layout; const { Sider, Content } = Layout;
const { Title } = Typography; const { Title } = Typography;
export const DocumentVersion: React.FC<IProps> = ({ documentId, onSelect }) => { export const DocumentVersion: React.FC<IProps> = ({ documentId, disabled = false, onSelect }) => {
const [visible, toggleVisible] = useToggle(false); const [visible, toggleVisible] = useToggle(false);
const { data, loading, error, refresh } = useDocumentVersion(documentId); const { data, loading, error, refresh } = useDocumentVersion(documentId);
const [selectedVersion, setSelectedVersion] = useState(null); const [selectedVersion, setSelectedVersion] = useState(null);
@ -65,7 +66,7 @@ export const DocumentVersion: React.FC<IProps> = ({ documentId, onSelect }) => {
return ( return (
<> <>
<Button type="primary" theme="light" onClick={toggleVisible}> <Button type="primary" theme="light" disabled={disabled} onClick={toggleVisible}>
</Button> </Button>
<Modal <Modal
@ -87,7 +88,7 @@ export const DocumentVersion: React.FC<IProps> = ({ documentId, onSelect }) => {
theme="light" theme="light"
type="primary" type="primary"
style={{ marginRight: 8 }} style={{ marginRight: 8 }}
disabled={loading || error} disabled={loading || !!error}
loading={loading} loading={loading}
onClick={() => refresh()} onClick={() => refresh()}
> >

View File

@ -139,7 +139,7 @@ export const useDocumentDetail = (documentId) => {
const { data, error, isLoading, refetch } = useQuery( const { data, error, isLoading, refetch } = useQuery(
DocumentApiDefinition.getDetailById.client(documentId), DocumentApiDefinition.getDetailById.client(documentId),
() => getDocumentDetail(documentId), () => getDocumentDetail(documentId),
{ staleTime: 3000 } { staleTime: 3000, refetchOnReconnect: true, refetchOnMount: true, refetchOnWindowFocus: true }
); );
/** /**

View File

@ -2,6 +2,7 @@
display: flex; display: flex;
width: 100%; width: 100%;
height: 100%; height: 100%;
min-height: 100px;
overflow: hidden; overflow: hidden;
flex-direction: column; flex-direction: column;