@builder6/rooms 3.0.4 → 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.
Files changed (54) hide show
  1. package/package.json +10 -7
  2. package/.prettierrc +0 -4
  3. package/src/emails/UnreadMention.tsx +0 -37
  4. package/src/emails/UnreadReplies.tsx +0 -49
  5. package/src/emails/_components/comment.tsx +0 -70
  6. package/src/emails/_components/header.tsx +0 -26
  7. package/src/emails/_components/headline.tsx +0 -20
  8. package/src/emails/_components/layout.tsx +0 -45
  9. package/src/emails/_lib/types.ts +0 -10
  10. package/src/emails/_styles/colors.ts +0 -7
  11. package/src/emails/_utils/cn.ts +0 -6
  12. package/src/emails/_utils/comments.ts +0 -61
  13. package/src/emails/_utils/getProps.ts +0 -7
  14. package/src/plugin.module.ts +0 -3
  15. package/src/rooms/app.controller.ts +0 -89
  16. package/src/rooms/dtos/inbox_notifications.dto.ts +0 -13
  17. package/src/rooms/dtos/notifications.dto.ts +0 -14
  18. package/src/rooms/dtos/room_members.dto.ts +0 -32
  19. package/src/rooms/dtos/rooms.dto.ts +0 -51
  20. package/src/rooms/emailNotification.service.tsx +0 -126
  21. package/src/rooms/emails/comment-body.tsx +0 -342
  22. package/src/rooms/emails/comment-with-body.ts +0 -24
  23. package/src/rooms/emails/index.ts +0 -25
  24. package/src/rooms/emails/lib/batch-users-resolver.ts +0 -120
  25. package/src/rooms/emails/lib/css-properties.ts +0 -123
  26. package/src/rooms/emails/lib/warning.ts +0 -25
  27. package/src/rooms/emails/thread-notification.tsx +0 -583
  28. package/src/rooms/globals/augmentation.ts +0 -89
  29. package/src/rooms/index.ts +0 -5
  30. package/src/rooms/lib/DateToString.ts +0 -9
  31. package/src/rooms/lib/Json.ts +0 -34
  32. package/src/rooms/lib/utils.ts +0 -240
  33. package/src/rooms/liveblocks.service.ts +0 -25
  34. package/src/rooms/notifications.service.ts +0 -235
  35. package/src/rooms/protocol/AuthToken.ts +0 -126
  36. package/src/rooms/protocol/Authentication.ts +0 -18
  37. package/src/rooms/protocol/BaseActivitiesData.ts +0 -5
  38. package/src/rooms/protocol/BaseRoomInfo.ts +0 -15
  39. package/src/rooms/protocol/BaseUserMeta.ts +0 -28
  40. package/src/rooms/protocol/ClientMsg.ts +0 -94
  41. package/src/rooms/protocol/Comments.ts +0 -202
  42. package/src/rooms/protocol/InboxNotifications.ts +0 -75
  43. package/src/rooms/protocol/Op.ts +0 -143
  44. package/src/rooms/protocol/SerializedCrdt.ts +0 -61
  45. package/src/rooms/protocol/ServerMsg.ts +0 -307
  46. package/src/rooms/protocol/VersionHistory.ts +0 -9
  47. package/src/rooms/rooms.controller.ts +0 -587
  48. package/src/rooms/rooms.gateway.ts +0 -267
  49. package/src/rooms/rooms.guard.ts +0 -52
  50. package/src/rooms/rooms.module.ts +0 -38
  51. package/src/rooms/rooms.moleculer.ts +0 -80
  52. package/src/rooms/rooms.service.ts +0 -723
  53. package/tsconfig.json +0 -10
  54. package/yarn-error.log +0 -17218
