@headless-lms/core 0.3.0 → 0.5.0
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/dist/assets/events.d.ts +24 -18
- package/dist/assets/file-urls.d.ts +11 -7
- package/dist/assets/file-urls.js +14 -4
- package/dist/assets/file-urls.js.map +1 -1
- package/dist/assets/index.d.ts +5 -4
- package/dist/assets/index.js +4 -3
- package/dist/assets/jobs.d.ts +15 -0
- package/dist/assets/jobs.js +30 -0
- package/dist/assets/jobs.js.map +1 -0
- package/dist/assets/ports.d.ts +37 -17
- package/dist/assets/service.d.ts +30 -15
- package/dist/assets/service.js +229 -59
- package/dist/assets/service.js.map +1 -1
- package/dist/automations/actions.js +2 -63
- package/dist/automations/actions.js.map +1 -1
- package/dist/automations/catalog.d.ts +1 -2
- package/dist/automations/catalog.js +137 -65
- package/dist/automations/catalog.js.map +1 -1
- package/dist/automations/community-derivations.js +20 -20
- package/dist/automations/community-derivations.js.map +1 -1
- package/dist/automations/community-mail.d.ts +36 -20
- package/dist/automations/community-mail.js +32 -24
- package/dist/automations/community-mail.js.map +1 -1
- package/dist/automations/event-permissions.d.ts +12 -0
- package/dist/automations/event-permissions.js +38 -0
- package/dist/automations/event-permissions.js.map +1 -0
- package/dist/automations/events.d.ts +0 -10
- package/dist/automations/index.d.ts +12 -4
- package/dist/automations/index.js +9 -1
- package/dist/automations/notifications/applicable.d.ts +8 -0
- package/dist/automations/notifications/applicable.js +12 -0
- package/dist/automations/notifications/applicable.js.map +1 -0
- package/dist/automations/notifications/debounce.d.ts +25 -0
- package/dist/automations/notifications/debounce.js +93 -0
- package/dist/automations/notifications/debounce.js.map +1 -0
- package/dist/automations/notifications/defaults.d.ts +8 -0
- package/dist/automations/notifications/defaults.js +346 -0
- package/dist/automations/notifications/defaults.js.map +1 -0
- package/dist/automations/notifications/delivery.d.ts +28 -0
- package/dist/automations/notifications/delivery.js +88 -0
- package/dist/automations/notifications/delivery.js.map +1 -0
- package/dist/automations/notifications/processor.d.ts +64 -0
- package/dist/automations/notifications/processor.js +270 -0
- package/dist/automations/notifications/processor.js.map +1 -0
- package/dist/automations/notifications/rules/content.d.ts +6 -0
- package/dist/automations/notifications/rules/content.js +96 -0
- package/dist/automations/notifications/rules/content.js.map +1 -0
- package/dist/automations/notifications/rules/discussion.d.ts +7 -0
- package/dist/automations/notifications/rules/discussion.js +678 -0
- package/dist/automations/notifications/rules/discussion.js.map +1 -0
- package/dist/automations/notifications/rules/entitlements.d.ts +6 -0
- package/dist/automations/notifications/rules/entitlements.js +276 -0
- package/dist/automations/notifications/rules/entitlements.js.map +1 -0
- package/dist/automations/notifications/rules/events.d.ts +9 -0
- package/dist/automations/notifications/rules/events.js +27 -0
- package/dist/automations/notifications/rules/events.js.map +1 -0
- package/dist/automations/notifications/rules/index.d.ts +6 -0
- package/dist/automations/notifications/rules/index.js +15 -0
- package/dist/automations/notifications/rules/index.js.map +1 -0
- package/dist/automations/notifications/rules/organizations.d.ts +6 -0
- package/dist/automations/notifications/rules/organizations.js +35 -0
- package/dist/automations/notifications/rules/organizations.js.map +1 -0
- package/dist/automations/notifications/rules/recipients.d.ts +32 -0
- package/dist/automations/notifications/rules/recipients.js +98 -0
- package/dist/automations/notifications/rules/recipients.js.map +1 -0
- package/dist/automations/notifications/service.d.ts +29 -0
- package/dist/automations/notifications/service.js +45 -0
- package/dist/automations/notifications/service.js.map +1 -0
- package/dist/automations/notifications/types.d.ts +7 -0
- package/dist/automations/notifications/types.js +560 -0
- package/dist/automations/notifications/types.js.map +1 -0
- package/dist/automations/ports.d.ts +134 -21
- package/dist/automations/service.d.ts +30 -30
- package/dist/automations/service.js +111 -144
- package/dist/automations/service.js.map +1 -1
- package/dist/content/event-sight.d.ts +11 -0
- package/dist/content/event-sight.js +11 -0
- package/dist/content/event-sight.js.map +1 -0
- package/dist/content/events.d.ts +437 -46
- package/dist/content/events.js +45 -1
- package/dist/content/events.js.map +1 -1
- package/dist/content/index.d.ts +8 -4
- package/dist/content/index.js +5 -1
- package/dist/content/jobs.d.ts +16 -0
- package/dist/content/jobs.js +37 -0
- package/dist/content/jobs.js.map +1 -0
- package/dist/content/model.d.ts +2 -2
- package/dist/content/notification-categories.d.ts +37 -0
- package/dist/content/notification-categories.js +73 -0
- package/dist/content/notification-categories.js.map +1 -0
- package/dist/content/ports.d.ts +132 -68
- package/dist/content/service.d.ts +100 -68
- package/dist/content/service.js +703 -146
- package/dist/content/service.js.map +1 -1
- package/dist/content/titles.d.ts +25 -0
- package/dist/content/titles.js +82 -0
- package/dist/content/titles.js.map +1 -0
- package/dist/content/types.d.ts +2 -2
- package/dist/discussion/authors.d.ts +4 -2
- package/dist/discussion/authors.js +9 -5
- package/dist/discussion/authors.js.map +1 -1
- package/dist/discussion/chats.d.ts +63 -39
- package/dist/discussion/chats.js +455 -190
- package/dist/discussion/chats.js.map +1 -1
- package/dist/discussion/digests.d.ts +24 -0
- package/dist/discussion/digests.js +104 -0
- package/dist/discussion/digests.js.map +1 -0
- package/dist/discussion/events.d.ts +227 -23
- package/dist/discussion/events.js +114 -19
- package/dist/discussion/events.js.map +1 -1
- package/dist/discussion/index.d.ts +12 -4
- package/dist/discussion/index.js +9 -2
- package/dist/discussion/jobs.d.ts +16 -0
- package/dist/discussion/jobs.js +40 -0
- package/dist/discussion/jobs.js.map +1 -0
- package/dist/discussion/live.d.ts +106 -0
- package/dist/discussion/live.js +642 -0
- package/dist/discussion/live.js.map +1 -0
- package/dist/discussion/mentions.d.ts +11 -0
- package/dist/discussion/mentions.js +35 -0
- package/dist/discussion/mentions.js.map +1 -0
- package/dist/discussion/ports.d.ts +279 -114
- package/dist/discussion/ports.js.map +1 -1
- package/dist/discussion/posts.d.ts +60 -44
- package/dist/discussion/posts.js +453 -287
- package/dist/discussion/posts.js.map +1 -1
- package/dist/discussion/rules.d.ts +18 -0
- package/dist/discussion/rules.js +22 -0
- package/dist/discussion/rules.js.map +1 -0
- package/dist/discussion/service.d.ts +50 -35
- package/dist/discussion/service.js +333 -205
- package/dist/discussion/service.js.map +1 -1
- package/dist/discussion/subscriptions.d.ts +37 -0
- package/dist/discussion/subscriptions.js +115 -0
- package/dist/discussion/subscriptions.js.map +1 -0
- package/dist/discussion/titles.d.ts +28 -0
- package/dist/discussion/titles.js +98 -0
- package/dist/discussion/titles.js.map +1 -0
- package/dist/entitlements/events.d.ts +20 -20
- package/dist/entitlements/index.d.ts +5 -4
- package/dist/entitlements/index.js +3 -2
- package/dist/entitlements/jobs.d.ts +12 -0
- package/dist/entitlements/jobs.js +18 -0
- package/dist/entitlements/jobs.js.map +1 -0
- package/dist/entitlements/model.d.ts +2 -2
- package/dist/entitlements/ports.d.ts +68 -38
- package/dist/entitlements/rules.d.ts +3 -6
- package/dist/entitlements/rules.js +5 -10
- package/dist/entitlements/rules.js.map +1 -1
- package/dist/entitlements/service.d.ts +43 -28
- package/dist/entitlements/service.js +387 -160
- package/dist/entitlements/service.js.map +1 -1
- package/dist/identity/devices.d.ts +30 -0
- package/dist/identity/devices.js +109 -0
- package/dist/identity/devices.js.map +1 -0
- package/dist/identity/index.d.ts +6 -5
- package/dist/identity/index.js +4 -3
- package/dist/identity/model.d.ts +2 -2
- package/dist/identity/notification-settings.d.ts +28 -0
- package/dist/identity/notification-settings.js +114 -0
- package/dist/identity/notification-settings.js.map +1 -0
- package/dist/identity/ports.d.ts +80 -56
- package/dist/identity/service.d.ts +8 -7
- package/dist/identity/service.js +17 -13
- package/dist/identity/service.js.map +1 -1
- package/dist/integrations/ports.d.ts +9 -8
- package/dist/integrations/service.d.ts +9 -8
- package/dist/integrations/service.js +33 -16
- package/dist/integrations/service.js.map +1 -1
- package/dist/organizations/default-roles.d.ts +22 -0
- package/dist/organizations/default-roles.js +89 -0
- package/dist/organizations/default-roles.js.map +1 -0
- package/dist/organizations/effective-permissions.d.ts +45 -0
- package/dist/organizations/effective-permissions.js +113 -0
- package/dist/organizations/effective-permissions.js.map +1 -0
- package/dist/organizations/events.d.ts +320 -181
- package/dist/organizations/events.js +90 -18
- package/dist/organizations/events.js.map +1 -1
- package/dist/organizations/index.d.ts +15 -10
- package/dist/organizations/index.js +8 -3
- package/dist/organizations/members.d.ts +66 -8
- package/dist/organizations/members.js.map +1 -1
- package/dist/organizations/notification-catalog.d.ts +31 -0
- package/dist/organizations/notification-catalog.js +32 -0
- package/dist/organizations/notification-catalog.js.map +1 -0
- package/dist/organizations/notification-categories.d.ts +34 -0
- package/dist/organizations/notification-categories.js +66 -0
- package/dist/organizations/notification-categories.js.map +1 -0
- package/dist/organizations/org-roles.d.ts +33 -0
- package/dist/organizations/org-roles.js +49 -0
- package/dist/organizations/org-roles.js.map +1 -0
- package/dist/organizations/permissions.d.ts +311 -0
- package/dist/organizations/permissions.js +327 -0
- package/dist/organizations/permissions.js.map +1 -0
- package/dist/organizations/ports.d.ts +187 -65
- package/dist/organizations/service.d.ts +104 -35
- package/dist/organizations/service.js +1247 -323
- package/dist/organizations/service.js.map +1 -1
- package/dist/organizations/testing.d.ts +18 -0
- package/dist/organizations/testing.js +37 -0
- package/dist/organizations/testing.js.map +1 -0
- package/dist/organizations/types.d.ts +5 -16
- package/dist/progress/index.d.ts +2 -2
- package/dist/progress/ports.d.ts +11 -3
- package/dist/progress/service.d.ts +6 -5
- package/dist/progress/service.js +23 -8
- package/dist/progress/service.js.map +1 -1
- package/dist/reporting/admin/index.d.ts +6 -4
- package/dist/reporting/admin/model.d.ts +36 -3
- package/dist/reporting/admin/ports.d.ts +39 -14
- package/dist/reporting/admin/service.d.ts +32 -11
- package/dist/reporting/admin/service.js +102 -18
- package/dist/reporting/admin/service.js.map +1 -1
- package/dist/reporting/events/index.d.ts +27 -0
- package/dist/reporting/events/index.js +20 -0
- package/dist/reporting/events/index.js.map +1 -0
- package/dist/reporting/member/calendar.d.ts +11 -0
- package/dist/reporting/member/calendar.js +75 -0
- package/dist/reporting/member/calendar.js.map +1 -0
- package/dist/reporting/member/index.d.ts +3 -2
- package/dist/reporting/member/model.d.ts +80 -26
- package/dist/reporting/member/ports.d.ts +50 -28
- package/dist/reporting/member/service.d.ts +81 -35
- package/dist/reporting/member/service.js +438 -145
- package/dist/reporting/member/service.js.map +1 -1
- package/dist/shared/actor.d.ts +22 -3
- package/dist/shared/errors.d.ts +15 -2
- package/dist/shared/errors.js +21 -2
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/id.d.ts +7 -0
- package/dist/shared/id.js +8 -1
- package/dist/shared/id.js.map +1 -1
- package/dist/shared/logger.d.ts +2 -1
- package/dist/shared/logger.js +10 -1
- package/dist/shared/logger.js.map +1 -1
- package/dist/shared/mailer.d.ts +11 -1
- package/dist/shared/mailer.js +32 -5
- package/dist/shared/mailer.js.map +1 -1
- package/dist/shared/notification-categories.d.ts +16 -0
- package/dist/shared/notification-categories.js +94 -0
- package/dist/shared/notification-categories.js.map +1 -0
- package/dist/shared/notification-settings.d.ts +14 -0
- package/dist/shared/notification-settings.js +61 -0
- package/dist/shared/notification-settings.js.map +1 -0
- package/dist/shared/ports.d.ts +68 -2
- package/dist/shared/settings.d.ts +2 -1
- package/dist/shared/settings.js +1 -0
- package/dist/shared/settings.js.map +1 -1
- package/dist/types/automations.d.ts +8 -2
- package/dist/types/content.d.ts +2 -2
- package/dist/types/discussion.d.ts +4 -5
- package/dist/types/email-templates.d.ts +119 -6
- package/dist/types/entitlements.d.ts +2 -2
- package/dist/types/identity.d.ts +14 -17
- package/dist/types/index.d.ts +13 -12
- package/dist/types/index.js +2 -1
- package/dist/types/notifications.d.ts +157 -0
- package/dist/types/notifications.js +1 -0
- package/dist/types/organizations.d.ts +3 -2
- package/dist/types/ports.d.ts +8 -1
- package/dist/types/ports.js +7 -1
- package/dist/types/ports.js.map +1 -0
- package/dist/types/schemas/assets.d.ts +5 -5
- package/dist/types/schemas/assets.js +4 -3
- package/dist/types/schemas/assets.js.map +1 -1
- package/dist/types/schemas/automations.d.ts +1 -10
- package/dist/types/schemas/automations.js +1 -3
- package/dist/types/schemas/automations.js.map +1 -1
- package/dist/types/schemas/content.d.ts +596 -11
- package/dist/types/schemas/content.js +225 -9
- package/dist/types/schemas/content.js.map +1 -1
- package/dist/types/schemas/discussion.d.ts +362 -66
- package/dist/types/schemas/discussion.js +123 -15
- package/dist/types/schemas/discussion.js.map +1 -1
- package/dist/types/schemas/entitlements.d.ts +14 -1
- package/dist/types/schemas/entitlements.js +11 -4
- package/dist/types/schemas/entitlements.js.map +1 -1
- package/dist/types/schemas/identity.d.ts +1 -85
- package/dist/types/schemas/identity.js +1 -42
- package/dist/types/schemas/identity.js.map +1 -1
- package/dist/types/schemas/index.d.ts +8 -7
- package/dist/types/schemas/index.js +8 -7
- package/dist/types/schemas/notifications.d.ts +96 -0
- package/dist/types/schemas/notifications.js +57 -0
- package/dist/types/schemas/notifications.js.map +1 -0
- package/dist/types/schemas/organizations.d.ts +96 -60
- package/dist/types/schemas/organizations.js +78 -27
- package/dist/types/schemas/organizations.js.map +1 -1
- package/dist/types/schemas/progress.d.ts +0 -1
- package/dist/types/schemas/progress.js +0 -1
- package/dist/types/schemas/progress.js.map +1 -1
- package/package.json +1 -1
- package/dist/automations/community-notifications.d.ts +0 -16
- package/dist/automations/community-notifications.js +0 -84
- package/dist/automations/community-notifications.js.map +0 -1
- package/dist/automations/seeds.d.ts +0 -17
- package/dist/automations/seeds.js +0 -235
- package/dist/automations/seeds.js.map +0 -1
- package/dist/identity/notifications.d.ts +0 -27
- package/dist/identity/notifications.js +0 -101
- package/dist/identity/notifications.js.map +0 -1
- package/dist/organizations/access-scopes.d.ts +0 -19
- package/dist/organizations/access-scopes.js +0 -89
- package/dist/organizations/access-scopes.js.map +0 -1
- package/dist/organizations/roles.d.ts +0 -19
- package/dist/organizations/roles.js +0 -82
- package/dist/organizations/roles.js.map +0 -1
package/dist/discussion/chats.js
CHANGED
|
@@ -1,45 +1,76 @@
|
|
|
1
|
-
import { genId } from "../shared/id.js";
|
|
2
|
-
import { ConflictError, ForbiddenError, InvalidInputError, NotFoundError } from "../shared/errors.js";
|
|
3
1
|
import { noopLogger } from "../shared/logger.js";
|
|
4
|
-
import {
|
|
2
|
+
import { ConflictError, ForbiddenError, InvalidInputError, NotFoundError, UnavailableError } from "../shared/errors.js";
|
|
3
|
+
import { genId } from "../shared/id.js";
|
|
5
4
|
import { discussionEvents } from "./events.js";
|
|
6
|
-
import { authorView } from "./authors.js";
|
|
5
|
+
import { authorName, authorView } from "./authors.js";
|
|
6
|
+
import { keepAllowed, mentionTokens, mentionViews } from "./mentions.js";
|
|
7
|
+
import { isTeam, teamBadge } from "./rules.js";
|
|
8
|
+
import { activeSubscription } from "./subscriptions.js";
|
|
7
9
|
import "./ports.js";
|
|
10
|
+
import { ChatHub, TYPING, publishChange } from "./live.js";
|
|
8
11
|
//#region src/discussion/chats.ts
|
|
12
|
+
const TYPING_FOR_MS = 5e3;
|
|
13
|
+
const TYPING_REPEAT_MS = 2e3;
|
|
14
|
+
function eventMessage(message) {
|
|
15
|
+
const { clientId: _clientId, ...rest } = message;
|
|
16
|
+
return rest;
|
|
17
|
+
}
|
|
9
18
|
var ChatServiceImpl = class {
|
|
19
|
+
/** This process's live chat hub; null without a PubSub. */
|
|
20
|
+
hub;
|
|
10
21
|
repo;
|
|
11
22
|
access;
|
|
12
23
|
content;
|
|
13
24
|
authors;
|
|
14
25
|
profiles;
|
|
26
|
+
subscriptions;
|
|
27
|
+
mentions;
|
|
15
28
|
images;
|
|
16
29
|
fileUrls;
|
|
17
30
|
uow;
|
|
31
|
+
pubsub;
|
|
18
32
|
logger;
|
|
33
|
+
typed = /* @__PURE__ */ new Map();
|
|
19
34
|
constructor(params) {
|
|
20
35
|
this.repo = params.repo;
|
|
21
36
|
this.access = params.access;
|
|
22
37
|
this.content = params.content;
|
|
23
38
|
this.authors = params.authors;
|
|
24
39
|
this.profiles = params.profiles;
|
|
40
|
+
this.subscriptions = params.subscriptions;
|
|
41
|
+
this.mentions = params.mentions;
|
|
25
42
|
this.images = params.images;
|
|
26
43
|
this.fileUrls = params.fileUrls;
|
|
27
44
|
this.uow = params.uow;
|
|
45
|
+
this.pubsub = params.pubsub ?? null;
|
|
28
46
|
this.logger = params.logger ?? noopLogger;
|
|
47
|
+
this.hub = this.pubsub ? new ChatHub({
|
|
48
|
+
pubsub: this.pubsub,
|
|
49
|
+
reads: this,
|
|
50
|
+
logger: this.logger,
|
|
51
|
+
...params.live
|
|
52
|
+
}) : null;
|
|
29
53
|
}
|
|
30
|
-
async startDirect(
|
|
54
|
+
async startDirect(input, actor) {
|
|
55
|
+
const orgId = actor.orgId;
|
|
31
56
|
if (actor.id === input.orgUserId) throw new InvalidInputError("You cannot message yourself");
|
|
32
57
|
if (input.body === null && input.assetIds.length > 0) throw new InvalidInputError("An attachment needs a message");
|
|
33
58
|
const settings = await this.requireSettings(orgId, input.communityId);
|
|
34
|
-
await this.requireActor(
|
|
35
|
-
const
|
|
36
|
-
|
|
59
|
+
await this.requireActor(actor, input.communityId);
|
|
60
|
+
const targetTeam = await this.requirePerson(orgId, input.communityId, input.orgUserId);
|
|
61
|
+
const actorTeam = isTeam(actor.permissions, input.communityId);
|
|
62
|
+
this.assertMayMessage(settings, actorTeam, targetTeam);
|
|
37
63
|
const first = input.body === null ? null : {
|
|
38
64
|
body: input.body,
|
|
39
65
|
replyToId: null,
|
|
40
66
|
assetIds: input.assetIds
|
|
41
67
|
};
|
|
42
|
-
|
|
68
|
+
await this.requireOwnedImages(orgId, actor.id, input.assetIds);
|
|
69
|
+
const teams = {
|
|
70
|
+
asked: /* @__PURE__ */ new Set([input.orgUserId]),
|
|
71
|
+
team: /* @__PURE__ */ new Map([[input.orgUserId, targetTeam]])
|
|
72
|
+
};
|
|
73
|
+
const saved = await this.uow.run(async ({ chats, mentions, outbox }) => {
|
|
43
74
|
await chats.lockDirectPair(orgId, input.communityId, actor.id, input.orgUserId);
|
|
44
75
|
const existing = await chats.findDirect(orgId, input.communityId, actor.id, input.orgUserId);
|
|
45
76
|
if (existing) {
|
|
@@ -49,22 +80,21 @@ var ChatServiceImpl = class {
|
|
|
49
80
|
...user,
|
|
50
81
|
leftAt: null
|
|
51
82
|
} : user);
|
|
52
|
-
if (first) await this.assertMaySend(chats, orgId, actor, existing, present, settings, first);
|
|
83
|
+
if (first) await this.assertMaySend(chats, orgId, actor, existing, present, settings, first, teams);
|
|
53
84
|
if (rejoin) await chats.join(orgId, this.memberRow(orgId, existing.communityId, existing.id, actor.id, /* @__PURE__ */ new Date()));
|
|
54
|
-
const written = first ? await this.writeMessage(chats, orgId, actor, existing, first) : null;
|
|
85
|
+
const written = first ? await this.writeMessage(chats, mentions, orgId, actor, existing, present, first) : null;
|
|
55
86
|
const chat = written?.chat ?? existing;
|
|
56
87
|
const events = [];
|
|
57
88
|
if (rejoin || written?.opened) events.push(this.updatedEvent(orgId, actor, chat, present));
|
|
58
|
-
if (written) events.push(this.sentEvent(orgId, actor, written.chat, written.message));
|
|
89
|
+
if (written) events.push(this.sentEvent(orgId, actor, written.chat, written.message, written.mentioned));
|
|
59
90
|
if (events.length > 0) await outbox.append(events);
|
|
60
91
|
return {
|
|
61
92
|
chat,
|
|
62
93
|
created: false
|
|
63
94
|
};
|
|
64
95
|
}
|
|
65
|
-
await this.requireOwnedImages(orgId, actor.id, input.assetIds);
|
|
66
96
|
if (await chats.isBlocked(orgId, input.communityId, actor.id, input.orgUserId)) throw new ForbiddenError("You cannot message this person");
|
|
67
|
-
const request = settings.firstMessageIsRequest && !
|
|
97
|
+
const request = settings.firstMessageIsRequest && !actorTeam && !targetTeam;
|
|
68
98
|
if (request && !first) throw new InvalidInputError("A message request needs a message");
|
|
69
99
|
const now = /* @__PURE__ */ new Date();
|
|
70
100
|
const chat = await chats.insertChat(orgId, {
|
|
@@ -83,8 +113,8 @@ var ChatServiceImpl = class {
|
|
|
83
113
|
for (const user of users) await chats.join(orgId, user);
|
|
84
114
|
const events = [this.createdEvent(orgId, actor, chat, users)];
|
|
85
115
|
if (first) {
|
|
86
|
-
const written = await this.writeMessage(chats, orgId, actor, chat, first);
|
|
87
|
-
events.push(this.sentEvent(orgId, actor, written.chat, written.message));
|
|
116
|
+
const written = await this.writeMessage(chats, mentions, orgId, actor, chat, users, first);
|
|
117
|
+
events.push(this.sentEvent(orgId, actor, written.chat, written.message, written.mentioned));
|
|
88
118
|
}
|
|
89
119
|
await outbox.append(events);
|
|
90
120
|
return {
|
|
@@ -106,16 +136,18 @@ var ChatServiceImpl = class {
|
|
|
106
136
|
});
|
|
107
137
|
return saved.chat;
|
|
108
138
|
}
|
|
109
|
-
async startGroup(
|
|
139
|
+
async startGroup(input, actor) {
|
|
140
|
+
const orgId = actor.orgId;
|
|
110
141
|
const settings = await this.requireSettings(orgId, input.communityId);
|
|
111
142
|
this.assertGroup(settings);
|
|
112
|
-
await this.requireActor(
|
|
143
|
+
await this.requireActor(actor, input.communityId);
|
|
113
144
|
const others = [...new Set(input.orgUserIds.filter((id) => id !== actor.id))].sort();
|
|
114
145
|
if (others.length < 2) throw new InvalidInputError("A group chat needs three people");
|
|
115
146
|
if (others.length + 1 > settings.groupSize) throw new ConflictError("This group is full");
|
|
147
|
+
const actorTeam = isTeam(actor.permissions, input.communityId);
|
|
116
148
|
for (const orgUserId of others) {
|
|
117
|
-
const
|
|
118
|
-
this.assertMayMessage(settings,
|
|
149
|
+
const targetTeam = await this.requirePerson(orgId, input.communityId, orgUserId);
|
|
150
|
+
this.assertMayMessage(settings, actorTeam, targetTeam);
|
|
119
151
|
}
|
|
120
152
|
const saved = await this.uow.run(async ({ chats, outbox }) => {
|
|
121
153
|
if (await chats.anyBlockedAmong(orgId, input.communityId, [actor.id, ...others])) throw new ForbiddenError("You cannot message this person");
|
|
@@ -146,15 +178,16 @@ var ChatServiceImpl = class {
|
|
|
146
178
|
});
|
|
147
179
|
return saved;
|
|
148
180
|
}
|
|
149
|
-
async addParticipant(
|
|
181
|
+
async addParticipant(chatId, orgUserId, actor) {
|
|
182
|
+
const orgId = actor.orgId;
|
|
150
183
|
const chat = await this.mustChat(this.repo, orgId, chatId);
|
|
151
|
-
await this.requireActor(
|
|
184
|
+
await this.requireActor(actor, chat.communityId);
|
|
152
185
|
await this.requireActive(this.repo, orgId, chat.id, actor.id);
|
|
153
186
|
if (chat.kind !== "group") throw new ConflictError("This chat is not a group");
|
|
154
187
|
const settings = await this.requireSettings(orgId, chat.communityId);
|
|
155
188
|
this.assertGroup(settings);
|
|
156
|
-
const
|
|
157
|
-
this.assertMayMessage(settings, actor,
|
|
189
|
+
const targetTeam = await this.requirePerson(orgId, chat.communityId, orgUserId);
|
|
190
|
+
this.assertMayMessage(settings, isTeam(actor.permissions, chat.communityId), targetTeam);
|
|
158
191
|
const saved = await this.uow.run(async ({ chats, outbox }) => {
|
|
159
192
|
const current = await this.mustChat(chats, orgId, chatId);
|
|
160
193
|
const users = await chats.participants(orgId, chatId);
|
|
@@ -177,9 +210,10 @@ var ChatServiceImpl = class {
|
|
|
177
210
|
});
|
|
178
211
|
return saved;
|
|
179
212
|
}
|
|
180
|
-
async leave(
|
|
213
|
+
async leave(chatId, actor) {
|
|
214
|
+
const orgId = actor.orgId;
|
|
181
215
|
const chat = await this.mustChat(this.repo, orgId, chatId);
|
|
182
|
-
await this.requireActor(
|
|
216
|
+
await this.requireActor(actor, chat.communityId);
|
|
183
217
|
await this.requireActive(this.repo, orgId, chat.id, actor.id);
|
|
184
218
|
const saved = await this.uow.run(async ({ chats, outbox }) => {
|
|
185
219
|
const current = await this.mustChat(chats, orgId, chatId);
|
|
@@ -201,9 +235,10 @@ var ChatServiceImpl = class {
|
|
|
201
235
|
});
|
|
202
236
|
return saved;
|
|
203
237
|
}
|
|
204
|
-
async accept(
|
|
238
|
+
async accept(chatId, actor) {
|
|
239
|
+
const orgId = actor.orgId;
|
|
205
240
|
const chat = await this.mustChat(this.repo, orgId, chatId);
|
|
206
|
-
await this.requireActor(
|
|
241
|
+
await this.requireActor(actor, chat.communityId);
|
|
207
242
|
await this.requireActive(this.repo, orgId, chat.id, actor.id);
|
|
208
243
|
if (chat.createdBy === actor.id) throw new ForbiddenError("You cannot accept this request");
|
|
209
244
|
if (chat.kind !== "direct" || chat.status !== "pending") throw new ConflictError("This chat is not a request");
|
|
@@ -222,60 +257,119 @@ var ChatServiceImpl = class {
|
|
|
222
257
|
});
|
|
223
258
|
return saved;
|
|
224
259
|
}
|
|
225
|
-
async send(
|
|
260
|
+
async send(chatId, input, actor) {
|
|
261
|
+
const orgId = actor.orgId;
|
|
226
262
|
const preview = await this.mustChat(this.repo, orgId, chatId);
|
|
227
|
-
await this.requireActor(
|
|
263
|
+
await this.requireActor(actor, preview.communityId);
|
|
228
264
|
const settings = await this.requireSettings(orgId, preview.communityId);
|
|
229
265
|
if (!settings.enabled) throw new ForbiddenError("Direct messages are off");
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
266
|
+
const clientId = input.clientId;
|
|
267
|
+
const before = await this.repo.participants(orgId, chatId);
|
|
268
|
+
const teams = await this.teamsOf(orgId, preview.communityId, before.map((user) => user.orgUserId).filter((orgUserId) => orgUserId !== actor.id));
|
|
269
|
+
await this.requireOwnedImages(orgId, actor.id, input.assetIds);
|
|
270
|
+
let saved;
|
|
271
|
+
try {
|
|
272
|
+
saved = await this.uow.run(async ({ chats, mentions, outbox }) => {
|
|
273
|
+
const chat = await this.mustChat(chats, orgId, chatId);
|
|
274
|
+
if (clientId) {
|
|
275
|
+
const stored = await chats.findByClientId(orgId, chatId, actor.id, clientId);
|
|
276
|
+
if (stored) return {
|
|
277
|
+
message: stored,
|
|
278
|
+
repeat: true
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const users = await chats.participants(orgId, chatId);
|
|
282
|
+
await this.assertMaySend(chats, orgId, actor, chat, users, settings, input, teams);
|
|
283
|
+
const written = await this.writeMessage(chats, mentions, orgId, actor, chat, users, input);
|
|
284
|
+
const events = [];
|
|
285
|
+
if (written.opened) events.push(this.updatedEvent(orgId, actor, written.chat, users));
|
|
286
|
+
events.push(this.sentEvent(orgId, actor, written.chat, written.message, written.mentioned));
|
|
287
|
+
await outbox.append(events);
|
|
288
|
+
return {
|
|
289
|
+
message: written.message,
|
|
290
|
+
repeat: false
|
|
291
|
+
};
|
|
292
|
+
});
|
|
293
|
+
} catch (error) {
|
|
294
|
+
const stored = clientId && error instanceof ConflictError ? await this.repo.findByClientId(orgId, chatId, actor.id, clientId) : null;
|
|
295
|
+
if (!stored) throw error;
|
|
296
|
+
saved = {
|
|
297
|
+
message: stored,
|
|
298
|
+
repeat: true
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
if (saved.repeat) this.logger.debug("message send repeated", {
|
|
302
|
+
orgId,
|
|
303
|
+
chatId,
|
|
304
|
+
messageId: saved.message.id
|
|
240
305
|
});
|
|
241
|
-
this.logger.info("message sent", {
|
|
306
|
+
else this.logger.info("message sent", {
|
|
242
307
|
orgId,
|
|
243
308
|
chatId,
|
|
244
|
-
messageId: saved.id,
|
|
309
|
+
messageId: saved.message.id,
|
|
245
310
|
actorId: actor.id
|
|
246
311
|
});
|
|
247
|
-
return this.viewOf(
|
|
312
|
+
return this.viewOf(actor, saved.message);
|
|
248
313
|
}
|
|
249
|
-
async editMessage(
|
|
314
|
+
async editMessage(messageId, body, actor) {
|
|
315
|
+
const orgId = actor.orgId;
|
|
250
316
|
const message = await this.mustMessage(this.repo, orgId, messageId);
|
|
251
|
-
await this.requireWritableChat(
|
|
317
|
+
await this.requireWritableChat(actor, message.chatId);
|
|
252
318
|
if (message.orgUserId !== actor.id) throw new ForbiddenError("You can change only your own message");
|
|
253
319
|
if (message.deletedAt) throw new ConflictError("This message was deleted");
|
|
254
|
-
if (message.body === body) return this.viewOf(
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
320
|
+
if (message.body === body) return this.viewOf(actor, message);
|
|
321
|
+
const now = /* @__PURE__ */ new Date();
|
|
322
|
+
const saved = await this.uow.run(async ({ chats, mentions, outbox }) => {
|
|
323
|
+
const next = await chats.updateMessage(orgId, messageId, {
|
|
324
|
+
body,
|
|
325
|
+
editedAt: now
|
|
326
|
+
});
|
|
327
|
+
if (!next) throw new NotFoundError("Message", messageId);
|
|
328
|
+
const chat = await this.mustChat(chats, orgId, next.chatId);
|
|
329
|
+
const users = await chats.participants(orgId, next.chatId);
|
|
330
|
+
const candidates = this.mentionable(body, users);
|
|
331
|
+
const added = await mentions.sync(orgId, "message", messageId, candidates, now);
|
|
332
|
+
await outbox.append([discussionEvents.messageUpdated.make({
|
|
333
|
+
orgId,
|
|
334
|
+
data: {
|
|
335
|
+
...eventMessage(next),
|
|
336
|
+
mentionedOrgUserIds: keepAllowed(candidates, added)
|
|
337
|
+
},
|
|
338
|
+
metadata: {
|
|
339
|
+
...this.meta(actor),
|
|
340
|
+
chatStatus: chat.status
|
|
341
|
+
}
|
|
342
|
+
})]);
|
|
343
|
+
return next;
|
|
258
344
|
});
|
|
259
|
-
if (!saved) throw new NotFoundError("Message", messageId);
|
|
260
345
|
this.logger.info("message edited", {
|
|
261
346
|
orgId,
|
|
262
347
|
messageId,
|
|
263
348
|
actorId: actor.id
|
|
264
349
|
});
|
|
265
|
-
return this.viewOf(
|
|
350
|
+
return this.viewOf(actor, saved);
|
|
266
351
|
}
|
|
267
|
-
async deleteMessage(
|
|
352
|
+
async deleteMessage(messageId, actor) {
|
|
353
|
+
const orgId = actor.orgId;
|
|
268
354
|
const message = await this.mustMessage(this.repo, orgId, messageId);
|
|
269
|
-
await this.requireActor(
|
|
270
|
-
if (!
|
|
271
|
-
if (message.orgUserId !== actor.id)
|
|
272
|
-
|
|
355
|
+
await this.requireActor(actor, message.communityId);
|
|
356
|
+
if (!actor.permissions.has("communities:moderate", message.communityId)) {
|
|
357
|
+
if (message.orgUserId !== actor.id) {
|
|
358
|
+
this.logger.warn("message delete refused — not the sender or a moderator", {
|
|
359
|
+
orgId,
|
|
360
|
+
messageId,
|
|
361
|
+
actorId: actor.id
|
|
362
|
+
});
|
|
363
|
+
throw new ForbiddenError("You cannot delete this message");
|
|
364
|
+
}
|
|
365
|
+
await this.requireWritableChat(actor, message.chatId);
|
|
273
366
|
}
|
|
274
367
|
if (message.deletedAt) throw new ConflictError("This message was deleted");
|
|
275
368
|
const now = /* @__PURE__ */ new Date();
|
|
276
|
-
const saved = await this.uow.run(async ({ chats, outbox }) => {
|
|
369
|
+
const saved = await this.uow.run(async ({ chats, mentions, outbox }) => {
|
|
277
370
|
const next = await chats.updateMessage(orgId, messageId, { deletedAt: now });
|
|
278
371
|
if (!next) throw new NotFoundError("Message", messageId);
|
|
372
|
+
await mentions.removeItem(orgId, "message", messageId);
|
|
279
373
|
await chats.resolveFlags(orgId, messageId, {
|
|
280
374
|
resolvedAt: now,
|
|
281
375
|
resolvedBy: actor.id,
|
|
@@ -283,7 +377,7 @@ var ChatServiceImpl = class {
|
|
|
283
377
|
});
|
|
284
378
|
await outbox.append([discussionEvents.messageDeleted.make({
|
|
285
379
|
orgId,
|
|
286
|
-
data: next,
|
|
380
|
+
data: eventMessage(next),
|
|
287
381
|
metadata: this.meta(actor)
|
|
288
382
|
})]);
|
|
289
383
|
return next;
|
|
@@ -293,55 +387,78 @@ var ChatServiceImpl = class {
|
|
|
293
387
|
messageId,
|
|
294
388
|
actorId: actor.id
|
|
295
389
|
});
|
|
296
|
-
return this.viewOf(
|
|
390
|
+
return this.viewOf(actor, saved);
|
|
297
391
|
}
|
|
298
|
-
async setReaction(
|
|
392
|
+
async setReaction(messageId, emoji, actor) {
|
|
393
|
+
const orgId = actor.orgId;
|
|
299
394
|
const message = await this.mustMessage(this.repo, orgId, messageId);
|
|
300
|
-
await this.requireActor(
|
|
395
|
+
await this.requireActor(actor, message.communityId);
|
|
301
396
|
await this.requireActive(this.repo, orgId, message.chatId, actor.id);
|
|
302
|
-
await this.
|
|
397
|
+
await this.uow.run(async ({ chats, outbox }) => {
|
|
398
|
+
const change = await chats.setReaction(orgId, message.communityId, messageId, actor.id, emoji);
|
|
399
|
+
if (!change.changed) return;
|
|
400
|
+
const where = {
|
|
401
|
+
messageId,
|
|
402
|
+
chatId: message.chatId,
|
|
403
|
+
communityId: message.communityId,
|
|
404
|
+
orgUserId: actor.id
|
|
405
|
+
};
|
|
406
|
+
const events = [];
|
|
407
|
+
if (change.inserted && emoji !== null) events.push(discussionEvents.messageReactionAdded.make({
|
|
408
|
+
orgId,
|
|
409
|
+
data: {
|
|
410
|
+
...where,
|
|
411
|
+
reaction: emoji
|
|
412
|
+
},
|
|
413
|
+
metadata: this.meta(actor)
|
|
414
|
+
}));
|
|
415
|
+
events.push(discussionEvents.messageReactionsChanged.make({
|
|
416
|
+
orgId,
|
|
417
|
+
data: {
|
|
418
|
+
...where,
|
|
419
|
+
reaction: emoji
|
|
420
|
+
},
|
|
421
|
+
metadata: this.meta(actor)
|
|
422
|
+
}));
|
|
423
|
+
await outbox.append(events);
|
|
424
|
+
});
|
|
303
425
|
this.logger.debug("message reaction set", {
|
|
304
426
|
orgId,
|
|
305
427
|
messageId
|
|
306
428
|
});
|
|
307
429
|
return (await this.repo.reactionsOf(orgId, [messageId], actor.id))[messageId] ?? { reactions: {} };
|
|
308
430
|
}
|
|
309
|
-
async markRead(
|
|
431
|
+
async markRead(chatId, messageId, actor) {
|
|
432
|
+
const orgId = actor.orgId;
|
|
310
433
|
const chat = await this.mustChat(this.repo, orgId, chatId);
|
|
311
|
-
await this.requireActor(
|
|
434
|
+
await this.requireActor(actor, chat.communityId);
|
|
312
435
|
await this.requireActive(this.repo, orgId, chat.id, actor.id);
|
|
313
436
|
if ((await this.mustMessage(this.repo, orgId, messageId)).chatId !== chatId) throw new NotFoundError("Message", messageId);
|
|
314
|
-
await this.
|
|
315
|
-
|
|
437
|
+
const moved = await this.uow.run(async ({ chats, outbox }) => {
|
|
438
|
+
if (!await chats.setRead(orgId, chatId, actor.id, messageId)) return false;
|
|
439
|
+
await outbox.append([discussionEvents.chatRead.make({
|
|
440
|
+
orgId,
|
|
441
|
+
data: {
|
|
442
|
+
chatId,
|
|
443
|
+
communityId: chat.communityId,
|
|
444
|
+
orgUserId: actor.id,
|
|
445
|
+
lastReadMessageId: messageId
|
|
446
|
+
},
|
|
447
|
+
metadata: this.meta(actor)
|
|
448
|
+
})]);
|
|
449
|
+
return true;
|
|
450
|
+
});
|
|
451
|
+
this.logger.debug(moved ? "chat marked read" : "chat read mark kept", {
|
|
316
452
|
orgId,
|
|
317
453
|
chatId,
|
|
318
454
|
messageId
|
|
319
455
|
});
|
|
320
456
|
}
|
|
321
|
-
async
|
|
322
|
-
const
|
|
323
|
-
await this.requireActor(orgId, actor, chat.communityId);
|
|
324
|
-
await this.requireActive(this.repo, orgId, chat.id, actor.id);
|
|
325
|
-
await this.repo.setMuted(orgId, chatId, actor.id, true, until ?? null);
|
|
326
|
-
this.logger.debug("chat muted", {
|
|
327
|
-
orgId,
|
|
328
|
-
chatId
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
async unmute(orgId, actor, chatId) {
|
|
332
|
-
const chat = await this.mustChat(this.repo, orgId, chatId);
|
|
333
|
-
await this.requireActor(orgId, actor, chat.communityId);
|
|
334
|
-
await this.requireActive(this.repo, orgId, chat.id, actor.id);
|
|
335
|
-
await this.repo.setMuted(orgId, chatId, actor.id, false, null);
|
|
336
|
-
this.logger.debug("chat unmuted", {
|
|
337
|
-
orgId,
|
|
338
|
-
chatId
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
async block(orgId, actor, communityId, orgUserId) {
|
|
457
|
+
async block(communityId, orgUserId, actor) {
|
|
458
|
+
const orgId = actor.orgId;
|
|
342
459
|
if (actor.id === orgUserId) throw new InvalidInputError("You cannot block yourself");
|
|
343
460
|
await this.requireSettings(orgId, communityId);
|
|
344
|
-
await this.requireActor(
|
|
461
|
+
await this.requireActor(actor, communityId);
|
|
345
462
|
await this.requirePerson(orgId, communityId, orgUserId);
|
|
346
463
|
await this.uow.run(async ({ chats }) => {
|
|
347
464
|
await chats.lockDirectPair(orgId, communityId, actor.id, orgUserId);
|
|
@@ -353,10 +470,11 @@ var ChatServiceImpl = class {
|
|
|
353
470
|
actorId: actor.id
|
|
354
471
|
});
|
|
355
472
|
}
|
|
356
|
-
async unblock(
|
|
473
|
+
async unblock(communityId, orgUserId, actor) {
|
|
474
|
+
const orgId = actor.orgId;
|
|
357
475
|
if (actor.id === orgUserId) throw new InvalidInputError("You cannot block yourself");
|
|
358
476
|
await this.requireSettings(orgId, communityId);
|
|
359
|
-
await this.requireActor(
|
|
477
|
+
await this.requireActor(actor, communityId);
|
|
360
478
|
await this.uow.run(async ({ chats }) => {
|
|
361
479
|
await chats.lockDirectPair(orgId, communityId, actor.id, orgUserId);
|
|
362
480
|
await chats.unblock(orgId, communityId, actor.id, orgUserId);
|
|
@@ -367,9 +485,10 @@ var ChatServiceImpl = class {
|
|
|
367
485
|
actorId: actor.id
|
|
368
486
|
});
|
|
369
487
|
}
|
|
370
|
-
async flagMessage(
|
|
488
|
+
async flagMessage(messageId, reason, actor) {
|
|
489
|
+
const orgId = actor.orgId;
|
|
371
490
|
const message = await this.mustMessage(this.repo, orgId, messageId);
|
|
372
|
-
await this.requireActor(
|
|
491
|
+
await this.requireActor(actor, message.communityId);
|
|
373
492
|
await this.requireActive(this.repo, orgId, message.chatId, actor.id);
|
|
374
493
|
const now = /* @__PURE__ */ new Date();
|
|
375
494
|
const saved = await this.uow.run(async ({ chats, outbox }) => {
|
|
@@ -405,11 +524,12 @@ var ChatServiceImpl = class {
|
|
|
405
524
|
});
|
|
406
525
|
return saved;
|
|
407
526
|
}
|
|
408
|
-
async resolveMessageFlags(
|
|
409
|
-
|
|
527
|
+
async resolveMessageFlags(messageId, resolution, communityId, actor) {
|
|
528
|
+
const orgId = actor.orgId;
|
|
410
529
|
const message = await this.mustMessage(this.repo, orgId, messageId);
|
|
411
|
-
if (communityId !==
|
|
412
|
-
|
|
530
|
+
if (communityId !== null && message.communityId !== communityId) throw new NotFoundError("Message", messageId);
|
|
531
|
+
this.requireModerator(actor, message.communityId, "resolve message flags");
|
|
532
|
+
await this.requireActor(actor, message.communityId);
|
|
413
533
|
await this.repo.resolveFlags(orgId, messageId, {
|
|
414
534
|
resolvedAt: /* @__PURE__ */ new Date(),
|
|
415
535
|
resolvedBy: actor.id,
|
|
@@ -423,28 +543,18 @@ var ChatServiceImpl = class {
|
|
|
423
543
|
actorId: actor.id
|
|
424
544
|
});
|
|
425
545
|
}
|
|
426
|
-
async lock(
|
|
427
|
-
return this.setLock(
|
|
546
|
+
async lock(chatId, communityId, actor) {
|
|
547
|
+
return this.setLock(actor, chatId, true, communityId);
|
|
428
548
|
}
|
|
429
|
-
async unlock(
|
|
430
|
-
return this.setLock(
|
|
549
|
+
async unlock(chatId, communityId, actor) {
|
|
550
|
+
return this.setLock(actor, chatId, false, communityId);
|
|
431
551
|
}
|
|
432
|
-
async listChats(
|
|
552
|
+
async listChats(communityId, query, actor) {
|
|
553
|
+
const orgId = actor.orgId;
|
|
433
554
|
await this.requireSettings(orgId, communityId);
|
|
434
|
-
await this.requireActor(
|
|
435
|
-
const now = /* @__PURE__ */ new Date();
|
|
555
|
+
await this.requireActor(actor, communityId);
|
|
436
556
|
const page = await this.repo.listForUser(orgId, communityId, actor.id, query);
|
|
437
|
-
const items = page.rows
|
|
438
|
-
...item,
|
|
439
|
-
...item.muted && item.mutedUntil !== null && item.mutedUntil <= now ? {
|
|
440
|
-
muted: false,
|
|
441
|
-
mutedUntil: null
|
|
442
|
-
} : {},
|
|
443
|
-
lastMessage: item.lastMessage && item.lastMessage.deletedAt !== null && !isStaffRole(actor.role) ? {
|
|
444
|
-
...item.lastMessage,
|
|
445
|
-
body: ""
|
|
446
|
-
} : item.lastMessage
|
|
447
|
-
}));
|
|
557
|
+
const items = await this.listItems(actor, communityId, page.rows);
|
|
448
558
|
this.logger.debug("chats listed", {
|
|
449
559
|
orgId,
|
|
450
560
|
communityId,
|
|
@@ -455,9 +565,100 @@ var ChatServiceImpl = class {
|
|
|
455
565
|
rows: items
|
|
456
566
|
};
|
|
457
567
|
}
|
|
458
|
-
async
|
|
568
|
+
async chat(chatId, actor) {
|
|
569
|
+
const orgId = actor.orgId;
|
|
459
570
|
const chat = await this.mustChat(this.repo, orgId, chatId);
|
|
460
|
-
await this.requireActor(
|
|
571
|
+
await this.requireActor(actor, chat.communityId);
|
|
572
|
+
await this.requireActive(this.repo, orgId, chat.id, actor.id);
|
|
573
|
+
const row = await this.repo.inboxRow(orgId, chatId, actor.id);
|
|
574
|
+
if (!row) throw new NotFoundError("Chat", chatId);
|
|
575
|
+
const [item] = await this.listItems(actor, chat.communityId, [row]);
|
|
576
|
+
this.logger.debug("chat read", {
|
|
577
|
+
orgId,
|
|
578
|
+
chatId
|
|
579
|
+
});
|
|
580
|
+
return item;
|
|
581
|
+
}
|
|
582
|
+
async directChat(communityId, orgUserId, actor) {
|
|
583
|
+
const orgId = actor.orgId;
|
|
584
|
+
await this.requireSettings(orgId, communityId);
|
|
585
|
+
await this.requireActor(actor, communityId);
|
|
586
|
+
const chat = orgUserId === actor.id ? null : await this.repo.findDirect(orgId, communityId, actor.id, orgUserId);
|
|
587
|
+
const row = chat ? await this.repo.inboxRow(orgId, chat.id, actor.id) : null;
|
|
588
|
+
if (!row) throw new NotFoundError("Chat", orgUserId);
|
|
589
|
+
const [item] = await this.listItems(actor, communityId, [row]);
|
|
590
|
+
this.logger.debug("direct chat read", {
|
|
591
|
+
orgId,
|
|
592
|
+
communityId,
|
|
593
|
+
chatId: row.chat.id
|
|
594
|
+
});
|
|
595
|
+
return item;
|
|
596
|
+
}
|
|
597
|
+
async unreadSummary(communityId, actor) {
|
|
598
|
+
const orgId = actor.orgId;
|
|
599
|
+
await this.requireSettings(orgId, communityId);
|
|
600
|
+
await this.requireActor(actor, communityId);
|
|
601
|
+
return this.repo.unreadSummary(orgId, communityId, actor.id, /* @__PURE__ */ new Date());
|
|
602
|
+
}
|
|
603
|
+
async typing(chatId, actor) {
|
|
604
|
+
const orgId = actor.orgId;
|
|
605
|
+
const chat = await this.mustChat(this.repo, orgId, chatId);
|
|
606
|
+
await this.requireActor(actor, chat.communityId);
|
|
607
|
+
await this.requireActive(this.repo, orgId, chat.id, actor.id);
|
|
608
|
+
if (chat.lockedAt || chat.status === "closed") throw new ForbiddenError("You cannot send in this chat");
|
|
609
|
+
if (!this.pubsub) return;
|
|
610
|
+
const now = Date.now();
|
|
611
|
+
const key = `${orgId}:${chatId}:${actor.id}`;
|
|
612
|
+
const last = this.typed.get(key);
|
|
613
|
+
if (last !== void 0 && now - last < TYPING_REPEAT_MS) return;
|
|
614
|
+
this.typed.set(key, now);
|
|
615
|
+
for (const [seen, at] of this.typed) if (now - at >= TYPING_REPEAT_MS) this.typed.delete(seen);
|
|
616
|
+
await publishChange(this.pubsub, {
|
|
617
|
+
event: TYPING,
|
|
618
|
+
orgId,
|
|
619
|
+
communityId: chat.communityId,
|
|
620
|
+
chatId,
|
|
621
|
+
orgUserId: actor.id,
|
|
622
|
+
until: new Date(now + TYPING_FOR_MS).toISOString()
|
|
623
|
+
}, this.logger);
|
|
624
|
+
}
|
|
625
|
+
async watch(communityId, actor) {
|
|
626
|
+
if (!this.hub) throw new UnavailableError("Live chat is not available");
|
|
627
|
+
await this.requireSettings(actor.orgId, communityId);
|
|
628
|
+
await this.requireActor(actor, communityId);
|
|
629
|
+
return this.hub.open({
|
|
630
|
+
communityId,
|
|
631
|
+
actor
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
async messageViews(orgId, messageIds) {
|
|
635
|
+
const messages = await this.repo.messagesByIds(orgId, [...new Set(messageIds)]);
|
|
636
|
+
return (await this.withSenders(orgId, null, messages)).map(({ viewerReaction: _viewerReaction, ...view }) => view);
|
|
637
|
+
}
|
|
638
|
+
activeParticipants(orgId, chatId) {
|
|
639
|
+
return this.repo.activeParticipants(orgId, chatId);
|
|
640
|
+
}
|
|
641
|
+
async messageReactions(orgId, messageId) {
|
|
642
|
+
return (await this.repo.reactionsOf(orgId, [messageId], ""))[messageId]?.reactions ?? {};
|
|
643
|
+
}
|
|
644
|
+
async liveChat(orgId, chatId) {
|
|
645
|
+
const chat = await this.repo.findChat(orgId, chatId);
|
|
646
|
+
if (!chat) return null;
|
|
647
|
+
const participantIds = await this.repo.activeParticipants(orgId, chatId);
|
|
648
|
+
const profiles = await this.participantsOf(orgId, chat.communityId, participantIds);
|
|
649
|
+
return {
|
|
650
|
+
chat,
|
|
651
|
+
participantIds,
|
|
652
|
+
participants: participantIds.flatMap((id) => profiles.get(id) ?? [])
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
memberStates(orgId, communityId, orgUserIds) {
|
|
656
|
+
return this.repo.memberStates(orgId, communityId, orgUserIds, /* @__PURE__ */ new Date());
|
|
657
|
+
}
|
|
658
|
+
async messages(chatId, query, actor) {
|
|
659
|
+
const orgId = actor.orgId;
|
|
660
|
+
const chat = await this.mustChat(this.repo, orgId, chatId);
|
|
661
|
+
await this.requireActor(actor, chat.communityId);
|
|
461
662
|
await this.requireActive(this.repo, orgId, chat.id, actor.id);
|
|
462
663
|
const page = await this.repo.listMessages(orgId, chatId, query);
|
|
463
664
|
this.logger.debug("chat messages read", {
|
|
@@ -469,18 +670,23 @@ var ChatServiceImpl = class {
|
|
|
469
670
|
rows: await this.withSenders(orgId, actor, page.rows)
|
|
470
671
|
};
|
|
471
672
|
}
|
|
472
|
-
async attachmentFile(
|
|
673
|
+
async attachmentFile(chatId, messageId, assetId, actor) {
|
|
674
|
+
const orgId = actor.orgId;
|
|
473
675
|
const chat = await this.repo.findChat(orgId, chatId);
|
|
474
676
|
const message = await this.repo.findMessage(orgId, messageId);
|
|
475
|
-
if (!chat || !message || message.chatId !== chatId)
|
|
476
|
-
if (!((await this.repo.attachmentsOf(orgId, [messageId]))[messageId] ?? []).includes(assetId))
|
|
477
|
-
if (!await this.access.isCommunityMember(
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
677
|
+
if (!chat || !message || message.chatId !== chatId) throw new NotFoundError("Message attachment", assetId);
|
|
678
|
+
if (!((await this.repo.attachmentsOf(orgId, [messageId]))[messageId] ?? []).includes(assetId)) throw new NotFoundError("Message attachment", assetId);
|
|
679
|
+
if (!await this.access.isCommunityMember(chat.communityId, actor)) throw new NotFoundError("Message attachment", assetId);
|
|
680
|
+
const moderates = actor.permissions.has("communities:moderate", chat.communityId);
|
|
681
|
+
if (!((await this.repo.participants(orgId, chatId)).some((user) => user.orgUserId === actor.id && user.leftAt === null) ? message.deletedAt === null || moderates : moderates && (await this.repo.listOpenFlags(orgId, chat.communityId)).some((flag) => flag.messageId === messageId))) throw new NotFoundError("Message attachment", assetId);
|
|
682
|
+
const url = await this.images.signFile(orgId, assetId);
|
|
683
|
+
if (!url) throw new NotFoundError("Message attachment", assetId);
|
|
684
|
+
return url;
|
|
685
|
+
}
|
|
686
|
+
async messageQueue(communityId, actor) {
|
|
687
|
+
const orgId = actor.orgId;
|
|
688
|
+
this.requireModerator(actor, communityId, "message queue");
|
|
689
|
+
await this.requireActor(actor, communityId);
|
|
484
690
|
await this.requireSettings(orgId, communityId);
|
|
485
691
|
const flags = await this.repo.listOpenFlags(orgId, communityId, 100);
|
|
486
692
|
const messages = await this.repo.messagesByIds(orgId, [...new Set(flags.map((flag) => flag.messageId))]);
|
|
@@ -511,14 +717,15 @@ var ChatServiceImpl = class {
|
|
|
511
717
|
participants: await this.repo.participants(orgId, chatId)
|
|
512
718
|
};
|
|
513
719
|
}
|
|
514
|
-
async titlesFor(
|
|
720
|
+
async titlesFor(chatIds, actor) {
|
|
721
|
+
const orgId = actor.orgId;
|
|
515
722
|
if (chatIds.length === 0) return {};
|
|
516
723
|
const summaries = (await this.repo.chatSummaries(orgId, [...new Set(chatIds)])).filter((summary) => summary.participantIds.includes(actor.id));
|
|
517
724
|
const others = [...new Set(summaries.flatMap((summary) => summary.participantIds.filter((id) => id !== actor.id)))];
|
|
518
725
|
const records = others.length > 0 ? await this.profiles.authorsOf(orgId, others) : {};
|
|
519
726
|
const nameOf = (id) => {
|
|
520
727
|
const record = records[id];
|
|
521
|
-
return record &&
|
|
728
|
+
return record && authorName(record) || "A member";
|
|
522
729
|
};
|
|
523
730
|
return Object.fromEntries(summaries.flatMap((summary) => {
|
|
524
731
|
const names = summary.participantIds.filter((id) => id !== actor.id).map(nameOf);
|
|
@@ -526,6 +733,12 @@ var ChatServiceImpl = class {
|
|
|
526
733
|
return summary.chat.kind === "group" ? [[summary.chat.id, "Group chat"]] : [];
|
|
527
734
|
}));
|
|
528
735
|
}
|
|
736
|
+
unreadInChat(orgId, chatId, orgUserId) {
|
|
737
|
+
return this.repo.unreadFor(orgId, chatId, orgUserId);
|
|
738
|
+
}
|
|
739
|
+
findMessage(orgId, messageId) {
|
|
740
|
+
return this.repo.findMessage(orgId, messageId);
|
|
741
|
+
}
|
|
529
742
|
async latestMessageBody(orgId, chatId) {
|
|
530
743
|
return (await this.repo.listMessages(orgId, chatId, {
|
|
531
744
|
page: 1,
|
|
@@ -535,11 +748,12 @@ var ChatServiceImpl = class {
|
|
|
535
748
|
isBlocked(orgId, communityId, a, b) {
|
|
536
749
|
return this.repo.isBlocked(orgId, communityId, a, b);
|
|
537
750
|
}
|
|
538
|
-
async setLock(
|
|
539
|
-
|
|
751
|
+
async setLock(actor, chatId, locked, communityId) {
|
|
752
|
+
const orgId = actor.orgId;
|
|
540
753
|
const chat = await this.mustChat(this.repo, orgId, chatId);
|
|
541
|
-
if (communityId !==
|
|
542
|
-
|
|
754
|
+
if (communityId !== null && chat.communityId !== communityId) throw new NotFoundError("Chat", chatId);
|
|
755
|
+
this.requireModerator(actor, chat.communityId, locked ? "lock chat" : "unlock chat");
|
|
756
|
+
await this.requireActor(actor, chat.communityId);
|
|
543
757
|
if (Boolean(chat.lockedAt) === locked) return chat;
|
|
544
758
|
const now = /* @__PURE__ */ new Date();
|
|
545
759
|
const saved = await this.uow.run(async ({ chats, outbox }) => {
|
|
@@ -563,44 +777,62 @@ var ChatServiceImpl = class {
|
|
|
563
777
|
});
|
|
564
778
|
return saved;
|
|
565
779
|
}
|
|
780
|
+
requireModerator(actor, communityId, action) {
|
|
781
|
+
if (!actor.permissions.has("communities:moderate", communityId)) {
|
|
782
|
+
this.logger.warn("chat moderation refused — not a moderator", {
|
|
783
|
+
orgId: actor.orgId,
|
|
784
|
+
communityId,
|
|
785
|
+
action,
|
|
786
|
+
actorId: actor.id
|
|
787
|
+
});
|
|
788
|
+
throw new ForbiddenError("You need communities:moderate");
|
|
789
|
+
}
|
|
790
|
+
}
|
|
566
791
|
async requireSettings(orgId, communityId) {
|
|
567
|
-
const settings = await this.content.getDmSettings(
|
|
792
|
+
const settings = await this.content.getDmSettings(communityId, this.authors.systemActor(orgId));
|
|
568
793
|
if (!settings) throw new NotFoundError("Community", communityId);
|
|
569
794
|
return settings;
|
|
570
795
|
}
|
|
571
|
-
async requireActor(
|
|
572
|
-
if (!await this.access.isCommunityMember(
|
|
796
|
+
async requireActor(actor, communityId) {
|
|
797
|
+
if (!await this.access.isCommunityMember(communityId, actor)) throw new ForbiddenError("You are not a member of this community");
|
|
573
798
|
}
|
|
799
|
+
/** Whether the person is team in the community; throws when they are not a
|
|
800
|
+
* member of it. */
|
|
574
801
|
async requirePerson(orgId, communityId, orgUserId) {
|
|
575
|
-
const
|
|
576
|
-
if (
|
|
577
|
-
return
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
802
|
+
const team = (await this.membersTeam(orgId, communityId, [orgUserId])).get(orgUserId);
|
|
803
|
+
if (team === void 0) throw new ForbiddenError("That person is not a member of this community");
|
|
804
|
+
return team;
|
|
805
|
+
}
|
|
806
|
+
/** Each member of the community among the given people, with whether they
|
|
807
|
+
* are team there. People who are not members are left out. */
|
|
808
|
+
async membersTeam(orgId, communityId, orgUserIds) {
|
|
809
|
+
const members = await this.access.communityMembersAmong(orgId, communityId, orgUserIds);
|
|
810
|
+
if (members.length === 0) return /* @__PURE__ */ new Map();
|
|
811
|
+
const permissions = await this.authors.permissionsOfMany(orgId, members);
|
|
812
|
+
return new Map(members.map((orgUserId) => {
|
|
813
|
+
const held = permissions.get(orgUserId);
|
|
814
|
+
return [orgUserId, held !== void 0 && isTeam(held, communityId)];
|
|
815
|
+
}));
|
|
816
|
+
}
|
|
817
|
+
async teamsOf(orgId, communityId, orgUserIds) {
|
|
818
|
+
return {
|
|
819
|
+
asked: new Set(orgUserIds),
|
|
820
|
+
team: await this.membersTeam(orgId, communityId, orgUserIds)
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
async assertMaySend(chats, orgId, actor, chat, users, settings, input, teams) {
|
|
593
824
|
this.assertActive(users, actor.id);
|
|
594
825
|
if (chat.lockedAt) throw new ForbiddenError("This chat is locked");
|
|
595
826
|
if (chat.status === "closed") throw new ForbiddenError("You cannot send in this chat");
|
|
596
827
|
if (this.pendingCreator(chat, actor)) throw new ForbiddenError("You cannot send in this chat");
|
|
597
828
|
const others = users.filter((other) => other.orgUserId !== actor.id && (chat.kind === "direct" || other.leftAt === null)).map((other) => other.orgUserId);
|
|
829
|
+
if (others.some((other) => !teams.asked.has(other))) throw new ConflictError("This chat changed while you were sending; send again");
|
|
598
830
|
if (others.length > 0) {
|
|
599
|
-
const
|
|
600
|
-
const
|
|
831
|
+
const recipients = chat.kind === "group" ? others.filter((other) => teams.team.has(other)) : others;
|
|
832
|
+
const actorTeam = isTeam(actor.permissions, chat.communityId);
|
|
601
833
|
for (const other of recipients) {
|
|
602
|
-
const
|
|
603
|
-
if (
|
|
834
|
+
const team = teams.team.get(other);
|
|
835
|
+
if (team === void 0 || !this.mayMessage(settings, actorTeam, team)) throw new ForbiddenError("You cannot send in this chat");
|
|
604
836
|
}
|
|
605
837
|
if (recipients.length > 0) {
|
|
606
838
|
if ((await chats.blockedWith(orgId, chat.communityId, actor.id, recipients)).length > 0) throw new ForbiddenError("You cannot send in this chat");
|
|
@@ -610,30 +842,35 @@ var ChatServiceImpl = class {
|
|
|
610
842
|
const reply = await chats.findMessage(orgId, input.replyToId);
|
|
611
843
|
if (!reply || reply.chatId !== chat.id) throw new NotFoundError("Message", input.replyToId);
|
|
612
844
|
}
|
|
613
|
-
await this.requireOwnedImages(orgId, actor.id, input.assetIds);
|
|
614
845
|
}
|
|
615
|
-
|
|
846
|
+
mentionable(body, users) {
|
|
847
|
+
return keepAllowed(mentionTokens(body), users.filter((user) => user.leftAt === null).map((user) => user.orgUserId));
|
|
848
|
+
}
|
|
849
|
+
async writeMessage(chats, mentions, orgId, actor, chat, users, input) {
|
|
616
850
|
const opened = chat.kind === "direct" && chat.status === "pending" && chat.createdBy !== actor.id;
|
|
617
851
|
const current = opened ? await this.mustUpdate(chats, orgId, chat.id, { status: "open" }) : chat;
|
|
618
852
|
const message = await chats.insertMessage(orgId, this.newMessage(orgId, current, actor, input, /* @__PURE__ */ new Date()));
|
|
619
853
|
if (input.assetIds.length > 0) await chats.attach(orgId, current.communityId, message.id, input.assetIds);
|
|
854
|
+
const mentioned = this.mentionable(input.body, users);
|
|
855
|
+
await mentions.sync(orgId, "message", message.id, mentioned, message.createdAt);
|
|
620
856
|
return {
|
|
621
857
|
chat: current,
|
|
622
858
|
message,
|
|
623
|
-
opened
|
|
859
|
+
opened,
|
|
860
|
+
mentioned
|
|
624
861
|
};
|
|
625
862
|
}
|
|
626
|
-
assertMayMessage(settings,
|
|
863
|
+
assertMayMessage(settings, actorTeam, targetTeam) {
|
|
627
864
|
if (!settings.enabled) throw new ForbiddenError("Direct messages are off");
|
|
628
|
-
if (!this.mayMessage(settings,
|
|
865
|
+
if (!this.mayMessage(settings, actorTeam, targetTeam)) throw new ForbiddenError("You cannot message this person");
|
|
629
866
|
}
|
|
630
867
|
assertGroup(settings) {
|
|
631
868
|
if (!settings.enabled) throw new ForbiddenError("Direct messages are off");
|
|
632
869
|
if (!settings.groupChats) throw new ForbiddenError("Group chats are off");
|
|
633
870
|
}
|
|
634
|
-
mayMessage(settings,
|
|
635
|
-
if (
|
|
636
|
-
if (
|
|
871
|
+
mayMessage(settings, actorTeam, targetTeam) {
|
|
872
|
+
if (actorTeam) return settings.teamToAnyone;
|
|
873
|
+
if (targetTeam) return settings.memberToTeam;
|
|
637
874
|
return settings.memberToMember;
|
|
638
875
|
}
|
|
639
876
|
async requireActive(repo, orgId, chatId, orgUserId) {
|
|
@@ -653,10 +890,10 @@ var ChatServiceImpl = class {
|
|
|
653
890
|
throw new NotFoundError("Asset", assetId);
|
|
654
891
|
}
|
|
655
892
|
}
|
|
656
|
-
async requireWritableChat(
|
|
657
|
-
const chat = await this.mustChat(this.repo, orgId, chatId);
|
|
658
|
-
await this.requireActor(
|
|
659
|
-
await this.requireActive(this.repo, orgId, chat.id, actor.id);
|
|
893
|
+
async requireWritableChat(actor, chatId) {
|
|
894
|
+
const chat = await this.mustChat(this.repo, actor.orgId, chatId);
|
|
895
|
+
await this.requireActor(actor, chat.communityId);
|
|
896
|
+
await this.requireActive(this.repo, actor.orgId, chat.id, actor.id);
|
|
660
897
|
if (chat.lockedAt || chat.status === "closed") throw new ForbiddenError("You cannot send in this chat");
|
|
661
898
|
}
|
|
662
899
|
pendingCreator(chat, actor) {
|
|
@@ -686,8 +923,6 @@ var ChatServiceImpl = class {
|
|
|
686
923
|
joinedAt: now,
|
|
687
924
|
leftAt: null,
|
|
688
925
|
lastReadMessageId: null,
|
|
689
|
-
muted: false,
|
|
690
|
-
mutedUntil: null,
|
|
691
926
|
createdAt: now,
|
|
692
927
|
updatedAt: now
|
|
693
928
|
};
|
|
@@ -701,6 +936,7 @@ var ChatServiceImpl = class {
|
|
|
701
936
|
orgUserId: actor.id,
|
|
702
937
|
body: input.body,
|
|
703
938
|
replyToId: input.replyToId,
|
|
939
|
+
clientId: input.clientId ?? null,
|
|
704
940
|
editedAt: null,
|
|
705
941
|
deletedAt: null,
|
|
706
942
|
createdAt: now,
|
|
@@ -717,7 +953,7 @@ var ChatServiceImpl = class {
|
|
|
717
953
|
};
|
|
718
954
|
}
|
|
719
955
|
meta(actor) {
|
|
720
|
-
return {
|
|
956
|
+
return { actorId: actor.id };
|
|
721
957
|
}
|
|
722
958
|
createdEvent(orgId, actor, chat, users) {
|
|
723
959
|
return discussionEvents.chatCreated.make({
|
|
@@ -745,51 +981,80 @@ var ChatServiceImpl = class {
|
|
|
745
981
|
metadata: this.meta(actor)
|
|
746
982
|
});
|
|
747
983
|
}
|
|
748
|
-
sentEvent(orgId, actor, chat, message) {
|
|
984
|
+
sentEvent(orgId, actor, chat, message, mentionedOrgUserIds) {
|
|
749
985
|
return discussionEvents.messageSent.make({
|
|
750
986
|
orgId,
|
|
751
|
-
data:
|
|
987
|
+
data: {
|
|
988
|
+
...eventMessage(message),
|
|
989
|
+
mentionedOrgUserIds
|
|
990
|
+
},
|
|
752
991
|
metadata: {
|
|
753
992
|
...this.meta(actor),
|
|
754
993
|
chatStatus: chat.status
|
|
755
994
|
}
|
|
756
995
|
});
|
|
757
996
|
}
|
|
758
|
-
async viewOf(
|
|
759
|
-
const [view] = await this.withSenders(orgId, actor, [message]);
|
|
997
|
+
async viewOf(actor, message) {
|
|
998
|
+
const [view] = await this.withSenders(actor.orgId, actor, [message]);
|
|
760
999
|
return view;
|
|
761
1000
|
}
|
|
762
|
-
async withSenders(orgId,
|
|
1001
|
+
async withSenders(orgId, viewer, messages) {
|
|
763
1002
|
if (messages.length === 0) return [];
|
|
764
1003
|
const messageIds = messages.map((message) => message.id);
|
|
765
|
-
const
|
|
766
|
-
|
|
1004
|
+
const senderIds = [...new Set(messages.map((message) => message.orgUserId))];
|
|
1005
|
+
const [records, permissions, attached, reactions, mentioned] = await Promise.all([
|
|
1006
|
+
this.profiles.authorsOf(orgId, senderIds),
|
|
1007
|
+
this.authors.permissionsOfMany(orgId, senderIds),
|
|
767
1008
|
this.repo.attachmentsOf(orgId, messageIds),
|
|
768
|
-
this.repo.reactionsOf(orgId, messageIds,
|
|
1009
|
+
this.repo.reactionsOf(orgId, messageIds, viewer?.id ?? ""),
|
|
1010
|
+
mentionViews(this.mentions, this.profiles, orgId, "message", messageIds)
|
|
769
1011
|
]);
|
|
770
|
-
const
|
|
1012
|
+
const hidden = (message) => message.deletedAt !== null && !viewer?.permissions.has("communities:moderate", message.communityId);
|
|
1013
|
+
const shown = (message) => hidden(message) ? [] : attached[message.id] ?? [];
|
|
771
1014
|
return messages.map((message) => {
|
|
772
1015
|
const record = records[message.orgUserId];
|
|
773
1016
|
if (!record) throw new NotFoundError("Member", message.orgUserId);
|
|
774
1017
|
const counts = reactions[message.id];
|
|
775
1018
|
return {
|
|
776
|
-
...
|
|
777
|
-
|
|
1019
|
+
...hidden(message) ? {
|
|
1020
|
+
...message,
|
|
1021
|
+
body: ""
|
|
1022
|
+
} : message,
|
|
1023
|
+
sender: authorView(orgId, record, this.fileUrls, teamBadge(permissions.get(message.orgUserId), message.communityId)),
|
|
778
1024
|
attachments: shown(message).map((assetId) => ({
|
|
779
1025
|
assetId,
|
|
780
1026
|
url: this.fileUrls.messageAttachment(message.chatId, message.id, assetId)
|
|
781
1027
|
})),
|
|
782
1028
|
reactions: counts?.reactions ?? {},
|
|
783
|
-
viewerReaction: counts?.viewerReaction ?? null
|
|
1029
|
+
viewerReaction: counts?.viewerReaction ?? null,
|
|
1030
|
+
mentions: hidden(message) ? [] : mentioned[message.id] ?? []
|
|
784
1031
|
};
|
|
785
1032
|
});
|
|
786
1033
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
1034
|
+
async listItems(actor, communityId, rows) {
|
|
1035
|
+
const orgId = actor.orgId;
|
|
1036
|
+
const seesDeleted = actor.permissions.has("communities:moderate", communityId);
|
|
1037
|
+
const now = /* @__PURE__ */ new Date();
|
|
1038
|
+
const [subscribed, profiles] = await Promise.all([this.subscriptions.forViewer(orgId, actor.id, "chat", rows.map((item) => item.chat.id)), this.participantsOf(orgId, communityId, rows.flatMap((item) => item.participantIds))]);
|
|
1039
|
+
const subscriptionOf = new Map(subscribed.map((row) => [row.itemId, row]));
|
|
1040
|
+
return rows.map((item) => ({
|
|
1041
|
+
...item,
|
|
1042
|
+
participants: item.participantIds.flatMap((id) => profiles.get(id) ?? []),
|
|
1043
|
+
subscription: activeSubscription(subscriptionOf.get(item.chat.id), now),
|
|
1044
|
+
lastMessage: item.lastMessage && item.lastMessage.deletedAt !== null && !seesDeleted ? {
|
|
1045
|
+
...item.lastMessage,
|
|
1046
|
+
body: ""
|
|
1047
|
+
} : item.lastMessage
|
|
1048
|
+
}));
|
|
1049
|
+
}
|
|
1050
|
+
async participantsOf(orgId, communityId, orgUserIds) {
|
|
1051
|
+
const ids = [...new Set(orgUserIds)];
|
|
1052
|
+
if (ids.length === 0) return /* @__PURE__ */ new Map();
|
|
1053
|
+
const [records, permissions] = await Promise.all([this.profiles.authorsOf(orgId, ids), this.authors.permissionsOfMany(orgId, ids)]);
|
|
1054
|
+
return new Map(ids.flatMap((id) => {
|
|
1055
|
+
const record = records[id];
|
|
1056
|
+
return record ? [[id, authorView(orgId, record, this.fileUrls, teamBadge(permissions.get(id), communityId))]] : [];
|
|
1057
|
+
}));
|
|
793
1058
|
}
|
|
794
1059
|
};
|
|
795
1060
|
//#endregion
|