diff --git a/packages/client/src/services/user.ts b/packages/client/src/services/user.ts index 95bb8522..42a5bb41 100644 --- a/packages/client/src/services/user.ts +++ b/packages/client/src/services/user.ts @@ -1,4 +1,5 @@ -import type { IPagination, IUser } from '@think/domains'; +import { IAuth, IUser, OrganizationApiDefinition } from '@think/domains'; +import Router from 'next/router'; import { HttpClient } from './http-client'; @@ -9,3 +10,14 @@ export const register = (data: Partial): Promise => { export const getUsers = (): Promise => { return HttpClient.get('/user'); }; + +export const getMentionUser = (): Promise<{ data: Array<{ auth: IAuth; user: IUser }>; total: number }> => { + const { organizationId } = Router.query; + return HttpClient.request({ + method: OrganizationApiDefinition.getMembers.method, + url: OrganizationApiDefinition.getMembers.client(organizationId as string), + params: { + pageSize: 10000, + }, + }); +}; diff --git a/packages/client/src/tiptap/core/extensions/mention.ts b/packages/client/src/tiptap/core/extensions/mention.ts index f545b0db..f8e15c91 100644 --- a/packages/client/src/tiptap/core/extensions/mention.ts +++ b/packages/client/src/tiptap/core/extensions/mention.ts @@ -1,14 +1,14 @@ import BulitInMention from '@tiptap/extension-mention'; import { ReactRenderer } from '@tiptap/react'; -import { getUsers } from 'services/user'; +import { getMentionUser } from 'services/user'; import tippy from 'tippy.js'; import { MentionList } from 'tiptap/core/wrappers/mention-list'; import { getDatasetAttribute } from 'tiptap/prose-utils'; const suggestion = { items: async ({ query }) => { - const res = await getUsers(); - const data = res.map((item) => item.name); + const res = await getMentionUser(); + const data = (res.data || []).map((item) => item.user.name); return data.filter((item) => item.toLowerCase().startsWith(query.toLowerCase())); }, diff --git a/packages/client/src/tiptap/editor/collaboration/collaboration/index.tsx b/packages/client/src/tiptap/editor/collaboration/collaboration/index.tsx index 980bf3f2..9194c3ca 100644 --- a/packages/client/src/tiptap/editor/collaboration/collaboration/index.tsx +++ b/packages/client/src/tiptap/editor/collaboration/collaboration/index.tsx @@ -46,6 +46,7 @@ export const CollaborationEditor = forwardRef((props: ICollaborationEditorProps, targetId: documentId, userId: user && user.id, docType: type, + editable, }, maxAttempts: 1, onAwarenessUpdate: throttle(({ states }) => { @@ -67,7 +68,7 @@ export const CollaborationEditor = forwardRef((props: ICollaborationEditorProps, setStatus(status); }, } as any); - }, [documentId, user, type, onAwarenessUpdate, toggleLoading]); + }, [documentId, user, type, editable, onAwarenessUpdate, toggleLoading]); useImperativeHandle( ref, diff --git a/packages/server/src/services/collaboration.service.ts b/packages/server/src/services/collaboration.service.ts index f04887cd..fe088651 100644 --- a/packages/server/src/services/collaboration.service.ts +++ b/packages/server/src/services/collaboration.service.ts @@ -10,6 +10,25 @@ import { DocumentStatus, IUser } from '@think/domains'; import * as lodash from 'lodash'; import * as Y from 'yjs'; +export const findMentions = (content) => { + const queue = [content]; + const res = []; + + while (queue.length) { + const node = queue.shift(); + + if (node.type === 'mention') { + res.push(node.attrs.id); + } + + if (node.content && node.content.length) { + queue.push(...node.content); + } + } + + return res; +}; + @Injectable() export class CollaborationService { server: typeof Server; @@ -244,6 +263,7 @@ export class CollaborationService { const targetId = requestParameters.get('targetId'); const docType = requestParameters.get('docType'); const userId = requestParameters.get('userId'); + const editable = requestParameters.get('editable'); if (docType === 'document') { const data = await this.documentService.findById(targetId); @@ -252,6 +272,14 @@ export class CollaborationService { title: '未命名文档', }); } + + if (editable) { + const content = data.content; + const json = JSON.parse(content).default; + const mentionUsers = findMentions(json); + this.documentService.notifyMentionUsers(targetId, mentionUsers); + } + return; } diff --git a/packages/server/src/services/document.service.ts b/packages/server/src/services/document.service.ts index 9edf7e18..797e802d 100644 --- a/packages/server/src/services/document.service.ts +++ b/packages/server/src/services/document.service.ts @@ -727,4 +727,33 @@ export class DocumentService { return data; } + + /** + * 通知文档中 @ 的用户 + * @param documentId + * @param mentionUsers + * @returns + */ + public async notifyMentionUsers(documentId, mentionUsers) { + const doc = await this.documentRepo.findOne(documentId); + if (!doc) return; + + await Promise.all( + mentionUsers + .map(async (userName) => { + const user = await this.userService.findOne({ name: userName }); + if (!user) return null; + return await this.messageService.notify(user.id, { + title: `文档「${doc.title}」提及了您`, + message: `文档「${doc.title}」提及了您,快去看看吧!`, + url: buildMessageURL('toDocument')({ + organizationId: doc.organizationId, + wikiId: doc.wikiId, + documentId: doc.id, + }), + }); + }) + .filter(Boolean) + ); + } } diff --git a/packages/server/src/services/message.service.ts b/packages/server/src/services/message.service.ts index 7fb49f03..fbd1a11e 100644 --- a/packages/server/src/services/message.service.ts +++ b/packages/server/src/services/message.service.ts @@ -19,6 +19,14 @@ export class MessageService { */ async notify(userId: IUser['id'], msg) { const data = { userId, ...msg }; + const mayBeNotified = await this.messageRepo.findOne(data); + + if (mayBeNotified) { + if (!mayBeNotified.read) { + return; + } + } + const res = await this.messageRepo.create(data); const ret = await this.messageRepo.save(res); return ret;