@@ -1,126 +0,0 @@
1
- import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
2
- import { ConfigService } from '@nestjs/config';
3
- import { ThreadNotificationEvent } from '@liveblocks/node';
4
- import { prepareThreadNotificationEmailAsReact } from './emails';
5
- import * as React from 'react';
6
- import { render } from '@react-email/render';
7
- import { EmailService } from '@builder6/email';
8
- import UnreadRepliesEmail from '../emails/UnreadReplies';
9
- import UnreadMentionEmail from '../emails/UnreadMention';
10
- import { RoomsService } from './rooms.service';
11
-
12
- @Injectable()
13
- export class EmailNotificationService {
14
- private readonly logger = new Logger(EmailNotificationService.name);
15
-
16
- constructor(
17
- @Inject(forwardRef(() => RoomsService))
18
- private roomsService: RoomsService,
19
- private configService: ConfigService,
20
- private emailService: EmailService,
21
- ) {}
22
-
23
- async sendThreadNotificationEmail({
24
- roomId,
25
- threadId,
26
- userId,
27
- fromUserId,
28
- inboxNotificationId,
29
- spaceId,
30
- }: {
31
- roomId: string;
32
- threadId: string;
33
- userId: string;
34
- fromUserId?: string;
35
- inboxNotificationId: string;
36
- spaceId: string;
37
- }) {
38
- const event: ThreadNotificationEvent = {
39
- type: 'notification',
40
- data: {
41
- channel: 'email',
42
- kind: 'thread',
43
- projectId: 'my-project-id',
44
- roomId,
45
- userId,
46
- threadId,
47
- inboxNotificationId,
48
- createdAt: new Date().toISOString(),
49
- },
50
- };
51
- const emailData = await prepareThreadNotificationEmailAsReact(
52
- this.roomsService as any,
53
- event,
54
- {
55
- resolveUsers: async ({ userIds }) => {
56
- return await this.roomsService.resolveUsers({ userIds });
57
- },
58
- resolveRoomInfo: async ({ roomId }) => {
59
- const room = await this.roomsService.resolveRoomInfo({
60
- roomId,
61
- });
62
- return room;
63
- },
64
- },
65
- );
66
- if (!emailData) {
67
- return;
68
- }
69
-
70
- const fromUsers = await this.roomsService.resolveUsers({
71
- userIds: [fromUserId],
72
- });
73
-
74
- const fromUser = fromUsers[0] || { name: 'Somebody' };
75
- const company = {
76
- name: this.configService.get('tenant.name'),
77
- url: this.configService.get('host'),
78
- logoUrl: this.configService.get('tenant.logo.url'),
79
- };
80
- const room = {
81
- name: emailData.roomInfo.name,
82
- url: emailData.roomInfo.url,
83
- };
84
-
85
- let email;
86
- let subject;
87
-
88
- switch (emailData.type) {
89
- // Handle unread replies use case
90
- case 'unreadReplies': {
91
- email = (
92
- <UnreadRepliesEmail
93
- company={company}
94
- room={room}
95
- comments={emailData.comments}
96
- />
97
- );
98
- subject = `[${company.name}] ${fromUser.name} replyed you: ${room.name}`;
99
- break;
100
- }
101
- // Handle last unread comment with mention use case
102
- case 'unreadMention': {
103
- email = (
104
- <UnreadMentionEmail
105
- company={company}
106
- room={room}
107
- comment={emailData.comment}
108
- />
109
- );
110
- subject = `[${company.name}] ${fromUser.name} mentioned you: ${room.name}`;
111
- break;
112
- }
113
- }
114
-
115
- const html = await render(email);
116
-
117
- await this.emailService.sendMailToUsers({
118
- spaceId,
119
- toUserIds: [userId],
120
- fromUserId,
121
- subject: subject,
122
- html,
123
- });
124
- return html;
125
- }
126
- }
@@ -1,342 +0,0 @@
1
- import type {
2
- BaseUserMeta,
3
- CommentBody,
4
- CommentBodyLink,
5
- CommentBodyMention,
6
- CommentBodyText,
7
- DU,
8
- OptionalPromise,
9
- ResolveUsersArgs,
10
- } from "@liveblocks/core";
11
- import {
12
- html,
13
- htmlSafe,
14
- isCommentBodyLink,
15
- isCommentBodyMention,
16
- isCommentBodyText,
17
- resolveUsersInCommentBody,
18
- stringifyCommentBody,
19
- toAbsoluteUrl,
20
- } from "@liveblocks/core";
21
- import * as React from "react";
22
-
23
- import type { CSSProperties } from "./lib/css-properties";
24
- import { toInlineCSSString } from "./lib/css-properties";
25
-
26
- export type CommentBodyContainerComponentProps = {
27
- /**
28
- * The blocks of the comment body
29
- */
30
- children: React.ReactNode;
31
- };
32
-
33
- export type CommentBodyParagraphComponentProps = {
34
- /**
35
- * The text content of the paragraph.
36
- */
37
- children: React.ReactNode;
38
- };
39
-
40
- export type CommentBodyTextComponentProps = {
41
- /**
42
- * The text element.
43
- */
44
- element: CommentBodyText;
45
- };
46
-
47
- export type CommentBodyLinkComponentProps = {
48
- /**
49
- * The link element.
50
- */
51
- element: CommentBodyLink;
52
-
53
- /**
54
- * The absolute URL of the link.
55
- */
56
- href: string;
57
- };
58
-
59
- export type CommentBodyMentionComponentProps<U extends BaseUserMeta = DU> = {
60
- /**
61
- * The mention element.
62
- */
63
- element: CommentBodyMention;
64
-
65
- /**
66
- * The mention's user info, if the `resolvedUsers` option was provided.
67
- */
68
- user?: U["info"];
69
- };
70
-
71
- export type ConvertCommentBodyAsReactComponents<U extends BaseUserMeta = DU> = {
72
- /**
73
- *
74
- * The component used to act as a container to wrap comment body blocks,
75
- */
76
- Container: React.ComponentType<CommentBodyContainerComponentProps>;
77
- /**
78
- * The component used to display paragraphs.
79
- */
80
- Paragraph: React.ComponentType<CommentBodyParagraphComponentProps>;
81
-
82
- /**
83
- * The component used to display text elements.
84
- */
85
- Text: React.ComponentType<CommentBodyTextComponentProps>;
86
-
87
- /**
88
- * The component used to display links.
89
- */
90
- Link: React.ComponentType<CommentBodyLinkComponentProps>;
91
-
92
- /**
93
- * The component used to display mentions.
94
- */
95
- Mention: React.ComponentType<CommentBodyMentionComponentProps<U>>;
96
- };
97
-
98
- const baseComponents: ConvertCommentBodyAsReactComponents<BaseUserMeta> = {
99
- Container: ({ children }) => <div>{children}</div>,
100
- Paragraph: ({ children }) => <p>{children}</p>,
101
- Text: ({ element }) => {
102
- // Note: construction following the schema 👇
103
- // <code><s><em><strong>{element.text}</strong></s></em></code>
104
- let children: React.ReactNode = element.text;
105
-
106
- if (element.bold) {
107
- children = <strong>{children}</strong>;
108
- }
109
-
110
- if (element.italic) {
111
- children = <em>{children}</em>;
112
- }
113
-
114
- if (element.strikethrough) {
115
- children = <s>{children}</s>;
116
- }
117
-
118
- if (element.code) {
119
- children = <code>{children}</code>;
120
- }
121
-
122
- return <span>{children}</span>;
123
- },
124
- Link: ({ element, href }) => (
125
- <a href={href} target="_blank" rel="noopener noreferrer">
126
- {element.text ?? element.url}
127
- </a>
128
- ),
129
- Mention: ({ element, user }) => (
130
- <span data-mention>@{user?.name ?? element.id}</span>
131
- ),
132
- };
133
-
134
- export type ConvertCommentBodyAsReactOptions<U extends BaseUserMeta = DU> = {
135
- /**
136
- * The components used to customize the resulting React nodes. Each components has
137
- * priority over the base components inherited.
138
- */
139
- components?: Partial<ConvertCommentBodyAsReactComponents<U>>;
140
- /**
141
- * A function that returns user info from user IDs.
142
- */
143
- resolveUsers?: (
144
- args: ResolveUsersArgs
145
- ) => OptionalPromise<(U["info"] | undefined)[] | undefined>;
146
- };
147
-
148
- /**
149
- * Convert a `CommentBody` into React elements
150
- */
151
- export async function convertCommentBodyAsReact(
152
- body: CommentBody,
153
- options?: ConvertCommentBodyAsReactOptions<BaseUserMeta>
154
- ): Promise<React.ReactNode> {
155
- const Components = {
156
- ...baseComponents,
157
- ...options?.components,
158
- };
159
- const resolvedUsers = await resolveUsersInCommentBody(
160
- body,
161
- options?.resolveUsers
162
- );
163
-
164
- const blocks = body.content.map((block, index) => {
165
- switch (block.type) {
166
- case "paragraph": {
167
- const children = block.children.map((inline, inlineIndex) => {
168
- if (isCommentBodyMention(inline)) {
169
- return inline.id ? (
170
- <Components.Mention
171
- key={`lb-comment-body-mention-${inlineIndex}`}
172
- element={inline}
173
- user={resolvedUsers.get(inline.id)}
174
- />
175
- ) : null;
176
- }
177
-
178
- if (isCommentBodyLink(inline)) {
179
- const href = toAbsoluteUrl(inline.url) ?? inline.url;
180
- return (
181
- <Components.Link
182
- key={`lb-comment-body-link-${inlineIndex}`}
183
- element={inline}
184
- href={href}
185
- />
186
- );
187
- }
188
-
189
- if (isCommentBodyText(inline)) {
190
- return (
191
- <Components.Text
192
- key={`lb-comment-body-text-${inlineIndex}`}
193
- element={inline}
194
- />
195
- );
196
- }
197
-
198
- return null;
199
- });
200
-
201
- return (
202
- <Components.Paragraph key={`lb-comment-body-paragraph-${index}`}>
203
- {children}
204
- </Components.Paragraph>
205
- );
206
- }
207
- default:
208
- console.warn(
209
- `Unsupported comment body block type: "${JSON.stringify(block.type)}"`
210
- );
211
- return null;
212
- }
213
- });
214
-
215
- return (
216
- <Components.Container key={"lb-comment-body-container"}>
217
- {blocks}
218
- </Components.Container>
219
- );
220
- }
221
-
222
- export type ConvertCommentBodyAsHtmlStyles = {
223
- /**
224
- * The default inline CSS styles used to display paragraphs.
225
- */
226
- paragraph: CSSProperties;
227
- /**
228
- * The default inline CSS styles used to display text `<strong />` elements.
229
- */
230
- strong: CSSProperties;
231
- /**
232
- * The default inline CSS styles used to display text `<code />` elements.
233
- */
234
- code: CSSProperties;
235
- /**
236
- * The default inline CSS styles used to display links.
237
- */
238
- mention: CSSProperties;
239
- /**
240
- * The default inline CSS styles used to display mentions.
241
- */
242
- link: CSSProperties;
243
- };
244
-
245
- const baseStyles: ConvertCommentBodyAsHtmlStyles = {
246
- paragraph: {
247
- fontSize: "14px",
248
- },
249
- strong: {
250
- fontWeight: 500,
251
- },
252
- code: {
253
- fontFamily:
254
- 'ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", "Oxygen Mono", "Ubuntu Mono", "Source Code Pro", "Fira Mono", "Droid Sans Mono", "Consolas", "Courier New", monospace',
255
- backgroundColor: "rgba(0,0,0,0.05)",
256
- border: "solid 1px rgba(0,0,0,0.1)",
257
- borderRadius: "4px",
258
- },
259
- mention: {
260
- color: "blue",
261
- },
262
- link: {
263
- textDecoration: "underline",
264
- },
265
- };
266
-
267
- export type ConvertCommentBodyAsHtmlOptions<U extends BaseUserMeta = DU> = {
268
- /**
269
- * The styles used to customize the html elements in the resulting html safe string.
270
- * Each styles has priority over the base styles inherited.
271
- */
272
- styles?: Partial<ConvertCommentBodyAsHtmlStyles>;
273
- /**
274
- * A function that returns user info from user IDs.
275
- */
276
- resolveUsers?: (
277
- args: ResolveUsersArgs
278
- ) => OptionalPromise<(U["info"] | undefined)[] | undefined>;
279
- };
280
-
281
- /**
282
- * Convert a `CommentBody` into an html safe string
283
- * with inline css styles
284
- */
285
- export async function convertCommentBodyAsHtml(
286
- body: CommentBody,
287
- options?: ConvertCommentBodyAsHtmlOptions<BaseUserMeta>
288
- ): Promise<string> {
289
- const styles = { ...baseStyles, ...options?.styles };
290
-
291
- const htmlBody = await stringifyCommentBody(body, {
292
- format: "html",
293
- resolveUsers: options?.resolveUsers,
294
- elements: {
295
- // NOTE: using prettier-ignore to preserve template strings
296
- paragraph: ({ children }) =>
297
- // prettier-ignore
298
- children ? html`<p style="${toInlineCSSString(styles.paragraph)}">${htmlSafe(children)}</p>` : children,
299
- text: ({ element }) => {
300
- // Note: construction following the schema 👇
301
- // <code><s><em><strong>{element.text}</strong></s></em></code>
302
- let children = element.text;
303
-
304
- if (!children) {
305
- return children;
306
- }
307
-
308
- if (element.bold) {
309
- // prettier-ignore
310
- children = html`<strong style="${toInlineCSSString(styles.strong)}">${children}</strong>`;
311
- }
312
-
313
- if (element.italic) {
314
- // prettier-ignore
315
- children = html`<em>${children}</em>`;
316
- }
317
-
318
- if (element.strikethrough) {
319
- // prettier-ignore
320
- children = html`<s>${children}</s>`;
321
- }
322
-
323
- if (element.code) {
324
- // prettier-ignore
325
- children = html`<code style="${toInlineCSSString(styles.code)}">${children}</code>`;
326
- }
327
-
328
- return children;
329
- },
330
- link: ({ element, href }) => {
331
- // prettier-ignore
332
- return html`<a href="${href}" target="_blank" rel="noopener noreferrer" style="${toInlineCSSString(styles.link)}">${element.text ?? element.url}</a>`;
333
- },
334
- mention: ({ element, user }) => {
335
- // prettier-ignore
336
- return html`<span data-mention style="${toInlineCSSString(styles.mention)}">@${user?.name ?? element.id}</span>`;
337
- },
338
- },
339
- });
340
-
341
- return htmlBody;
342
- }
@@ -1,24 +0,0 @@
1
- import type { CommentBody, CommentData } from "@liveblocks/core";
2
-
3
- export type CommentDataWithBody = Omit<CommentData, "body" | "deletedAt"> & {
4
- body: CommentBody;
5
- deletedAt?: never;
6
- };
7
-
8
- const isCommentDataWithBody = (
9
- comment: CommentData
10
- ): comment is CommentDataWithBody => {
11
- return comment.body !== undefined && comment.deletedAt === undefined;
12
- };
13
-
14
- export function filterCommentsWithBody(
15
- comments: CommentData[]
16
- ): CommentDataWithBody[] {
17
- const commentsWithBody: CommentDataWithBody[] = [];
18
- for (const comment of comments) {
19
- if (isCommentDataWithBody(comment)) {
20
- commentsWithBody.push(comment);
21
- }
22
- }
23
- return commentsWithBody;
24
- }
@@ -1,25 +0,0 @@
1
- import { detectDupes } from "@liveblocks/core";
2
-
3
- export type {
4
- CommentBodyContainerComponentProps,
5
- CommentBodyLinkComponentProps,
6
- CommentBodyMentionComponentProps,
7
- CommentBodyParagraphComponentProps,
8
- CommentBodyTextComponentProps,
9
- ConvertCommentBodyAsHtmlStyles,
10
- ConvertCommentBodyAsReactComponents,
11
- } from "./comment-body";
12
- export type {
13
- CommentEmailAsHtmlData,
14
- CommentEmailAsReactData,
15
- PrepareThreadNotificationEmailAsHtmlOptions,
16
- PrepareThreadNotificationEmailAsReactOptions,
17
- ResolveRoomInfoArgs,
18
- ThreadNotificationEmailDataAsHtml,
19
- ThreadNotificationEmailDataAsReact,
20
- } from "./thread-notification";
21
- export {
22
- prepareThreadNotificationEmailAsHtml,
23
- prepareThreadNotificationEmailAsReact,
24
- } from "./thread-notification";
25
- export type { ResolveUsersArgs } from "@liveblocks/core";
@@ -1,120 +0,0 @@
1
- import type {
2
- BaseUserMeta,
3
- DU,
4
- OptionalPromise,
5
- ResolveUsersArgs,
6
- } from "@liveblocks/core";
7
- import { Promise_withResolvers } from "@liveblocks/core";
8
-
9
- import { createDevelopmentWarning } from "./warning";
10
-
11
- type ResolveUserOptionalPromise<U extends BaseUserMeta> = (
12
- args: ResolveUsersArgs
13
- ) => OptionalPromise<(U["info"] | undefined)[] | undefined>;
14
-
15
- /**
16
- * Batch calls to `resolveUsers` to one and only call.
17
- * It will avoid any performances issues and invocation timeouts on our customers' webhook handlers.
18
- *
19
- * This batch call will stack pending promises referring to `resolveUsers` in a map, resolve all users given in args at once
20
- * and then resolve pending promises all at once.
21
- */
22
- class BatchUsersResolver<U extends BaseUserMeta> {
23
- private isResolved: boolean;
24
- private markAsResolved: () => void;
25
- private resolvePromise: Promise<void>;
26
-
27
- private primeResolveUsersFn: ResolveUserOptionalPromise<U> | undefined;
28
- private usersById: Map<string, U["info"] | undefined>;
29
-
30
- private warnAsAlreadyResolved: () => void;
31
-
32
- constructor(resolveUsers: ResolveUserOptionalPromise<U> | undefined) {
33
- const { promise, resolve } = Promise_withResolvers<void>();
34
-
35
- this.isResolved = false;
36
- this.markAsResolved = resolve;
37
- this.resolvePromise = promise;
38
-
39
- this.primeResolveUsersFn = resolveUsers;
40
- this.usersById = new Map();
41
-
42
- this.warnAsAlreadyResolved = createDevelopmentWarning(
43
- true,
44
- "Batch users resolver promise already resolved. It can only resolve once."
45
- );
46
- }
47
-
48
- resolveUsers = async (
49
- args: ResolveUsersArgs
50
- ): Promise<(U["info"] | undefined)[] | undefined> => {
51
- if (this.isResolved) {
52
- this.warnAsAlreadyResolved();
53
- return undefined;
54
- }
55
-
56
- // Note: register all user Ids
57
- for (const userId of args.userIds) {
58
- this.usersById.set(userId, undefined);
59
- }
60
-
61
- // Note: waiting until the batch promise is resolved
62
- await this.resolvePromise;
63
-
64
- // Note: once the batch promise is resolved
65
- // we can return safely resolved users
66
- return args.userIds.map((userId) => this.usersById.get(userId));
67
- };
68
-
69
- async resolve(): Promise<void> {
70
- if (this.isResolved) {
71
- this.warnAsAlreadyResolved();
72
- return;
73
- }
74
-
75
- // Note: set an array of unique user ids
76
- const userIds = Array.from(this.usersById.keys());
77
- const users = await this.primeResolveUsersFn?.({ userIds });
78
-
79
- for (const [index, userId] of userIds.entries()) {
80
- const user = users?.[index];
81
- this.usersById.set(userId, user);
82
- }
83
-
84
- this.isResolved = true;
85
- this.markAsResolved();
86
- }
87
- }
88
-
89
- export type CreateBatchUsersResolverReturnType<U extends BaseUserMeta> = {
90
- resolveUsers: (
91
- args: ResolveUsersArgs
92
- ) => Promise<(U["info"] | undefined)[] | undefined>;
93
- resolve: () => Promise<void>;
94
- };
95
-
96
- export function createBatchUsersResolver<U extends BaseUserMeta = DU>({
97
- resolveUsers,
98
- callerName,
99
- }: {
100
- resolveUsers?: (
101
- args: ResolveUsersArgs
102
- ) => OptionalPromise<(U["info"] | undefined)[] | undefined>;
103
- callerName: string;
104
- }): CreateBatchUsersResolverReturnType<U> {
105
- const warnIfNoResolveUsers = createDevelopmentWarning(
106
- () => !resolveUsers,
107
- `Set "resolveUsers" option in "${callerName}" to specify users info`
108
- );
109
- const batchUsersResolver = new BatchUsersResolver(resolveUsers);
110
-
111
- const resolve = async (): Promise<void> => {
112
- warnIfNoResolveUsers();
113
- await batchUsersResolver.resolve();
114
- };
115
-
116
- return {
117
- resolveUsers: batchUsersResolver.resolveUsers,
118
- resolve,
119
- } as const;
120
- }