From a316f97bb90bd99072bc26d8fcba3c91dfe58ce1 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Thu, 26 May 2022 12:29:50 +0800 Subject: [PATCH] chore: add user in document version --- .../document/version/index.module.scss | 75 +++++++++++++----- .../src/components/document/version/index.tsx | 78 +++++++++---------- packages/client/src/data/document.tsx | 18 +++-- .../src/services/collaboration.service.ts | 7 +- .../src/services/document-version.service.ts | 55 ++++++++++--- .../server/src/services/document.service.ts | 2 +- 6 files changed, 159 insertions(+), 76 deletions(-) diff --git a/packages/client/src/components/document/version/index.module.scss b/packages/client/src/components/document/version/index.module.scss index 0c400ec6..ae5f95ed 100644 --- a/packages/client/src/components/document/version/index.module.scss +++ b/packages/client/src/components/document/version/index.module.scss @@ -16,34 +16,73 @@ } .contentWrap { + position: relative; display: flex; height: calc(100vh - 56px); margin: 0 -24px; flex-wrap: nowrap; - :global { - .semi-navigation-inner { - flex-direction: column; + main { + padding: 24px; + overflow: auto; + background-color: var(--semi-color-bg-0); + flex: 1; - .semi-navigation-header-list-outer { - flex: 1; - height: calc(100% - 64px); - } + &.isMobile { + padding: 0; + overflow: hidden; - .semi-navigation-footer { - display: block; - - .semi-navigation-collapse-btn { - display: flex; - align-items: center; - justify-content: flex-start; - } + > div { + height: 100%; + padding: 24px; + margin-top: 71px; + overflow: auto; } } } - .selected { - color: var(--semi-color-primary); - background-color: var(--semi-color-primary-light-default); + aside { + width: 240px; + border-left: 1px solid var(--semi-color-border); + + &.isMobile { + position: absolute; + top: 0; + left: 0; + display: flex; + width: 100%; + height: 71px; + padding: 8px 24px; + overflow: auto; + border-left: 0; + border-bottom: 1px solid var(--semi-color-border); + flex-wrap: nowrap; + } + + .item { + display: flex; + height: 54px; + padding: 8px 12px; + margin-top: 8px; + white-space: nowrap; + cursor: pointer; + border-radius: var(--semi-border-radius-small); + align-items: center; + flex-direction: column; + + &.isMobile { + margin: 0; + } + + &:hover { + color: var(--semi-color-text-1); + background-color: var(--semi-color-fill-1); + } + + &.selected { + color: var(--semi-color-primary); + background-color: var(--semi-color-primary-light-default); + } + } } } diff --git a/packages/client/src/components/document/version/index.tsx b/packages/client/src/components/document/version/index.tsx index 2a6549d6..15c3782c 100644 --- a/packages/client/src/components/document/version/index.tsx +++ b/packages/client/src/components/document/version/index.tsx @@ -1,11 +1,12 @@ import { IconChevronLeft } from '@douyinfe/semi-icons'; -import { Button, Layout, Modal, Nav, Typography } from '@douyinfe/semi-ui'; +import { Button, Modal, Typography } from '@douyinfe/semi-ui'; import { EditorContent, useEditor } from '@tiptap/react'; import cls from 'classnames'; import { DataRender } from 'components/data-render'; import { LocaleTime } from 'components/locale-time'; import { useDocumentVersion } from 'data/document'; import { safeJSONParse } from 'helpers/json'; +import { IsOnMobile } from 'hooks/use-on-mobile'; import { useToggle } from 'hooks/use-toggle'; import React, { useCallback, useEffect, useState } from 'react'; import { CollaborationKit } from 'tiptap/editor'; @@ -18,12 +19,12 @@ interface IProps { onSelect?: (data) => void; } -const { Sider, Content } = Layout; -const { Title } = Typography; +const { Title, Text } = Typography; export const DocumentVersion: React.FC = ({ documentId, disabled = false, onSelect }) => { + const { isMobile } = IsOnMobile.useHook(); const [visible, toggleVisible] = useToggle(false); - const { data, loading, error, refresh } = useDocumentVersion(documentId); + const { data, loading, error, refresh } = useDocumentVersion(documentId, { enabled: visible }); const [selectedVersion, setSelectedVersion] = useState(null); const editor = useEditor({ @@ -83,6 +84,11 @@ export const DocumentVersion: React.FC = ({ documentId, disabled = false 版本记录 +
+ +
- {onSelect && ( - - )}
} @@ -109,40 +110,35 @@ export const DocumentVersion: React.FC = ({ documentId, disabled = false error={error} empty={!loading && !data.length} normalContent={() => ( - - - - - +
+
- - +
+ +
)} /> diff --git a/packages/client/src/data/document.tsx b/packages/client/src/data/document.tsx index 762b7c1c..a7384efa 100644 --- a/packages/client/src/data/document.tsx +++ b/packages/client/src/data/document.tsx @@ -2,7 +2,7 @@ import { DocumentApiDefinition, IAuthority, IDocument, IUser, IWiki } from '@thi import { triggerRefreshTocs } from 'event'; import { useAsyncLoading } from 'hooks/use-async-loading'; import { useCallback, useState } from 'react'; -import { useQuery } from 'react-query'; +import { QueriesOptions, useQuery, UseQueryOptions } from 'react-query'; import { HttpClient } from 'services/http-client'; type IDocumentWithVisitedAt = IDocument & { visitedAt: string }; @@ -186,7 +186,10 @@ export const useDocumentDetail = (documentId) => { * @param documentId * @returns */ -export const getDocumentVersion = (documentId, cookie = null): Promise> => { +export const getDocumentVersion = ( + documentId, + cookie = null +): Promise> => { return HttpClient.request({ method: DocumentApiDefinition.getVersionById.method, url: DocumentApiDefinition.getVersionById.client(documentId), @@ -199,9 +202,14 @@ export const getDocumentVersion = (documentId, cookie = null): Promise { - const { data, error, isLoading, refetch } = useQuery(DocumentApiDefinition.getVersionById.client(documentId), () => - getDocumentVersion(documentId) +export const useDocumentVersion = ( + documentId, + options: UseQueryOptions> +) => { + const { data, error, isLoading, refetch } = useQuery( + DocumentApiDefinition.getVersionById.client(documentId), + () => getDocumentVersion(documentId), + options ); return { data: data || [], loading: isLoading, error, refresh: refetch }; }; diff --git a/packages/server/src/services/collaboration.service.ts b/packages/server/src/services/collaboration.service.ts index 3484df0a..1b0460e4 100644 --- a/packages/server/src/services/collaboration.service.ts +++ b/packages/server/src/services/collaboration.service.ts @@ -176,13 +176,18 @@ export class CollaborationService { const targetId = requestParameters.get('targetId'); const docType = requestParameters.get('docType'); + const userId = requestParameters.get('userId'); const updateDocument = async (user: OutUser, documentId: string, data) => { await this.documentService.updateDocument(user, documentId, data); this.debounce( `onStoreDocumentVersion-${documentId}`, () => { - this.documentVersionService.storeDocumentVersion(documentId, data.content); + this.documentVersionService.storeDocumentVersion({ + documentId, + data: data.content, + userId, + }); }, this.debounceTime * 2 ); diff --git a/packages/server/src/services/document-version.service.ts b/packages/server/src/services/document-version.service.ts index 134b8111..1e029b45 100644 --- a/packages/server/src/services/document-version.service.ts +++ b/packages/server/src/services/document-version.service.ts @@ -1,10 +1,12 @@ -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common'; import { getConfig } from '@think/config'; -import { IDocument } from '@think/domains'; +import { IDocument, IUser } from '@think/domains'; import Redis from 'ioredis'; import * as lodash from 'lodash'; -type VerisonDataItem = { version: string; data: string }; +import { UserService } from './user.service'; + +type VerisonDataItem = { version: string; data: string; userId: IUser['id']; createUser: IUser }; @Injectable() export class DocumentVersionService { @@ -12,14 +14,36 @@ export class DocumentVersionService { private max = 0; private error: string | null = '[think] 文档版本服务启动中'; - constructor() { + constructor( + @Inject(forwardRef(() => UserService)) + private readonly userService: UserService + ) { this.init(); } - private versionDataToArray(data: Record): Array { + private async withUser(data: Array>): Promise { + return await Promise.all( + data.filter(Boolean).map(async (record) => { + const { userId } = record; + const createUser = await this.userService.findById(userId); + return { ...record, createUser }; + }) + ); + } + + private versionDataToArray(data: Record): Array> { return Object.keys(data) .sort((a, b) => +b - +a) - .map((key) => ({ version: key, data: data[key] })); + .map((key) => { + const str = data[key]; + try { + const json = JSON.parse(str); + return { version: key, ...json }; + } catch (e) { + return null; + } + }) + .filter(Boolean); } private async init() { @@ -66,7 +90,7 @@ export class DocumentVersionService { if (this.max <= 0) return; const res = await this.redis.hgetall(documentId); if (!res) return; - const data = this.versionDataToArray(res); + const data = await this.versionDataToArray(res); while (data.length > this.max) { const lastVersion = data.pop().version; @@ -80,10 +104,20 @@ export class DocumentVersionService { * @param data * @returns */ - public async storeDocumentVersion(documentId: IDocument['id'], data: IDocument['content']) { + public async storeDocumentVersion(arg: { + documentId: IDocument['id']; + data: IDocument['content']; + userId: IUser['id']; + }) { if (!this.redis) return; + + const { documentId, data, userId } = arg; + const storeData = JSON.stringify({ + data, + userId, + }); const version = '' + Date.now(); - await this.redis.hsetnx(documentId, version, data); + await this.redis.hsetnx(documentId, version, storeData); await this.checkCacheLength(documentId); } @@ -97,6 +131,7 @@ export class DocumentVersionService { throw new HttpException(this.error, HttpStatus.NOT_IMPLEMENTED); } const res = await this.redis.hgetall(documentId); - return res ? this.versionDataToArray(res) : []; + if (!res) return []; + return await this.withUser(this.versionDataToArray(res)); } } diff --git a/packages/server/src/services/document.service.ts b/packages/server/src/services/document.service.ts index bb7aa952..99e697c8 100644 --- a/packages/server/src/services/document.service.ts +++ b/packages/server/src/services/document.service.ts @@ -50,7 +50,7 @@ export class DocumentService { @Inject(forwardRef(() => ViewService)) private readonly viewService: ViewService ) { - this.documentVersionService = new DocumentVersionService(); + this.documentVersionService = new DocumentVersionService(this.userService); this.collaborationService = new CollaborationService( this.userService, this,