mirror of https://github.com/fantasticit/think.git
Merge pull request #139 from fantasticit/fix/13x
fix paste html, notify user who is mentioned
This commit is contained in:
commit
ac81cc8e31
|
@ -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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -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()));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ interface IPasteOptions {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将 markdown 转换为 prosemirror 节点
|
* 将 markdown 转换为 prosemirror 节点
|
||||||
* FIXME: prosemirror 节点的类型是什么?
|
|
||||||
*/
|
*/
|
||||||
markdownToProsemirror: (arg: { schema: Schema; content: string; needTitle: boolean }) => Node;
|
markdownToProsemirror: (arg: { schema: Schema; content: string; needTitle: boolean }) => Node;
|
||||||
|
|
||||||
|
@ -111,8 +110,8 @@ export const Paste = Extension.create<IPasteOptions>({
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增:office 套件内容处理
|
// FIXME:各家 office 套件标准不一样,是否需要做成用户自行选择粘贴 html 或者 图片?
|
||||||
if (html?.includes('urn:schemas-microsoft-com:office')) {
|
if (html?.length) {
|
||||||
const doc = htmlToProsemirror({
|
const doc = htmlToProsemirror({
|
||||||
schema: editor.schema,
|
schema: editor.schema,
|
||||||
html,
|
html,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue