notify user that is mentioned in document
This commit is contained in:
fantasticit 2022-07-29 22:54:50 +08:00
parent 52e8f637c9
commit 837643b641
6 changed files with 83 additions and 5 deletions

View File

@ -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'; import { HttpClient } from './http-client';
@ -9,3 +10,14 @@ export const register = (data: Partial<IUser>): Promise<IUser> => {
export const getUsers = (): Promise<IUser[]> => { export const getUsers = (): Promise<IUser[]> => {
return HttpClient.get('/user'); 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,
},
});
};

View File

@ -1,14 +1,14 @@
import BulitInMention from '@tiptap/extension-mention'; import BulitInMention from '@tiptap/extension-mention';
import { ReactRenderer } from '@tiptap/react'; import { ReactRenderer } from '@tiptap/react';
import { getUsers } from 'services/user'; import { getMentionUser } from 'services/user';
import tippy from 'tippy.js'; import tippy from 'tippy.js';
import { MentionList } from 'tiptap/core/wrappers/mention-list'; import { MentionList } from 'tiptap/core/wrappers/mention-list';
import { getDatasetAttribute } from 'tiptap/prose-utils'; import { getDatasetAttribute } from 'tiptap/prose-utils';
const suggestion = { const suggestion = {
items: async ({ query }) => { items: async ({ query }) => {
const res = await getUsers(); const res = await getMentionUser();
const data = res.map((item) => item.name); const data = (res.data || []).map((item) => item.user.name);
return data.filter((item) => item.toLowerCase().startsWith(query.toLowerCase())); return data.filter((item) => item.toLowerCase().startsWith(query.toLowerCase()));
}, },

View File

@ -46,6 +46,7 @@ export const CollaborationEditor = forwardRef((props: ICollaborationEditorProps,
targetId: documentId, targetId: documentId,
userId: user && user.id, userId: user && user.id,
docType: type, docType: type,
editable,
}, },
maxAttempts: 1, maxAttempts: 1,
onAwarenessUpdate: throttle(({ states }) => { onAwarenessUpdate: throttle(({ states }) => {
@ -67,7 +68,7 @@ export const CollaborationEditor = forwardRef((props: ICollaborationEditorProps,
setStatus(status); setStatus(status);
}, },
} as any); } as any);
}, [documentId, user, type, onAwarenessUpdate, toggleLoading]); }, [documentId, user, type, editable, onAwarenessUpdate, toggleLoading]);
useImperativeHandle( useImperativeHandle(
ref, ref,

View File

@ -10,6 +10,25 @@ import { DocumentStatus, IUser } from '@think/domains';
import * as lodash from 'lodash'; import * as lodash from 'lodash';
import * as Y from 'yjs'; 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() @Injectable()
export class CollaborationService { export class CollaborationService {
server: typeof Server; server: typeof Server;
@ -244,6 +263,7 @@ export class CollaborationService {
const targetId = requestParameters.get('targetId'); const targetId = requestParameters.get('targetId');
const docType = requestParameters.get('docType'); const docType = requestParameters.get('docType');
const userId = requestParameters.get('userId'); const userId = requestParameters.get('userId');
const editable = requestParameters.get('editable');
if (docType === 'document') { if (docType === 'document') {
const data = await this.documentService.findById(targetId); const data = await this.documentService.findById(targetId);
@ -252,6 +272,14 @@ export class CollaborationService {
title: '未命名文档', title: '未命名文档',
}); });
} }
if (editable) {
const content = data.content;
const json = JSON.parse(content).default;
const mentionUsers = findMentions(json);
this.documentService.notifyMentionUsers(targetId, mentionUsers);
}
return; return;
} }

View File

@ -727,4 +727,33 @@ export class DocumentService {
return data; 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)
);
}
} }

View File

@ -19,6 +19,14 @@ export class MessageService {
*/ */
async notify(userId: IUser['id'], msg) { async notify(userId: IUser['id'], msg) {
const data = { userId, ...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 res = await this.messageRepo.create(data);
const ret = await this.messageRepo.save(res); const ret = await this.messageRepo.save(res);
return ret; return ret;