@headless-lms/core 0.3.0 → 0.4.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 +9 -9
- package/dist/assets/index.d.ts +3 -2
- package/dist/assets/index.js +2 -1
- 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 +4 -3
- package/dist/assets/service.d.ts +0 -1
- package/dist/assets/service.js +0 -4
- 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 +69 -62
- package/dist/automations/catalog.js.map +1 -1
- package/dist/automations/community-derivations.js +4 -4
- package/dist/automations/community-derivations.js.map +1 -1
- package/dist/automations/community-mail.d.ts +3 -17
- package/dist/automations/community-mail.js +1 -21
- package/dist/automations/community-mail.js.map +1 -1
- package/dist/automations/events.d.ts +0 -10
- package/dist/automations/index.d.ts +11 -4
- package/dist/automations/index.js +8 -1
- 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 +316 -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 +39 -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 +249 -0
- package/dist/automations/notifications/rules/entitlements.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 +28 -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 +99 -0
- package/dist/automations/notifications/rules/recipients.js.map +1 -0
- package/dist/automations/notifications/service.d.ts +27 -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 +473 -0
- package/dist/automations/notifications/types.js.map +1 -0
- package/dist/automations/ports.d.ts +119 -5
- package/dist/automations/service.d.ts +10 -13
- package/dist/automations/service.js +21 -83
- package/dist/automations/service.js.map +1 -1
- package/dist/content/events.d.ts +130 -80
- package/dist/content/events.js +11 -0
- package/dist/content/events.js.map +1 -1
- package/dist/content/index.d.ts +6 -4
- package/dist/content/index.js +3 -1
- package/dist/content/model.d.ts +2 -2
- package/dist/content/notification-categories.d.ts +36 -0
- package/dist/content/notification-categories.js +64 -0
- package/dist/content/notification-categories.js.map +1 -0
- package/dist/content/ports.d.ts +25 -6
- package/dist/content/service.d.ts +4 -4
- package/dist/content/service.js +24 -8
- package/dist/content/service.js.map +1 -1
- package/dist/content/titles.d.ts +22 -0
- package/dist/content/titles.js +69 -0
- package/dist/content/titles.js.map +1 -0
- package/dist/content/types.d.ts +2 -2
- package/dist/discussion/chats.d.ts +9 -4
- package/dist/discussion/chats.js +80 -47
- 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 +194 -28
- package/dist/discussion/events.js +74 -17
- package/dist/discussion/events.js.map +1 -1
- package/dist/discussion/index.d.ts +7 -2
- package/dist/discussion/index.js +6 -1
- 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/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 +107 -24
- package/dist/discussion/ports.js.map +1 -1
- package/dist/discussion/posts.d.ts +18 -8
- package/dist/discussion/posts.js +207 -155
- package/dist/discussion/posts.js.map +1 -1
- package/dist/discussion/service.d.ts +17 -8
- package/dist/discussion/service.js +105 -41
- package/dist/discussion/service.js.map +1 -1
- package/dist/discussion/subscriptions.d.ts +37 -0
- package/dist/discussion/subscriptions.js +99 -0
- package/dist/discussion/subscriptions.js.map +1 -0
- package/dist/discussion/titles.d.ts +26 -0
- package/dist/discussion/titles.js +84 -0
- package/dist/discussion/titles.js.map +1 -0
- package/dist/entitlements/events.d.ts +20 -20
- package/dist/entitlements/index.d.ts +2 -1
- package/dist/entitlements/index.js +2 -1
- 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/ports.d.ts +7 -3
- package/dist/entitlements/service.d.ts +5 -2
- package/dist/entitlements/service.js +23 -3
- package/dist/entitlements/service.js.map +1 -1
- package/dist/identity/devices.d.ts +30 -0
- package/dist/identity/devices.js +107 -0
- package/dist/identity/devices.js.map +1 -0
- package/dist/identity/events.d.ts +2 -2
- package/dist/identity/index.d.ts +5 -4
- package/dist/identity/index.js +3 -2
- package/dist/identity/model.d.ts +2 -2
- package/dist/identity/notification-settings.d.ts +28 -0
- package/dist/identity/notification-settings.js +113 -0
- package/dist/identity/notification-settings.js.map +1 -0
- package/dist/identity/ports.d.ts +78 -54
- package/dist/integrations/events.d.ts +3 -3
- package/dist/organizations/events.d.ts +32 -32
- package/dist/organizations/index.d.ts +2 -1
- package/dist/organizations/index.js +2 -1
- package/dist/organizations/notification-categories.d.ts +32 -0
- package/dist/organizations/notification-categories.js +51 -0
- package/dist/organizations/notification-categories.js.map +1 -0
- package/dist/organizations/ports.d.ts +3 -0
- package/dist/organizations/service.d.ts +2 -0
- package/dist/organizations/service.js +7 -0
- package/dist/organizations/service.js.map +1 -1
- package/dist/progress/events.d.ts +14 -14
- package/dist/reporting/member/index.d.ts +2 -2
- package/dist/reporting/member/model.d.ts +56 -23
- package/dist/reporting/member/ports.d.ts +9 -6
- package/dist/reporting/member/service.d.ts +33 -14
- package/dist/reporting/member/service.js +138 -60
- package/dist/reporting/member/service.js.map +1 -1
- package/dist/shared/errors.d.ts +13 -1
- package/dist/shared/errors.js +18 -1
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/id.d.ts +3 -0
- package/dist/shared/id.js +4 -1
- package/dist/shared/id.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 +48 -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 +2 -2
- package/dist/types/email-templates.d.ts +97 -3
- package/dist/types/identity.d.ts +2 -2
- package/dist/types/index.d.ts +7 -6
- package/dist/types/notifications.d.ts +157 -0
- package/dist/types/notifications.js +1 -0
- 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 +62 -1
- package/dist/types/schemas/content.js +25 -3
- package/dist/types/schemas/content.js.map +1 -1
- package/dist/types/schemas/discussion.d.ts +85 -12
- package/dist/types/schemas/discussion.js +47 -13
- package/dist/types/schemas/discussion.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 +6 -5
- package/dist/types/schemas/index.js +6 -5
- package/dist/types/schemas/notifications.d.ts +95 -0
- package/dist/types/schemas/notifications.js +56 -0
- package/dist/types/schemas/notifications.js.map +1 -0
- package/dist/types/schemas/progress.d.ts +3 -3
- 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
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Activity } from "../types/schemas/content.js";
|
|
2
|
+
import { SubjectRef } from "../types/notifications.js";
|
|
3
|
+
import "../types/index.js";
|
|
4
|
+
import { Actor } from "../shared/actor.js";
|
|
5
|
+
import { BundlesService, ContentService, ItemTitle, ItemTitleAccess, ItemTitles } from "./ports.js";
|
|
6
|
+
//#region src/content/titles.d.ts
|
|
7
|
+
declare function activityTitle(activity: Pick<Activity, 'settings'>): string;
|
|
8
|
+
type TitleReads = Pick<ContentService, 'getCourse' | 'getActivity' | 'getDownload' | 'getSpace' | 'getCommunity'> & Pick<BundlesService, 'getBundle' | 'listBundleItems'>;
|
|
9
|
+
declare class ContentItemTitles implements ItemTitles {
|
|
10
|
+
private readonly deps;
|
|
11
|
+
constructor(deps: {
|
|
12
|
+
content: TitleReads;
|
|
13
|
+
access: ItemTitleAccess;
|
|
14
|
+
});
|
|
15
|
+
titlesFor(orgId: string, actor: Actor, refs: SubjectRef[]): Promise<Record<string, ItemTitle>>;
|
|
16
|
+
private titleOf;
|
|
17
|
+
private opensCourse;
|
|
18
|
+
private holdsBundle;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { ContentItemTitles, activityTitle };
|
|
22
|
+
//# sourceMappingURL=titles.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { isStaffRole } from "../organizations/roles.js";
|
|
2
|
+
//#region src/content/titles.ts
|
|
3
|
+
function activityTitle(activity) {
|
|
4
|
+
const title = activity.settings?.title;
|
|
5
|
+
return typeof title === "string" ? title : "";
|
|
6
|
+
}
|
|
7
|
+
function isPublishedActivity(activity) {
|
|
8
|
+
return activity.settings?.published !== false;
|
|
9
|
+
}
|
|
10
|
+
var ContentItemTitles = class {
|
|
11
|
+
deps;
|
|
12
|
+
constructor(deps) {
|
|
13
|
+
this.deps = deps;
|
|
14
|
+
}
|
|
15
|
+
async titlesFor(orgId, actor, refs) {
|
|
16
|
+
const unique = new Map(refs.map((ref) => [`${ref.type}:${ref.id}`, ref]));
|
|
17
|
+
const entries = await Promise.all([...unique].map(async ([key, ref]) => {
|
|
18
|
+
const title = await this.titleOf(orgId, actor, ref);
|
|
19
|
+
return title === null ? null : [key, { title }];
|
|
20
|
+
}));
|
|
21
|
+
return Object.fromEntries(entries.filter((entry) => entry !== null));
|
|
22
|
+
}
|
|
23
|
+
async titleOf(orgId, actor, ref) {
|
|
24
|
+
const staff = isStaffRole(actor.role);
|
|
25
|
+
switch (ref.type) {
|
|
26
|
+
case "course": {
|
|
27
|
+
const course = await this.deps.content.getCourse(orgId, ref.id);
|
|
28
|
+
return course && (staff || await this.opensCourse(orgId, actor, course)) ? course.title : null;
|
|
29
|
+
}
|
|
30
|
+
case "activity": {
|
|
31
|
+
const activity = await this.deps.content.getActivity(orgId, ref.id);
|
|
32
|
+
if (!activity) return null;
|
|
33
|
+
if (staff) return activityTitle(activity);
|
|
34
|
+
const course = await this.deps.content.getCourse(orgId, activity.courseId);
|
|
35
|
+
return course && isPublishedActivity(activity) && await this.opensCourse(orgId, actor, course) ? activityTitle(activity) : null;
|
|
36
|
+
}
|
|
37
|
+
case "download": {
|
|
38
|
+
const download = await this.deps.content.getDownload(orgId, ref.id);
|
|
39
|
+
if (!download) return null;
|
|
40
|
+
return staff || download.status === "published" && await this.deps.access.hasAccess(orgId, actor.id, download.id) ? download.title : null;
|
|
41
|
+
}
|
|
42
|
+
case "bundle": {
|
|
43
|
+
const bundle = await this.deps.content.getBundle(orgId, ref.id);
|
|
44
|
+
if (!bundle) return null;
|
|
45
|
+
return staff || await this.holdsBundle(orgId, actor, ref.id) ? bundle.name : null;
|
|
46
|
+
}
|
|
47
|
+
case "community": {
|
|
48
|
+
const community = await this.deps.content.getCommunity(orgId, ref.id);
|
|
49
|
+
return community && await this.deps.access.isCommunityMember(orgId, actor, ref.id) ? community.title : null;
|
|
50
|
+
}
|
|
51
|
+
case "space": {
|
|
52
|
+
const space = await this.deps.content.getSpace(orgId, ref.id);
|
|
53
|
+
return space && await this.deps.access.canOpenSpace(orgId, actor, ref.id) ? space.name : null;
|
|
54
|
+
}
|
|
55
|
+
default: return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async opensCourse(orgId, actor, course) {
|
|
59
|
+
return course.status === "published" && this.deps.access.hasAccess(orgId, actor.id, course.id);
|
|
60
|
+
}
|
|
61
|
+
async holdsBundle(orgId, actor, bundleId) {
|
|
62
|
+
const items = await this.deps.content.listBundleItems(orgId, bundleId);
|
|
63
|
+
return (await Promise.all(items.map((item) => this.deps.access.hasAccess(orgId, actor.id, item.contentId)))).some(Boolean);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
//#endregion
|
|
67
|
+
export { ContentItemTitles, activityTitle };
|
|
68
|
+
|
|
69
|
+
//# sourceMappingURL=titles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"titles.js","names":[],"sources":["../../src/content/titles.ts"],"sourcesContent":["import { isStaffRole } from '../organizations/index.js';\nimport type { Actor } from '../shared/actor.js';\nimport type { Activity, SubjectRef } from '../types/index.js';\nimport type {\n BundlesService,\n ContentService,\n ItemTitle,\n ItemTitleAccess,\n ItemTitles,\n} from './ports.js';\n\nexport function activityTitle(activity: Pick<Activity, 'settings'>): string {\n const title = (activity.settings as { title?: unknown } | null)?.title;\n return typeof title === 'string' ? title : '';\n}\n\nfunction isPublishedActivity(activity: Pick<Activity, 'settings'>): boolean {\n return (activity.settings as { published?: boolean } | null)?.published !== false;\n}\n\ntype TitleReads = Pick<\n ContentService,\n 'getCourse' | 'getActivity' | 'getDownload' | 'getSpace' | 'getCommunity'\n> &\n Pick<BundlesService, 'getBundle' | 'listBundleItems'>;\n\nexport class ContentItemTitles implements ItemTitles {\n constructor(\n private readonly deps: {\n content: TitleReads;\n access: ItemTitleAccess;\n },\n ) {}\n\n async titlesFor(\n orgId: string,\n actor: Actor,\n refs: SubjectRef[],\n ): Promise<Record<string, ItemTitle>> {\n const unique = new Map(refs.map((ref) => [`${ref.type}:${ref.id}`, ref]));\n const entries = await Promise.all(\n [...unique].map(async ([key, ref]): Promise<[string, ItemTitle] | null> => {\n const title = await this.titleOf(orgId, actor, ref);\n return title === null ? null : [key, { title }];\n }),\n );\n return Object.fromEntries(\n entries.filter((entry): entry is [string, ItemTitle] => entry !== null),\n );\n }\n\n private async titleOf(orgId: string, actor: Actor, ref: SubjectRef): Promise<string | null> {\n const staff = isStaffRole(actor.role);\n switch (ref.type) {\n case 'course': {\n const course = await this.deps.content.getCourse(orgId, ref.id);\n return course && (staff || (await this.opensCourse(orgId, actor, course)))\n ? course.title\n : null;\n }\n case 'activity': {\n const activity = await this.deps.content.getActivity(orgId, ref.id);\n if (!activity) {\n return null;\n }\n if (staff) {\n return activityTitle(activity);\n }\n const course = await this.deps.content.getCourse(orgId, activity.courseId);\n return course &&\n isPublishedActivity(activity) &&\n (await this.opensCourse(orgId, actor, course))\n ? activityTitle(activity)\n : null;\n }\n case 'download': {\n const download = await this.deps.content.getDownload(orgId, ref.id);\n if (!download) {\n return null;\n }\n return staff ||\n (download.status === 'published' &&\n (await this.deps.access.hasAccess(orgId, actor.id, download.id)))\n ? download.title\n : null;\n }\n case 'bundle': {\n const bundle = await this.deps.content.getBundle(orgId, ref.id);\n if (!bundle) {\n return null;\n }\n return staff || (await this.holdsBundle(orgId, actor, ref.id)) ? bundle.name : null;\n }\n case 'community': {\n const community = await this.deps.content.getCommunity(orgId, ref.id);\n return community && (await this.deps.access.isCommunityMember(orgId, actor, ref.id))\n ? community.title\n : null;\n }\n case 'space': {\n const space = await this.deps.content.getSpace(orgId, ref.id);\n return space && (await this.deps.access.canOpenSpace(orgId, actor, ref.id))\n ? space.name\n : null;\n }\n default:\n return null;\n }\n }\n\n private async opensCourse(\n orgId: string,\n actor: Actor,\n course: { id: string; status: string },\n ): Promise<boolean> {\n return course.status === 'published' && this.deps.access.hasAccess(orgId, actor.id, course.id);\n }\n\n private async holdsBundle(orgId: string, actor: Actor, bundleId: string): Promise<boolean> {\n const items = await this.deps.content.listBundleItems(orgId, bundleId);\n const held = await Promise.all(\n items.map((item) => this.deps.access.hasAccess(orgId, actor.id, item.contentId)),\n );\n return held.some(Boolean);\n }\n}\n"],"mappings":"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"}
|
package/dist/content/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Page } from "../types/shared.js";
|
|
2
|
-
import { AddDownloadAssetInput, CreateBundleInput, CreateCommunityInput, CreateCourseInput, CreateDownloadInput, CreateSpaceInput, ListBundlesQuery, ListCommunitiesQuery, ListCoursesQuery, ListDownloadsQuery, PatchDmSettingsInput, ReorderDownloadAssetsInput, SetSpaceTopicsInput, UpdateBundleInput, UpdateCommunityInput, UpdateCourseInput, UpdateDownloadInput, UpdateSpaceInput } from "../types/schemas/content.js";
|
|
2
|
+
import { AddDownloadAssetInput, CreateBundleInput, CreateCommunityInput, CreateCourseInput, CreateDownloadInput, CreateSpaceInput, ListBundlesQuery, ListCommunitiesQuery, ListCoursesQuery, ListDownloadsQuery, PatchCommunitySettingsInput, PatchDmSettingsInput, ReorderDownloadAssetsInput, SetSpaceTopicsInput, UpdateBundleInput, UpdateCommunityInput, UpdateCourseInput, UpdateDownloadInput, UpdateSpaceInput } from "../types/schemas/content.js";
|
|
3
3
|
import "../types/index.js";
|
|
4
|
-
export type { AddDownloadAssetInput, CreateBundleInput, CreateCommunityInput, CreateCourseInput, CreateDownloadInput, CreateSpaceInput, ListBundlesQuery, ListCommunitiesQuery, ListCoursesQuery, ListDownloadsQuery, Page, PatchDmSettingsInput, ReorderDownloadAssetsInput, SetSpaceTopicsInput, UpdateBundleInput, UpdateCommunityInput, UpdateCourseInput, UpdateDownloadInput, UpdateSpaceInput };
|
|
4
|
+
export type { AddDownloadAssetInput, CreateBundleInput, CreateCommunityInput, CreateCourseInput, CreateDownloadInput, CreateSpaceInput, ListBundlesQuery, ListCommunitiesQuery, ListCoursesQuery, ListDownloadsQuery, Page, PatchCommunitySettingsInput, PatchDmSettingsInput, ReorderDownloadAssetsInput, SetSpaceTopicsInput, UpdateBundleInput, UpdateCommunityInput, UpdateCourseInput, UpdateDownloadInput, UpdateSpaceInput };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Page } from "../types/shared.js";
|
|
2
2
|
import { Logger } from "../types/ports.js";
|
|
3
|
-
import { Chat, ChatUser, MessageFlag, MessageFlagResolution, MessageView, ReactionEmoji } from "../types/schemas/discussion.js";
|
|
3
|
+
import { Chat, ChatUser, Message, MessageFlag, MessageFlagResolution, MessageView, ReactionEmoji } from "../types/schemas/discussion.js";
|
|
4
4
|
import "../types/index.js";
|
|
5
5
|
import "../shared/ports.js";
|
|
6
6
|
import { FileUrls } from "../assets/file-urls.js";
|
|
@@ -11,7 +11,7 @@ import { CommunityReader } from "../content/ports.js";
|
|
|
11
11
|
import "../content/index.js";
|
|
12
12
|
import { CommunityAccessReader } from "../entitlements/ports.js";
|
|
13
13
|
import "../entitlements/index.js";
|
|
14
|
-
import { AuthorProfileReader, AuthorRoleReader, ChatListItem, ChatRepository, ChatService, ChatUnitOfWork, MessageQueue, MessageReactions, PageQuery, SendMessageInput, StartDirectInput, StartGroupInput } from "./ports.js";
|
|
14
|
+
import { AuthorProfileReader, AuthorRoleReader, ChatListItem, ChatRepository, ChatService, ChatUnitOfWork, ChatUnread, MentionsRepository, MessageQueue, MessageReactions, PageQuery, SendMessageInput, StartDirectInput, StartGroupInput, SubscriptionsRepository } from "./ports.js";
|
|
15
15
|
//#region src/discussion/chats.d.ts
|
|
16
16
|
type ChatServiceParams = {
|
|
17
17
|
repo: ChatRepository;
|
|
@@ -19,6 +19,8 @@ type ChatServiceParams = {
|
|
|
19
19
|
content: CommunityReader;
|
|
20
20
|
authors: AuthorRoleReader;
|
|
21
21
|
profiles: AuthorProfileReader;
|
|
22
|
+
subscriptions: SubscriptionsRepository;
|
|
23
|
+
mentions: MentionsRepository;
|
|
22
24
|
images: MemberImageReader & Pick<AssetFiles, 'signFile'>;
|
|
23
25
|
fileUrls: FileUrls;
|
|
24
26
|
uow: ChatUnitOfWork;
|
|
@@ -30,6 +32,8 @@ declare class ChatServiceImpl implements ChatService {
|
|
|
30
32
|
private readonly content;
|
|
31
33
|
private readonly authors;
|
|
32
34
|
private readonly profiles;
|
|
35
|
+
private readonly subscriptions;
|
|
36
|
+
private readonly mentions;
|
|
33
37
|
private readonly images;
|
|
34
38
|
private readonly fileUrls;
|
|
35
39
|
private readonly uow;
|
|
@@ -45,8 +49,6 @@ declare class ChatServiceImpl implements ChatService {
|
|
|
45
49
|
deleteMessage(orgId: string, actor: Actor, messageId: string): Promise<MessageView>;
|
|
46
50
|
setReaction(orgId: string, actor: Actor, messageId: string, emoji: ReactionEmoji | null): Promise<MessageReactions>;
|
|
47
51
|
markRead(orgId: string, actor: Actor, chatId: string, messageId: string): Promise<void>;
|
|
48
|
-
mute(orgId: string, actor: Actor, chatId: string, until?: Date): Promise<void>;
|
|
49
|
-
unmute(orgId: string, actor: Actor, chatId: string): Promise<void>;
|
|
50
52
|
block(orgId: string, actor: Actor, communityId: string, orgUserId: string): Promise<void>;
|
|
51
53
|
unblock(orgId: string, actor: Actor, communityId: string, orgUserId: string): Promise<void>;
|
|
52
54
|
flagMessage(orgId: string, actor: Actor, messageId: string, reason: string): Promise<MessageFlag>;
|
|
@@ -65,6 +67,8 @@ declare class ChatServiceImpl implements ChatService {
|
|
|
65
67
|
participants: ChatUser[];
|
|
66
68
|
} | null>;
|
|
67
69
|
titlesFor(orgId: string, actor: Actor, chatIds: string[]): Promise<Record<string, string>>;
|
|
70
|
+
unreadInChat(orgId: string, chatId: string, orgUserId: string): Promise<ChatUnread>;
|
|
71
|
+
findMessage(orgId: string, messageId: string): Promise<Message | null>;
|
|
68
72
|
latestMessageBody(orgId: string, chatId: string): Promise<string | null>;
|
|
69
73
|
isBlocked(orgId: string, communityId: string, a: string, b: string): Promise<boolean>;
|
|
70
74
|
private setLock;
|
|
@@ -74,6 +78,7 @@ declare class ChatServiceImpl implements ChatService {
|
|
|
74
78
|
private personRole;
|
|
75
79
|
private personRoles;
|
|
76
80
|
private assertMaySend;
|
|
81
|
+
private mentionable;
|
|
77
82
|
private writeMessage;
|
|
78
83
|
private assertMayMessage;
|
|
79
84
|
private assertGroup;
|
package/dist/discussion/chats.js
CHANGED
|
@@ -4,6 +4,8 @@ import { noopLogger } from "../shared/logger.js";
|
|
|
4
4
|
import { isStaffRole } from "../organizations/roles.js";
|
|
5
5
|
import { discussionEvents } from "./events.js";
|
|
6
6
|
import { authorView } from "./authors.js";
|
|
7
|
+
import { keepAllowed, mentionTokens, mentionViews } from "./mentions.js";
|
|
8
|
+
import { activeSubscription } from "./subscriptions.js";
|
|
7
9
|
import "./ports.js";
|
|
8
10
|
//#region src/discussion/chats.ts
|
|
9
11
|
var ChatServiceImpl = class {
|
|
@@ -12,6 +14,8 @@ var ChatServiceImpl = class {
|
|
|
12
14
|
content;
|
|
13
15
|
authors;
|
|
14
16
|
profiles;
|
|
17
|
+
subscriptions;
|
|
18
|
+
mentions;
|
|
15
19
|
images;
|
|
16
20
|
fileUrls;
|
|
17
21
|
uow;
|
|
@@ -22,6 +26,8 @@ var ChatServiceImpl = class {
|
|
|
22
26
|
this.content = params.content;
|
|
23
27
|
this.authors = params.authors;
|
|
24
28
|
this.profiles = params.profiles;
|
|
29
|
+
this.subscriptions = params.subscriptions;
|
|
30
|
+
this.mentions = params.mentions;
|
|
25
31
|
this.images = params.images;
|
|
26
32
|
this.fileUrls = params.fileUrls;
|
|
27
33
|
this.uow = params.uow;
|
|
@@ -39,7 +45,7 @@ var ChatServiceImpl = class {
|
|
|
39
45
|
replyToId: null,
|
|
40
46
|
assetIds: input.assetIds
|
|
41
47
|
};
|
|
42
|
-
const saved = await this.uow.run(async ({ chats, outbox }) => {
|
|
48
|
+
const saved = await this.uow.run(async ({ chats, mentions, outbox }) => {
|
|
43
49
|
await chats.lockDirectPair(orgId, input.communityId, actor.id, input.orgUserId);
|
|
44
50
|
const existing = await chats.findDirect(orgId, input.communityId, actor.id, input.orgUserId);
|
|
45
51
|
if (existing) {
|
|
@@ -51,11 +57,11 @@ var ChatServiceImpl = class {
|
|
|
51
57
|
} : user);
|
|
52
58
|
if (first) await this.assertMaySend(chats, orgId, actor, existing, present, settings, first);
|
|
53
59
|
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;
|
|
60
|
+
const written = first ? await this.writeMessage(chats, mentions, orgId, actor, existing, present, first) : null;
|
|
55
61
|
const chat = written?.chat ?? existing;
|
|
56
62
|
const events = [];
|
|
57
63
|
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));
|
|
64
|
+
if (written) events.push(this.sentEvent(orgId, actor, written.chat, written.message, written.mentioned));
|
|
59
65
|
if (events.length > 0) await outbox.append(events);
|
|
60
66
|
return {
|
|
61
67
|
chat,
|
|
@@ -83,8 +89,8 @@ var ChatServiceImpl = class {
|
|
|
83
89
|
for (const user of users) await chats.join(orgId, user);
|
|
84
90
|
const events = [this.createdEvent(orgId, actor, chat, users)];
|
|
85
91
|
if (first) {
|
|
86
|
-
const written = await this.writeMessage(chats, orgId, actor, chat, first);
|
|
87
|
-
events.push(this.sentEvent(orgId, actor, written.chat, written.message));
|
|
92
|
+
const written = await this.writeMessage(chats, mentions, orgId, actor, chat, users, first);
|
|
93
|
+
events.push(this.sentEvent(orgId, actor, written.chat, written.message, written.mentioned));
|
|
88
94
|
}
|
|
89
95
|
await outbox.append(events);
|
|
90
96
|
return {
|
|
@@ -227,14 +233,14 @@ var ChatServiceImpl = class {
|
|
|
227
233
|
await this.requireActor(orgId, actor, preview.communityId);
|
|
228
234
|
const settings = await this.requireSettings(orgId, preview.communityId);
|
|
229
235
|
if (!settings.enabled) throw new ForbiddenError("Direct messages are off");
|
|
230
|
-
const saved = await this.uow.run(async ({ chats, outbox }) => {
|
|
236
|
+
const saved = await this.uow.run(async ({ chats, mentions, outbox }) => {
|
|
231
237
|
const chat = await this.mustChat(chats, orgId, chatId);
|
|
232
238
|
const users = await chats.participants(orgId, chatId);
|
|
233
239
|
await this.assertMaySend(chats, orgId, actor, chat, users, settings, input);
|
|
234
|
-
const written = await this.writeMessage(chats, orgId, actor, chat, input);
|
|
240
|
+
const written = await this.writeMessage(chats, mentions, orgId, actor, chat, users, input);
|
|
235
241
|
const events = [];
|
|
236
242
|
if (written.opened) events.push(this.updatedEvent(orgId, actor, written.chat, users));
|
|
237
|
-
events.push(this.sentEvent(orgId, actor, written.chat, written.message));
|
|
243
|
+
events.push(this.sentEvent(orgId, actor, written.chat, written.message, written.mentioned));
|
|
238
244
|
await outbox.append(events);
|
|
239
245
|
return written.message;
|
|
240
246
|
});
|
|
@@ -252,11 +258,30 @@ var ChatServiceImpl = class {
|
|
|
252
258
|
if (message.orgUserId !== actor.id) throw new ForbiddenError("You can change only your own message");
|
|
253
259
|
if (message.deletedAt) throw new ConflictError("This message was deleted");
|
|
254
260
|
if (message.body === body) return this.viewOf(orgId, actor, message);
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
261
|
+
const now = /* @__PURE__ */ new Date();
|
|
262
|
+
const saved = await this.uow.run(async ({ chats, mentions, outbox }) => {
|
|
263
|
+
const next = await chats.updateMessage(orgId, messageId, {
|
|
264
|
+
body,
|
|
265
|
+
editedAt: now
|
|
266
|
+
});
|
|
267
|
+
if (!next) throw new NotFoundError("Message", messageId);
|
|
268
|
+
const chat = await this.mustChat(chats, orgId, next.chatId);
|
|
269
|
+
const users = await chats.participants(orgId, next.chatId);
|
|
270
|
+
const candidates = this.mentionable(body, users);
|
|
271
|
+
const added = await mentions.sync(orgId, "message", messageId, candidates, now);
|
|
272
|
+
await outbox.append([discussionEvents.messageUpdated.make({
|
|
273
|
+
orgId,
|
|
274
|
+
data: {
|
|
275
|
+
...next,
|
|
276
|
+
mentionedOrgUserIds: keepAllowed(candidates, added)
|
|
277
|
+
},
|
|
278
|
+
metadata: {
|
|
279
|
+
...this.meta(actor),
|
|
280
|
+
chatStatus: chat.status
|
|
281
|
+
}
|
|
282
|
+
})]);
|
|
283
|
+
return next;
|
|
258
284
|
});
|
|
259
|
-
if (!saved) throw new NotFoundError("Message", messageId);
|
|
260
285
|
this.logger.info("message edited", {
|
|
261
286
|
orgId,
|
|
262
287
|
messageId,
|
|
@@ -273,9 +298,10 @@ var ChatServiceImpl = class {
|
|
|
273
298
|
}
|
|
274
299
|
if (message.deletedAt) throw new ConflictError("This message was deleted");
|
|
275
300
|
const now = /* @__PURE__ */ new Date();
|
|
276
|
-
const saved = await this.uow.run(async ({ chats, outbox }) => {
|
|
301
|
+
const saved = await this.uow.run(async ({ chats, mentions, outbox }) => {
|
|
277
302
|
const next = await chats.updateMessage(orgId, messageId, { deletedAt: now });
|
|
278
303
|
if (!next) throw new NotFoundError("Message", messageId);
|
|
304
|
+
await mentions.removeItem(orgId, "message", messageId);
|
|
279
305
|
await chats.resolveFlags(orgId, messageId, {
|
|
280
306
|
resolvedAt: now,
|
|
281
307
|
resolvedBy: actor.id,
|
|
@@ -299,7 +325,20 @@ var ChatServiceImpl = class {
|
|
|
299
325
|
const message = await this.mustMessage(this.repo, orgId, messageId);
|
|
300
326
|
await this.requireActor(orgId, actor, message.communityId);
|
|
301
327
|
await this.requireActive(this.repo, orgId, message.chatId, actor.id);
|
|
302
|
-
await this.
|
|
328
|
+
await this.uow.run(async ({ chats, outbox }) => {
|
|
329
|
+
if (!await chats.setReaction(orgId, message.communityId, messageId, actor.id, emoji) || emoji === null) return;
|
|
330
|
+
await outbox.append([discussionEvents.messageReactionAdded.make({
|
|
331
|
+
orgId,
|
|
332
|
+
data: {
|
|
333
|
+
messageId,
|
|
334
|
+
chatId: message.chatId,
|
|
335
|
+
communityId: message.communityId,
|
|
336
|
+
orgUserId: actor.id,
|
|
337
|
+
reaction: emoji
|
|
338
|
+
},
|
|
339
|
+
metadata: this.meta(actor)
|
|
340
|
+
})]);
|
|
341
|
+
});
|
|
303
342
|
this.logger.debug("message reaction set", {
|
|
304
343
|
orgId,
|
|
305
344
|
messageId
|
|
@@ -318,26 +357,6 @@ var ChatServiceImpl = class {
|
|
|
318
357
|
messageId
|
|
319
358
|
});
|
|
320
359
|
}
|
|
321
|
-
async mute(orgId, actor, chatId, until) {
|
|
322
|
-
const chat = await this.mustChat(this.repo, orgId, chatId);
|
|
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
360
|
async block(orgId, actor, communityId, orgUserId) {
|
|
342
361
|
if (actor.id === orgUserId) throw new InvalidInputError("You cannot block yourself");
|
|
343
362
|
await this.requireSettings(orgId, communityId);
|
|
@@ -434,12 +453,11 @@ var ChatServiceImpl = class {
|
|
|
434
453
|
await this.requireActor(orgId, actor, communityId);
|
|
435
454
|
const now = /* @__PURE__ */ new Date();
|
|
436
455
|
const page = await this.repo.listForUser(orgId, communityId, actor.id, query);
|
|
456
|
+
const subscribed = await this.subscriptions.forViewer(orgId, actor.id, "chat", page.rows.map((item) => item.chat.id));
|
|
457
|
+
const subscriptionOf = new Map(subscribed.map((row) => [row.itemId, row]));
|
|
437
458
|
const items = page.rows.map((item) => ({
|
|
438
459
|
...item,
|
|
439
|
-
|
|
440
|
-
muted: false,
|
|
441
|
-
mutedUntil: null
|
|
442
|
-
} : {},
|
|
460
|
+
subscription: activeSubscription(subscriptionOf.get(item.chat.id), now),
|
|
443
461
|
lastMessage: item.lastMessage && item.lastMessage.deletedAt !== null && !isStaffRole(actor.role) ? {
|
|
444
462
|
...item.lastMessage,
|
|
445
463
|
body: ""
|
|
@@ -526,6 +544,12 @@ var ChatServiceImpl = class {
|
|
|
526
544
|
return summary.chat.kind === "group" ? [[summary.chat.id, "Group chat"]] : [];
|
|
527
545
|
}));
|
|
528
546
|
}
|
|
547
|
+
unreadInChat(orgId, chatId, orgUserId) {
|
|
548
|
+
return this.repo.unreadFor(orgId, chatId, orgUserId);
|
|
549
|
+
}
|
|
550
|
+
findMessage(orgId, messageId) {
|
|
551
|
+
return this.repo.findMessage(orgId, messageId);
|
|
552
|
+
}
|
|
529
553
|
async latestMessageBody(orgId, chatId) {
|
|
530
554
|
return (await this.repo.listMessages(orgId, chatId, {
|
|
531
555
|
page: 1,
|
|
@@ -612,15 +636,21 @@ var ChatServiceImpl = class {
|
|
|
612
636
|
}
|
|
613
637
|
await this.requireOwnedImages(orgId, actor.id, input.assetIds);
|
|
614
638
|
}
|
|
615
|
-
|
|
639
|
+
mentionable(body, users) {
|
|
640
|
+
return keepAllowed(mentionTokens(body), users.filter((user) => user.leftAt === null).map((user) => user.orgUserId));
|
|
641
|
+
}
|
|
642
|
+
async writeMessage(chats, mentions, orgId, actor, chat, users, input) {
|
|
616
643
|
const opened = chat.kind === "direct" && chat.status === "pending" && chat.createdBy !== actor.id;
|
|
617
644
|
const current = opened ? await this.mustUpdate(chats, orgId, chat.id, { status: "open" }) : chat;
|
|
618
645
|
const message = await chats.insertMessage(orgId, this.newMessage(orgId, current, actor, input, /* @__PURE__ */ new Date()));
|
|
619
646
|
if (input.assetIds.length > 0) await chats.attach(orgId, current.communityId, message.id, input.assetIds);
|
|
647
|
+
const mentioned = this.mentionable(input.body, users);
|
|
648
|
+
await mentions.sync(orgId, "message", message.id, mentioned, message.createdAt);
|
|
620
649
|
return {
|
|
621
650
|
chat: current,
|
|
622
651
|
message,
|
|
623
|
-
opened
|
|
652
|
+
opened,
|
|
653
|
+
mentioned
|
|
624
654
|
};
|
|
625
655
|
}
|
|
626
656
|
assertMayMessage(settings, actor, role) {
|
|
@@ -686,8 +716,6 @@ var ChatServiceImpl = class {
|
|
|
686
716
|
joinedAt: now,
|
|
687
717
|
leftAt: null,
|
|
688
718
|
lastReadMessageId: null,
|
|
689
|
-
muted: false,
|
|
690
|
-
mutedUntil: null,
|
|
691
719
|
createdAt: now,
|
|
692
720
|
updatedAt: now
|
|
693
721
|
};
|
|
@@ -745,10 +773,13 @@ var ChatServiceImpl = class {
|
|
|
745
773
|
metadata: this.meta(actor)
|
|
746
774
|
});
|
|
747
775
|
}
|
|
748
|
-
sentEvent(orgId, actor, chat, message) {
|
|
776
|
+
sentEvent(orgId, actor, chat, message, mentionedOrgUserIds) {
|
|
749
777
|
return discussionEvents.messageSent.make({
|
|
750
778
|
orgId,
|
|
751
|
-
data:
|
|
779
|
+
data: {
|
|
780
|
+
...message,
|
|
781
|
+
mentionedOrgUserIds
|
|
782
|
+
},
|
|
752
783
|
metadata: {
|
|
753
784
|
...this.meta(actor),
|
|
754
785
|
chatStatus: chat.status
|
|
@@ -762,10 +793,11 @@ var ChatServiceImpl = class {
|
|
|
762
793
|
async withSenders(orgId, actor, messages) {
|
|
763
794
|
if (messages.length === 0) return [];
|
|
764
795
|
const messageIds = messages.map((message) => message.id);
|
|
765
|
-
const [records, attached, reactions] = await Promise.all([
|
|
796
|
+
const [records, attached, reactions, mentioned] = await Promise.all([
|
|
766
797
|
this.profiles.authorsOf(orgId, [...new Set(messages.map((message) => message.orgUserId))]),
|
|
767
798
|
this.repo.attachmentsOf(orgId, messageIds),
|
|
768
|
-
this.repo.reactionsOf(orgId, messageIds, actor.id)
|
|
799
|
+
this.repo.reactionsOf(orgId, messageIds, actor.id),
|
|
800
|
+
mentionViews(this.mentions, this.profiles, orgId, "message", messageIds)
|
|
769
801
|
]);
|
|
770
802
|
const shown = (message) => message.deletedAt !== null && !isStaffRole(actor.role) ? [] : attached[message.id] ?? [];
|
|
771
803
|
return messages.map((message) => {
|
|
@@ -780,7 +812,8 @@ var ChatServiceImpl = class {
|
|
|
780
812
|
url: this.fileUrls.messageAttachment(message.chatId, message.id, assetId)
|
|
781
813
|
})),
|
|
782
814
|
reactions: counts?.reactions ?? {},
|
|
783
|
-
viewerReaction: counts?.viewerReaction ?? null
|
|
815
|
+
viewerReaction: counts?.viewerReaction ?? null,
|
|
816
|
+
mentions: message.deletedAt !== null && !isStaffRole(actor.role) ? [] : mentioned[message.id] ?? []
|
|
784
817
|
};
|
|
785
818
|
});
|
|
786
819
|
}
|