@builder6/rooms 3.0.5 → 3.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -7
- package/.prettierrc +0 -4
- package/src/emails/UnreadMention.tsx +0 -37
- package/src/emails/UnreadReplies.tsx +0 -49
- package/src/emails/_components/comment.tsx +0 -70
- package/src/emails/_components/header.tsx +0 -26
- package/src/emails/_components/headline.tsx +0 -20
- package/src/emails/_components/layout.tsx +0 -45
- package/src/emails/_lib/types.ts +0 -10
- package/src/emails/_styles/colors.ts +0 -7
- package/src/emails/_utils/cn.ts +0 -6
- package/src/emails/_utils/comments.ts +0 -61
- package/src/emails/_utils/getProps.ts +0 -7
- package/src/plugin.module.ts +0 -3
- package/src/rooms/app.controller.ts +0 -89
- package/src/rooms/dtos/inbox_notifications.dto.ts +0 -13
- package/src/rooms/dtos/notifications.dto.ts +0 -14
- package/src/rooms/dtos/room_members.dto.ts +0 -32
- package/src/rooms/dtos/rooms.dto.ts +0 -51
- package/src/rooms/emailNotification.service.tsx +0 -126
- package/src/rooms/emails/comment-body.tsx +0 -342
- package/src/rooms/emails/comment-with-body.ts +0 -24
- package/src/rooms/emails/index.ts +0 -25
- package/src/rooms/emails/lib/batch-users-resolver.ts +0 -120
- package/src/rooms/emails/lib/css-properties.ts +0 -123
- package/src/rooms/emails/lib/warning.ts +0 -25
- package/src/rooms/emails/thread-notification.tsx +0 -583
- package/src/rooms/globals/augmentation.ts +0 -89
- package/src/rooms/index.ts +0 -5
- package/src/rooms/lib/DateToString.ts +0 -9
- package/src/rooms/lib/Json.ts +0 -34
- package/src/rooms/lib/utils.ts +0 -240
- package/src/rooms/liveblocks.service.ts +0 -25
- package/src/rooms/notifications.service.ts +0 -235
- package/src/rooms/protocol/AuthToken.ts +0 -126
- package/src/rooms/protocol/Authentication.ts +0 -18
- package/src/rooms/protocol/BaseActivitiesData.ts +0 -5
- package/src/rooms/protocol/BaseRoomInfo.ts +0 -15
- package/src/rooms/protocol/BaseUserMeta.ts +0 -28
- package/src/rooms/protocol/ClientMsg.ts +0 -94
- package/src/rooms/protocol/Comments.ts +0 -202
- package/src/rooms/protocol/InboxNotifications.ts +0 -75
- package/src/rooms/protocol/Op.ts +0 -143
- package/src/rooms/protocol/SerializedCrdt.ts +0 -61
- package/src/rooms/protocol/ServerMsg.ts +0 -307
- package/src/rooms/protocol/VersionHistory.ts +0 -9
- package/src/rooms/rooms.controller.ts +0 -587
- package/src/rooms/rooms.gateway.ts +0 -267
- package/src/rooms/rooms.guard.ts +0 -52
- package/src/rooms/rooms.module.ts +0 -38
- package/src/rooms/rooms.moleculer.ts +0 -80
- package/src/rooms/rooms.service.ts +0 -723
- package/tsconfig.json +0 -10
- package/yarn-error.log +0 -17218
|
@@ -1,583 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
BaseRoomInfo,
|
|
3
|
-
BaseUserMeta,
|
|
4
|
-
CommentBody,
|
|
5
|
-
CommentData,
|
|
6
|
-
DRI,
|
|
7
|
-
DU,
|
|
8
|
-
InboxNotificationData,
|
|
9
|
-
OptionalPromise,
|
|
10
|
-
ResolveUsersArgs,
|
|
11
|
-
} from '@liveblocks/core';
|
|
12
|
-
import {
|
|
13
|
-
generateCommentUrl,
|
|
14
|
-
getMentionedIdsFromCommentBody,
|
|
15
|
-
} from '@liveblocks/core';
|
|
16
|
-
import type { Liveblocks, ThreadNotificationEvent } from '@liveblocks/node';
|
|
17
|
-
import type React from 'react';
|
|
18
|
-
|
|
19
|
-
import type {
|
|
20
|
-
ConvertCommentBodyAsHtmlStyles,
|
|
21
|
-
ConvertCommentBodyAsReactComponents,
|
|
22
|
-
} from './comment-body';
|
|
23
|
-
import {
|
|
24
|
-
convertCommentBodyAsHtml,
|
|
25
|
-
convertCommentBodyAsReact,
|
|
26
|
-
} from './comment-body';
|
|
27
|
-
import type { CommentDataWithBody } from './comment-with-body';
|
|
28
|
-
import { filterCommentsWithBody } from './comment-with-body';
|
|
29
|
-
import { createBatchUsersResolver } from './lib/batch-users-resolver';
|
|
30
|
-
|
|
31
|
-
/** @internal */
|
|
32
|
-
export const getUnreadComments = ({
|
|
33
|
-
comments,
|
|
34
|
-
inboxNotification,
|
|
35
|
-
userId,
|
|
36
|
-
}: {
|
|
37
|
-
comments: CommentData[];
|
|
38
|
-
inboxNotification: InboxNotificationData;
|
|
39
|
-
userId: string;
|
|
40
|
-
}): CommentDataWithBody[] => {
|
|
41
|
-
const commentsWithBody = filterCommentsWithBody(comments);
|
|
42
|
-
const readAt = inboxNotification.readAt;
|
|
43
|
-
|
|
44
|
-
return commentsWithBody;
|
|
45
|
-
// .filter((c) => c.userId !== userId)
|
|
46
|
-
// .filter((c) =>
|
|
47
|
-
// readAt
|
|
48
|
-
// ? c.createdAt > readAt && c.createdAt <= inboxNotification.notifiedAt
|
|
49
|
-
// : c.createdAt <= inboxNotification.notifiedAt
|
|
50
|
-
// );
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
/** @internal */
|
|
54
|
-
export const getLastUnreadCommentWithMention = ({
|
|
55
|
-
comments,
|
|
56
|
-
mentionedUserId,
|
|
57
|
-
}: {
|
|
58
|
-
comments: CommentDataWithBody[];
|
|
59
|
-
mentionedUserId: string;
|
|
60
|
-
}): CommentDataWithBody | null => {
|
|
61
|
-
return (
|
|
62
|
-
Array.from(comments)
|
|
63
|
-
.reverse()
|
|
64
|
-
.filter((c) => c.userId !== mentionedUserId)
|
|
65
|
-
.find((c) => {
|
|
66
|
-
const mentionedUserIds = getMentionedIdsFromCommentBody(c.body);
|
|
67
|
-
return mentionedUserIds.includes(mentionedUserId);
|
|
68
|
-
}) ?? null
|
|
69
|
-
);
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export type ThreadNotificationData =
|
|
73
|
-
| { type: 'unreadMention'; comment: CommentDataWithBody }
|
|
74
|
-
| { type: 'unreadReplies'; comments: CommentDataWithBody[] };
|
|
75
|
-
|
|
76
|
-
/** @internal */
|
|
77
|
-
export const extractThreadNotificationData = async ({
|
|
78
|
-
client,
|
|
79
|
-
event,
|
|
80
|
-
}: {
|
|
81
|
-
client: Liveblocks;
|
|
82
|
-
event: ThreadNotificationEvent;
|
|
83
|
-
}): Promise<ThreadNotificationData | null> => {
|
|
84
|
-
const { threadId, roomId, userId, inboxNotificationId } = event.data;
|
|
85
|
-
const [thread, inboxNotification] = await Promise.all([
|
|
86
|
-
client.getThread({ roomId, threadId }),
|
|
87
|
-
client.getInboxNotification({ inboxNotificationId, userId }),
|
|
88
|
-
]);
|
|
89
|
-
|
|
90
|
-
const unreadComments = getUnreadComments({
|
|
91
|
-
comments: thread.comments,
|
|
92
|
-
inboxNotification,
|
|
93
|
-
userId,
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
if (unreadComments.length <= 0) {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const lastUnreadCommentWithMention = getLastUnreadCommentWithMention({
|
|
101
|
-
comments: unreadComments,
|
|
102
|
-
mentionedUserId: userId,
|
|
103
|
-
});
|
|
104
|
-
if (lastUnreadCommentWithMention !== null) {
|
|
105
|
-
return { type: 'unreadMention', comment: lastUnreadCommentWithMention };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
type: 'unreadReplies',
|
|
110
|
-
comments: unreadComments,
|
|
111
|
-
};
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
export type CommentEmailBaseData = {
|
|
115
|
-
id: string;
|
|
116
|
-
threadId: string;
|
|
117
|
-
roomId: string;
|
|
118
|
-
userId: string;
|
|
119
|
-
createdAt: Date;
|
|
120
|
-
url?: string;
|
|
121
|
-
rawBody: CommentBody;
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
export type ResolveRoomInfoArgs = {
|
|
125
|
-
/**
|
|
126
|
-
* The ID of the room to resolve
|
|
127
|
-
*/
|
|
128
|
-
roomId: string;
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
type PrepareThreadNotificationEmailBaseDataOptions = {
|
|
132
|
-
/**
|
|
133
|
-
* A function that returns room info from room IDs.
|
|
134
|
-
*/
|
|
135
|
-
resolveRoomInfo?: (
|
|
136
|
-
args: ResolveRoomInfoArgs,
|
|
137
|
-
) => OptionalPromise<DRI | undefined>;
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
export type ThreadNotificationEmailBaseData = (
|
|
141
|
-
| { type: 'unreadMention'; comment: CommentEmailBaseData }
|
|
142
|
-
| { type: 'unreadReplies'; comments: CommentEmailBaseData[] }
|
|
143
|
-
) & { roomInfo: DRI };
|
|
144
|
-
|
|
145
|
-
/** @internal */
|
|
146
|
-
export const makeCommentEmailBaseData = ({
|
|
147
|
-
roomInfo,
|
|
148
|
-
comment,
|
|
149
|
-
}: {
|
|
150
|
-
roomInfo: BaseRoomInfo | undefined;
|
|
151
|
-
comment: CommentDataWithBody;
|
|
152
|
-
}): CommentEmailBaseData => {
|
|
153
|
-
const url = roomInfo?.url
|
|
154
|
-
? generateCommentUrl({
|
|
155
|
-
roomUrl: roomInfo?.url,
|
|
156
|
-
commentId: comment.id,
|
|
157
|
-
})
|
|
158
|
-
: undefined;
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
id: comment.id,
|
|
162
|
-
userId: comment.userId,
|
|
163
|
-
threadId: comment.threadId,
|
|
164
|
-
roomId: comment.roomId,
|
|
165
|
-
createdAt: comment.createdAt,
|
|
166
|
-
url,
|
|
167
|
-
rawBody: comment.body,
|
|
168
|
-
};
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
/** @internal */
|
|
172
|
-
export const prepareThreadNotificationEmailBaseData = async ({
|
|
173
|
-
client,
|
|
174
|
-
event,
|
|
175
|
-
options = {},
|
|
176
|
-
}: {
|
|
177
|
-
client: Liveblocks;
|
|
178
|
-
event: ThreadNotificationEvent;
|
|
179
|
-
options?: PrepareThreadNotificationEmailBaseDataOptions;
|
|
180
|
-
}): Promise<ThreadNotificationEmailBaseData | null> => {
|
|
181
|
-
const { roomId } = event.data;
|
|
182
|
-
|
|
183
|
-
const roomInfo = options.resolveRoomInfo
|
|
184
|
-
? await options.resolveRoomInfo({ roomId })
|
|
185
|
-
: undefined;
|
|
186
|
-
const resolvedRoomInfo: DRI = {
|
|
187
|
-
...roomInfo,
|
|
188
|
-
name: roomInfo?.name ?? roomId,
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const data = await extractThreadNotificationData({ client, event });
|
|
192
|
-
if (data === null) {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
switch (data.type) {
|
|
197
|
-
case 'unreadMention':
|
|
198
|
-
return {
|
|
199
|
-
type: 'unreadMention',
|
|
200
|
-
comment: makeCommentEmailBaseData({
|
|
201
|
-
roomInfo,
|
|
202
|
-
comment: data.comment,
|
|
203
|
-
}),
|
|
204
|
-
roomInfo: resolvedRoomInfo,
|
|
205
|
-
};
|
|
206
|
-
case 'unreadReplies': {
|
|
207
|
-
return {
|
|
208
|
-
type: 'unreadReplies',
|
|
209
|
-
comments: data.comments.map((comment) =>
|
|
210
|
-
makeCommentEmailBaseData({ roomInfo, comment }),
|
|
211
|
-
),
|
|
212
|
-
roomInfo: resolvedRoomInfo,
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
/** @internal */
|
|
219
|
-
const resolveAuthorsInfo = async <U extends BaseUserMeta>({
|
|
220
|
-
comments,
|
|
221
|
-
resolveUsers,
|
|
222
|
-
}: {
|
|
223
|
-
comments: CommentEmailBaseData[];
|
|
224
|
-
resolveUsers?: (
|
|
225
|
-
args: ResolveUsersArgs,
|
|
226
|
-
) => OptionalPromise<(U['info'] | undefined)[] | undefined>;
|
|
227
|
-
}): Promise<Map<string, U['info']>> => {
|
|
228
|
-
const resolvedAuthors = new Map<string, U['info']>();
|
|
229
|
-
if (!resolveUsers) {
|
|
230
|
-
return resolvedAuthors;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const userIds = comments.map((c) => c.userId);
|
|
234
|
-
const users = await resolveUsers({ userIds });
|
|
235
|
-
|
|
236
|
-
for (const [index, userId] of userIds.entries()) {
|
|
237
|
-
const user = users?.[index];
|
|
238
|
-
if (user) {
|
|
239
|
-
resolvedAuthors.set(userId, user);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
return resolvedAuthors;
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
export type CommentEmailAsHtmlData<U extends BaseUserMeta = DU> = Omit<
|
|
247
|
-
CommentEmailBaseData,
|
|
248
|
-
'userId' | 'rawBody'
|
|
249
|
-
> & {
|
|
250
|
-
author: U;
|
|
251
|
-
htmlBody: string;
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
export type CommentEmailAsReactData<U extends BaseUserMeta = DU> = Omit<
|
|
255
|
-
CommentEmailBaseData,
|
|
256
|
-
'userId' | 'rawBody'
|
|
257
|
-
> & {
|
|
258
|
-
author: U;
|
|
259
|
-
reactBody: React.ReactNode;
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
type ThreadNotificationEmailUnreadRepliesData<
|
|
263
|
-
U extends BaseUserMeta,
|
|
264
|
-
C extends CommentEmailAsHtmlData<U> | CommentEmailAsReactData<U>,
|
|
265
|
-
> = {
|
|
266
|
-
type: 'unreadReplies';
|
|
267
|
-
comments: C[];
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
type ThreadNotificationEmailUnreadMentionsData<
|
|
271
|
-
U extends BaseUserMeta,
|
|
272
|
-
C extends CommentEmailAsHtmlData<U> | CommentEmailAsReactData<U>,
|
|
273
|
-
> = {
|
|
274
|
-
type: 'unreadMention';
|
|
275
|
-
comment: C;
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
// Note: export for testing helpers
|
|
279
|
-
export type ThreadNotificationEmailData<
|
|
280
|
-
U extends BaseUserMeta,
|
|
281
|
-
C extends CommentEmailAsHtmlData<U> | CommentEmailAsReactData<U>,
|
|
282
|
-
> = (
|
|
283
|
-
| ThreadNotificationEmailUnreadRepliesData<U, C>
|
|
284
|
-
| ThreadNotificationEmailUnreadMentionsData<U, C>
|
|
285
|
-
) & { roomInfo: DRI };
|
|
286
|
-
|
|
287
|
-
export type PrepareThreadNotificationEmailAsHtmlOptions<
|
|
288
|
-
U extends BaseUserMeta = DU,
|
|
289
|
-
> = PrepareThreadNotificationEmailBaseDataOptions & {
|
|
290
|
-
/**
|
|
291
|
-
* A function that returns info from user IDs.
|
|
292
|
-
*/
|
|
293
|
-
resolveUsers?: (
|
|
294
|
-
args: ResolveUsersArgs,
|
|
295
|
-
) => OptionalPromise<(U['info'] | undefined)[] | undefined>;
|
|
296
|
-
/**
|
|
297
|
-
* The styles used to customize the html elements in the resulting html safe string inside a comment body.
|
|
298
|
-
* Each styles has priority over the base styles inherited.
|
|
299
|
-
*/
|
|
300
|
-
styles?: Partial<ConvertCommentBodyAsHtmlStyles>;
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
export type ThreadNotificationEmailDataAsHtml = ThreadNotificationEmailData<
|
|
304
|
-
BaseUserMeta,
|
|
305
|
-
CommentEmailAsHtmlData
|
|
306
|
-
>;
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Prepares data from a `ThreadNotificationEvent` and convert comment bodies as an html safe string.
|
|
310
|
-
*
|
|
311
|
-
* @param client The `Liveblocks` node client
|
|
312
|
-
* @param event The `ThreadNotificationEvent` received in the webhook handler
|
|
313
|
-
* @param options The optional options to provide to resolve users, resolve room info
|
|
314
|
-
* and customize comment bodies html elements styles with inline CSS.
|
|
315
|
-
*
|
|
316
|
-
* It returns a `ThreadNotificationEmailDataAsHtml` or `null` if there are no unread comments (mention or replies).
|
|
317
|
-
*
|
|
318
|
-
* @example
|
|
319
|
-
* import { Liveblocks} from "@liveblocks/node"
|
|
320
|
-
* import { prepareThreadNotificationEmailAsHtml } from "@liveblocks/emails"
|
|
321
|
-
*
|
|
322
|
-
* const liveblocks = new Liveblocks({ secret: "sk_..." })
|
|
323
|
-
* const emailData = prepareThreadNotificationEmailAsHtml(
|
|
324
|
-
* liveblocks,
|
|
325
|
-
* event,
|
|
326
|
-
* {
|
|
327
|
-
* resolveUsers,
|
|
328
|
-
* resolveRoomInfo,
|
|
329
|
-
* styles,
|
|
330
|
-
* }
|
|
331
|
-
* )
|
|
332
|
-
*
|
|
333
|
-
*/
|
|
334
|
-
export async function prepareThreadNotificationEmailAsHtml(
|
|
335
|
-
client: Liveblocks,
|
|
336
|
-
event: ThreadNotificationEvent,
|
|
337
|
-
options: PrepareThreadNotificationEmailAsHtmlOptions<BaseUserMeta> = {},
|
|
338
|
-
): Promise<ThreadNotificationEmailDataAsHtml | null> {
|
|
339
|
-
const data = await prepareThreadNotificationEmailBaseData({
|
|
340
|
-
client,
|
|
341
|
-
event,
|
|
342
|
-
options: { resolveRoomInfo: options.resolveRoomInfo },
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
if (data === null) {
|
|
346
|
-
return null;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
const batchUsersResolver = createBatchUsersResolver<BaseUserMeta>({
|
|
350
|
-
resolveUsers: options.resolveUsers,
|
|
351
|
-
callerName: 'prepareThreadNotificationEmailAsHtml',
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
switch (data.type) {
|
|
355
|
-
case 'unreadMention': {
|
|
356
|
-
const { comment } = data;
|
|
357
|
-
|
|
358
|
-
const authorsInfoPromise = resolveAuthorsInfo({
|
|
359
|
-
comments: [comment],
|
|
360
|
-
resolveUsers: batchUsersResolver.resolveUsers,
|
|
361
|
-
});
|
|
362
|
-
const commentBodyPromise = convertCommentBodyAsHtml(comment.rawBody, {
|
|
363
|
-
resolveUsers: batchUsersResolver.resolveUsers,
|
|
364
|
-
styles: options.styles,
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
await batchUsersResolver.resolve();
|
|
368
|
-
|
|
369
|
-
const [authorsInfo, commentBodyHtml] = await Promise.all([
|
|
370
|
-
authorsInfoPromise,
|
|
371
|
-
commentBodyPromise,
|
|
372
|
-
]);
|
|
373
|
-
const authorInfo = authorsInfo.get(comment.userId);
|
|
374
|
-
|
|
375
|
-
return {
|
|
376
|
-
type: 'unreadMention',
|
|
377
|
-
comment: {
|
|
378
|
-
id: comment.id,
|
|
379
|
-
threadId: comment.threadId,
|
|
380
|
-
roomId: comment.roomId,
|
|
381
|
-
author: authorInfo
|
|
382
|
-
? { id: comment.userId, info: authorInfo }
|
|
383
|
-
: { id: comment.userId, info: { name: comment.userId } },
|
|
384
|
-
createdAt: comment.createdAt,
|
|
385
|
-
url: comment.url,
|
|
386
|
-
htmlBody: commentBodyHtml,
|
|
387
|
-
},
|
|
388
|
-
roomInfo: data.roomInfo,
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
case 'unreadReplies': {
|
|
392
|
-
const { comments } = data;
|
|
393
|
-
|
|
394
|
-
const authorsInfoPromise = resolveAuthorsInfo({
|
|
395
|
-
comments,
|
|
396
|
-
resolveUsers: batchUsersResolver.resolveUsers,
|
|
397
|
-
});
|
|
398
|
-
const commentBodiesPromises = comments.map((c) =>
|
|
399
|
-
convertCommentBodyAsHtml(c.rawBody, {
|
|
400
|
-
resolveUsers: batchUsersResolver.resolveUsers,
|
|
401
|
-
styles: options.styles,
|
|
402
|
-
}),
|
|
403
|
-
);
|
|
404
|
-
|
|
405
|
-
await batchUsersResolver.resolve();
|
|
406
|
-
|
|
407
|
-
const [authorsInfo, ...commentBodies] = await Promise.all([
|
|
408
|
-
authorsInfoPromise,
|
|
409
|
-
...commentBodiesPromises,
|
|
410
|
-
]);
|
|
411
|
-
|
|
412
|
-
return {
|
|
413
|
-
type: 'unreadReplies',
|
|
414
|
-
comments: comments.map((comment, index) => {
|
|
415
|
-
const authorInfo = authorsInfo.get(comment.userId);
|
|
416
|
-
const commentBodyHtml = commentBodies[index];
|
|
417
|
-
|
|
418
|
-
return {
|
|
419
|
-
id: comment.id,
|
|
420
|
-
threadId: comment.threadId,
|
|
421
|
-
roomId: comment.roomId,
|
|
422
|
-
author: authorInfo
|
|
423
|
-
? { id: comment.userId, info: authorInfo }
|
|
424
|
-
: { id: comment.userId, info: { name: comment.userId } },
|
|
425
|
-
createdAt: comment.createdAt,
|
|
426
|
-
url: comment.url,
|
|
427
|
-
htmlBody: commentBodyHtml ?? '',
|
|
428
|
-
};
|
|
429
|
-
}),
|
|
430
|
-
roomInfo: data.roomInfo,
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
export type PrepareThreadNotificationEmailAsReactOptions<
|
|
437
|
-
U extends BaseUserMeta = DU,
|
|
438
|
-
> = PrepareThreadNotificationEmailBaseDataOptions & {
|
|
439
|
-
/**
|
|
440
|
-
* A function that returns info from user IDs.
|
|
441
|
-
*/
|
|
442
|
-
resolveUsers?: (
|
|
443
|
-
args: ResolveUsersArgs,
|
|
444
|
-
) => OptionalPromise<(U['info'] | undefined)[] | undefined>;
|
|
445
|
-
/**
|
|
446
|
-
* The components used to customize the resulting React nodes inside a comment body.
|
|
447
|
-
* Each components has priority over the base components inherited internally defined.
|
|
448
|
-
*/
|
|
449
|
-
components?: Partial<ConvertCommentBodyAsReactComponents<U>>;
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
export type ThreadNotificationEmailDataAsReact = ThreadNotificationEmailData<
|
|
453
|
-
BaseUserMeta,
|
|
454
|
-
CommentEmailAsReactData
|
|
455
|
-
>;
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* Prepares data from a `ThreadNotificationEvent` and convert comment bodies as React nodes.
|
|
459
|
-
*
|
|
460
|
-
* @param client The `Liveblocks` node client
|
|
461
|
-
* @param event The `ThreadNotificationEvent` received in the webhook handler
|
|
462
|
-
* @param options The optional options to provide to resolve users, resolve room info and customize comment bodies React components.
|
|
463
|
-
*
|
|
464
|
-
* It returns a `ThreadNotificationEmailDataAsReact` or `null` if there are no unread comments (mention or replies).
|
|
465
|
-
*
|
|
466
|
-
* @example
|
|
467
|
-
* import { Liveblocks} from "@liveblocks/node"
|
|
468
|
-
* import { prepareThreadNotificationEmailAsReact } from "@liveblocks/emails"
|
|
469
|
-
*
|
|
470
|
-
* const liveblocks = new Liveblocks({ secret: "sk_..." })
|
|
471
|
-
* const emailData = prepareThreadNotificationEmailAsReact(
|
|
472
|
-
* liveblocks,
|
|
473
|
-
* event,
|
|
474
|
-
* {
|
|
475
|
-
* resolveUsers,
|
|
476
|
-
* resolveRoomInfo,
|
|
477
|
-
* components,
|
|
478
|
-
* }
|
|
479
|
-
* )
|
|
480
|
-
*
|
|
481
|
-
*/
|
|
482
|
-
export async function prepareThreadNotificationEmailAsReact(
|
|
483
|
-
client: Liveblocks,
|
|
484
|
-
event: ThreadNotificationEvent,
|
|
485
|
-
options: PrepareThreadNotificationEmailAsReactOptions<BaseUserMeta> = {},
|
|
486
|
-
): Promise<ThreadNotificationEmailDataAsReact | null> {
|
|
487
|
-
const data = await prepareThreadNotificationEmailBaseData({
|
|
488
|
-
client,
|
|
489
|
-
event,
|
|
490
|
-
options: { resolveRoomInfo: options.resolveRoomInfo },
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
if (data === null) {
|
|
494
|
-
return null;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
const batchUsersResolver = createBatchUsersResolver<BaseUserMeta>({
|
|
498
|
-
resolveUsers: options.resolveUsers,
|
|
499
|
-
callerName: 'prepareThreadNotificationEmailAsReact',
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
switch (data.type) {
|
|
503
|
-
case 'unreadMention': {
|
|
504
|
-
const { comment } = data;
|
|
505
|
-
|
|
506
|
-
const authorsInfoPromise = resolveAuthorsInfo({
|
|
507
|
-
comments: [comment],
|
|
508
|
-
resolveUsers: batchUsersResolver.resolveUsers,
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
const commentBodyPromise = convertCommentBodyAsReact(comment.rawBody, {
|
|
512
|
-
resolveUsers: batchUsersResolver.resolveUsers,
|
|
513
|
-
components: options.components,
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
await batchUsersResolver.resolve();
|
|
517
|
-
|
|
518
|
-
const [authorsInfo, commentBodyReact] = await Promise.all([
|
|
519
|
-
authorsInfoPromise,
|
|
520
|
-
commentBodyPromise,
|
|
521
|
-
]);
|
|
522
|
-
const authorInfo = authorsInfo.get(comment.userId);
|
|
523
|
-
|
|
524
|
-
return {
|
|
525
|
-
type: 'unreadMention',
|
|
526
|
-
comment: {
|
|
527
|
-
id: comment.id,
|
|
528
|
-
threadId: comment.threadId,
|
|
529
|
-
roomId: comment.roomId,
|
|
530
|
-
author: authorInfo
|
|
531
|
-
? { id: comment.userId, info: authorInfo }
|
|
532
|
-
: { id: comment.userId, info: { name: comment.userId } },
|
|
533
|
-
createdAt: comment.createdAt,
|
|
534
|
-
url: comment.url,
|
|
535
|
-
reactBody: commentBodyReact,
|
|
536
|
-
},
|
|
537
|
-
roomInfo: data.roomInfo,
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
case 'unreadReplies': {
|
|
541
|
-
const { comments } = data;
|
|
542
|
-
const authorsInfoPromise = resolveAuthorsInfo({
|
|
543
|
-
comments,
|
|
544
|
-
resolveUsers: batchUsersResolver.resolveUsers,
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
const commentBodiesPromises = comments.map((c) =>
|
|
548
|
-
convertCommentBodyAsReact(c.rawBody, {
|
|
549
|
-
resolveUsers: batchUsersResolver.resolveUsers,
|
|
550
|
-
components: options.components,
|
|
551
|
-
}),
|
|
552
|
-
);
|
|
553
|
-
|
|
554
|
-
await batchUsersResolver.resolve();
|
|
555
|
-
|
|
556
|
-
const [authorsInfo, ...commentBodies] = await Promise.all([
|
|
557
|
-
authorsInfoPromise,
|
|
558
|
-
...commentBodiesPromises,
|
|
559
|
-
]);
|
|
560
|
-
|
|
561
|
-
return {
|
|
562
|
-
type: 'unreadReplies',
|
|
563
|
-
comments: comments.map((comment, index) => {
|
|
564
|
-
const authorInfo = authorsInfo.get(comment.userId);
|
|
565
|
-
const commentBodyReact = commentBodies[index];
|
|
566
|
-
|
|
567
|
-
return {
|
|
568
|
-
id: comment.id,
|
|
569
|
-
threadId: comment.threadId,
|
|
570
|
-
roomId: comment.roomId,
|
|
571
|
-
author: authorInfo
|
|
572
|
-
? { id: comment.userId, info: authorInfo }
|
|
573
|
-
: { id: comment.userId, info: { name: comment.userId } },
|
|
574
|
-
createdAt: comment.createdAt,
|
|
575
|
-
url: comment.url,
|
|
576
|
-
reactBody: commentBodyReact ?? null,
|
|
577
|
-
};
|
|
578
|
-
}),
|
|
579
|
-
roomInfo: data.roomInfo,
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
// import type { LsonObject } from "../crdts/Lson";
|
|
2
|
-
import type { Json, JsonObject } from '../lib/Json';
|
|
3
|
-
import type { BaseActivitiesData } from '../protocol/BaseActivitiesData';
|
|
4
|
-
import type { BaseRoomInfo } from '../protocol/BaseRoomInfo';
|
|
5
|
-
import type { BaseUserMeta } from '../protocol/BaseUserMeta';
|
|
6
|
-
import type { BaseMetadata } from '../protocol/Comments';
|
|
7
|
-
|
|
8
|
-
// declare global {
|
|
9
|
-
// /**
|
|
10
|
-
// * Namespace for user-defined Liveblocks types.
|
|
11
|
-
// */
|
|
12
|
-
// export interface Liveblocks {
|
|
13
|
-
// [key: string]: unknown;
|
|
14
|
-
// }
|
|
15
|
-
// }
|
|
16
|
-
|
|
17
|
-
// NOTE: When extending this list, make sure to also add respective error
|
|
18
|
-
// message docs (in ../../docs/pages/errors/*.mdx).
|
|
19
|
-
type ExtendableTypes =
|
|
20
|
-
| 'Presence'
|
|
21
|
-
| 'Storage'
|
|
22
|
-
| 'UserMeta'
|
|
23
|
-
| 'RoomEvent'
|
|
24
|
-
| 'ThreadMetadata'
|
|
25
|
-
| 'RoomInfo'
|
|
26
|
-
| 'ActivitiesData';
|
|
27
|
-
|
|
28
|
-
type MakeErrorString<
|
|
29
|
-
K extends ExtendableTypes,
|
|
30
|
-
Reason extends string = 'does not match its requirements',
|
|
31
|
-
> = `The type you provided for '${K}' ${Reason}. To learn how to fix this, see https://liveblocks.io/docs/errors/${K}`;
|
|
32
|
-
|
|
33
|
-
type GetOverride<
|
|
34
|
-
K extends ExtendableTypes,
|
|
35
|
-
B,
|
|
36
|
-
Reason extends string = 'does not match its requirements',
|
|
37
|
-
> = GetOverrideOrErrorValue<K, B, MakeErrorString<K, Reason>>;
|
|
38
|
-
|
|
39
|
-
type GetOverrideOrErrorValue<
|
|
40
|
-
K extends ExtendableTypes,
|
|
41
|
-
B,
|
|
42
|
-
ErrorType,
|
|
43
|
-
> = unknown extends Liveblocks[K]
|
|
44
|
-
? B
|
|
45
|
-
: Liveblocks[K] extends B
|
|
46
|
-
? Liveblocks[K]
|
|
47
|
-
: ErrorType;
|
|
48
|
-
|
|
49
|
-
// ------------------------------------------------------------------------
|
|
50
|
-
|
|
51
|
-
export type DP = GetOverride<
|
|
52
|
-
'Presence',
|
|
53
|
-
JsonObject,
|
|
54
|
-
'is not a valid JSON object'
|
|
55
|
-
>;
|
|
56
|
-
|
|
57
|
-
export type DS = GetOverride<
|
|
58
|
-
'Storage',
|
|
59
|
-
JsonObject, // TODO: LsonObject,
|
|
60
|
-
'is not a valid LSON value'
|
|
61
|
-
>;
|
|
62
|
-
|
|
63
|
-
export type DU = GetOverrideOrErrorValue<
|
|
64
|
-
'UserMeta',
|
|
65
|
-
BaseUserMeta,
|
|
66
|
-
// Normally, the error will be a string value, but by building this custom
|
|
67
|
-
// error shape for the UserMeta type, the errors will more likely trickle
|
|
68
|
-
// down into the end user's code base, instead of happening inside
|
|
69
|
-
// node_modules, where it may remain hidden if skipLibCheck is set in the end
|
|
70
|
-
// user's project.
|
|
71
|
-
Record<'id' | 'info', MakeErrorString<'UserMeta'>>
|
|
72
|
-
>;
|
|
73
|
-
|
|
74
|
-
export type DE = GetOverride<'RoomEvent', Json, 'is not a valid JSON value'>;
|
|
75
|
-
|
|
76
|
-
export type DM = GetOverride<'ThreadMetadata', BaseMetadata>;
|
|
77
|
-
|
|
78
|
-
export type DRI = GetOverride<'RoomInfo', BaseRoomInfo>;
|
|
79
|
-
export type DAD = GetOverrideOrErrorValue<
|
|
80
|
-
'ActivitiesData',
|
|
81
|
-
BaseActivitiesData,
|
|
82
|
-
{
|
|
83
|
-
[K in keyof Liveblocks['ActivitiesData']]: "At least one of the custom notification kinds you provided for 'ActivitiesData' does not match its requirements. To learn how to fix this, see https://liveblocks.io/docs/errors/ActivitiesData";
|
|
84
|
-
}
|
|
85
|
-
>;
|
|
86
|
-
|
|
87
|
-
export type KDAD = keyof DAD extends `$${string}`
|
|
88
|
-
? keyof DAD
|
|
89
|
-
: "Custom notification kinds must start with '$' but your custom 'ActivitiesData' type contains at least one kind which doesn't. To learn how to fix this, see https://liveblocks.io/docs/errors/ActivitiesData";
|
package/src/rooms/index.ts
DELETED