@ai-matrx/messaging 0.13.1 → 0.15.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/CHANGELOG.md +59 -0
- package/README.md +5 -1
- package/dist/index.cjs +59 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -9
- package/dist/index.d.ts +37 -9
- package/dist/index.js +59 -18
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +121 -33
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +47 -10
- package/dist/react.d.ts +47 -10
- package/dist/react.js +121 -33
- package/dist/react.js.map +1 -1
- package/package.json +4 -3
package/dist/index.d.cts
CHANGED
|
@@ -207,7 +207,11 @@ interface Page<TItem, TCursor> {
|
|
|
207
207
|
/** What the host injects. Identity ONLY — every hard part is in the package. */
|
|
208
208
|
interface MessagingIdentity {
|
|
209
209
|
readonly userId: UserId;
|
|
210
|
-
|
|
210
|
+
/**
|
|
211
|
+
* The active organization, or `null` when none is selected. Reads never
|
|
212
|
+
* depend on it; it is only where a NEW conversation lands.
|
|
213
|
+
*/
|
|
214
|
+
readonly organizationId: OrganizationId | null;
|
|
211
215
|
}
|
|
212
216
|
interface DraftMessage {
|
|
213
217
|
readonly conversationId: ConversationId;
|
|
@@ -628,7 +632,9 @@ declare function createOutbox(options: OutboxOptions): Outbox;
|
|
|
628
632
|
* errors in 0.6 seconds. A missing session is a NORMAL lifecycle moment — it
|
|
629
633
|
* warns and retries; it is never a red error.
|
|
630
634
|
*/
|
|
631
|
-
type MessagingErrorCode = "session-unavailable" | "contention" | "forbidden" | "not-found" | "conflict" | "transport" | "invalid-response" | "misconfigured"
|
|
635
|
+
type MessagingErrorCode = "session-unavailable" | "contention" | "forbidden" | "not-found" | "conflict" | "transport" | "invalid-response" | "misconfigured"
|
|
636
|
+
/** A NEW conversation needs an organization to land in and none is selected. */
|
|
637
|
+
| "organization-required" | "unknown";
|
|
632
638
|
declare class MessagingError extends Error {
|
|
633
639
|
readonly code: MessagingErrorCode;
|
|
634
640
|
/** The remedy. Nothing fails silently, and nothing fails without saying what to do. */
|
|
@@ -920,15 +926,21 @@ interface RepositoryOptions {
|
|
|
920
926
|
*/
|
|
921
927
|
client: MessagingSupabaseClient;
|
|
922
928
|
/**
|
|
923
|
-
* The active organization
|
|
924
|
-
*
|
|
929
|
+
* The active organization, or `null`/omitted when none is selected.
|
|
930
|
+
*
|
|
931
|
+
* 🚨 READS NEVER NEED IT (access belongs to the person, 2026-09-23). Every
|
|
932
|
+
* read here is answered by RLS as the session's user; the organization is
|
|
933
|
+
* only the DESTINATION of a NEW conversation. Without one, starting a
|
|
934
|
+
* conversation is refused with `organization-required` (the host asks the
|
|
935
|
+
* person to choose), and a message or member added to an EXISTING
|
|
936
|
+
* conversation is stamped with that conversation's own organization.
|
|
925
937
|
*
|
|
926
938
|
* 🚨 THERE IS NO `userId` HERE, AND THERE WILL NOT BE ONE (DD-241). Who is
|
|
927
939
|
* acting is read from `client`'s own session — see `session-identity.ts` for
|
|
928
940
|
* the class that closed and why a handed-in id could not be made safe by
|
|
929
941
|
* convention. A caller that still passes `identity` is IGNORED and told so.
|
|
930
942
|
*/
|
|
931
|
-
organizationId
|
|
943
|
+
organizationId?: string | null | undefined;
|
|
932
944
|
/**
|
|
933
945
|
* INTERNAL. The React provider already runs one session reader for its own
|
|
934
946
|
* render loop and hands it here so the two cannot answer differently and so
|
|
@@ -949,8 +961,11 @@ interface RepositoryOptions {
|
|
|
949
961
|
now?: (() => number) | undefined;
|
|
950
962
|
}
|
|
951
963
|
interface MessagingRepository {
|
|
952
|
-
/**
|
|
953
|
-
|
|
964
|
+
/**
|
|
965
|
+
* The active organization new conversations land in, or `null` when none is
|
|
966
|
+
* selected — reads work either way.
|
|
967
|
+
*/
|
|
968
|
+
readonly organizationId: OrganizationId | null;
|
|
954
969
|
/**
|
|
955
970
|
* Who the session says is acting — awaited, because the first `getSession()`
|
|
956
971
|
* is a real read. Rejects with `session-unavailable` when nobody is signed
|
|
@@ -1107,11 +1122,18 @@ interface MessagingSnapshot {
|
|
|
1107
1122
|
} | null;
|
|
1108
1123
|
/** Conversations whose last open failed, keyed by id. Cleared on the next open. */
|
|
1109
1124
|
readonly conversationReadFailures: ReadonlyMap<ConversationId, ConversationReadFailure>;
|
|
1125
|
+
/**
|
|
1126
|
+
* The inbox read FAILED (and no later read succeeded). Without it a failed
|
|
1127
|
+
* first read left `hasLoadedConversations` false forever — a skeleton that
|
|
1128
|
+
* never resolves is a screen telling a lie (RC-B12 round 11).
|
|
1129
|
+
*/
|
|
1130
|
+
readonly conversationsLoadError: string | null;
|
|
1110
1131
|
}
|
|
1111
1132
|
interface MessagingStore {
|
|
1112
1133
|
snapshot(): MessagingSnapshot;
|
|
1113
1134
|
subscribe(listener: (snapshot: MessagingSnapshot) => void): () => void;
|
|
1114
1135
|
setConversations(items: readonly ConversationSummary[], hasMore: boolean): void;
|
|
1136
|
+
setConversationsLoadError(message: string | null): void;
|
|
1115
1137
|
/**
|
|
1116
1138
|
* Switch the archive axis. Clears the list and marks it UNLOADED: the rows
|
|
1117
1139
|
* on screen belong to the old filter, and leaving them there while the new
|
|
@@ -1221,6 +1243,8 @@ interface MessagingEngine {
|
|
|
1221
1243
|
* refreshes the archived count the reveal label prints.
|
|
1222
1244
|
*/
|
|
1223
1245
|
setArchiveFilter(next: MessagingArchiveFilter): Promise<void>;
|
|
1246
|
+
/** Read the inbox again (the failed-read view's Try again). Never throws; a failure lands in the snapshot. */
|
|
1247
|
+
refreshInbox(): Promise<void>;
|
|
1224
1248
|
/** Re-read how many archived conversations exist, for the reveal's label. */
|
|
1225
1249
|
refreshArchivedCount(): Promise<void>;
|
|
1226
1250
|
/** Open a conversation: load its thread and subscribe to its channel. */
|
|
@@ -1311,9 +1335,13 @@ declare function formatLastSeen(lastSeenMs: number | null, now?: number): string
|
|
|
1311
1335
|
|
|
1312
1336
|
declare function projectUserSummary(row: Record<string, unknown>): UserSummary;
|
|
1313
1337
|
declare function projectMessageAction(value: unknown): MessageAction | null;
|
|
1314
|
-
declare function projectMessage(row: Record<string, unknown>,
|
|
1338
|
+
declare function projectMessage(row: Record<string, unknown>,
|
|
1339
|
+
/** The active organization, or `null` when none is selected (reads never need one). */
|
|
1340
|
+
fallbackOrganizationId: OrganizationId | null): Message;
|
|
1315
1341
|
declare function projectParticipantRole(value: unknown): ParticipantRole;
|
|
1316
|
-
declare function projectConversationSummary(row: Record<string, unknown>, viewerId: UserId,
|
|
1342
|
+
declare function projectConversationSummary(row: Record<string, unknown>, viewerId: UserId,
|
|
1343
|
+
/** The active organization, or `null` when none is selected (reads never need one). */
|
|
1344
|
+
fallbackOrganizationId: OrganizationId | null): ConversationSummary;
|
|
1317
1345
|
|
|
1318
1346
|
/**
|
|
1319
1347
|
* MATRX REFERENCES — a message can name a platform entity, and everything it
|
package/dist/index.d.ts
CHANGED
|
@@ -207,7 +207,11 @@ interface Page<TItem, TCursor> {
|
|
|
207
207
|
/** What the host injects. Identity ONLY — every hard part is in the package. */
|
|
208
208
|
interface MessagingIdentity {
|
|
209
209
|
readonly userId: UserId;
|
|
210
|
-
|
|
210
|
+
/**
|
|
211
|
+
* The active organization, or `null` when none is selected. Reads never
|
|
212
|
+
* depend on it; it is only where a NEW conversation lands.
|
|
213
|
+
*/
|
|
214
|
+
readonly organizationId: OrganizationId | null;
|
|
211
215
|
}
|
|
212
216
|
interface DraftMessage {
|
|
213
217
|
readonly conversationId: ConversationId;
|
|
@@ -628,7 +632,9 @@ declare function createOutbox(options: OutboxOptions): Outbox;
|
|
|
628
632
|
* errors in 0.6 seconds. A missing session is a NORMAL lifecycle moment — it
|
|
629
633
|
* warns and retries; it is never a red error.
|
|
630
634
|
*/
|
|
631
|
-
type MessagingErrorCode = "session-unavailable" | "contention" | "forbidden" | "not-found" | "conflict" | "transport" | "invalid-response" | "misconfigured"
|
|
635
|
+
type MessagingErrorCode = "session-unavailable" | "contention" | "forbidden" | "not-found" | "conflict" | "transport" | "invalid-response" | "misconfigured"
|
|
636
|
+
/** A NEW conversation needs an organization to land in and none is selected. */
|
|
637
|
+
| "organization-required" | "unknown";
|
|
632
638
|
declare class MessagingError extends Error {
|
|
633
639
|
readonly code: MessagingErrorCode;
|
|
634
640
|
/** The remedy. Nothing fails silently, and nothing fails without saying what to do. */
|
|
@@ -920,15 +926,21 @@ interface RepositoryOptions {
|
|
|
920
926
|
*/
|
|
921
927
|
client: MessagingSupabaseClient;
|
|
922
928
|
/**
|
|
923
|
-
* The active organization
|
|
924
|
-
*
|
|
929
|
+
* The active organization, or `null`/omitted when none is selected.
|
|
930
|
+
*
|
|
931
|
+
* 🚨 READS NEVER NEED IT (access belongs to the person, 2026-09-23). Every
|
|
932
|
+
* read here is answered by RLS as the session's user; the organization is
|
|
933
|
+
* only the DESTINATION of a NEW conversation. Without one, starting a
|
|
934
|
+
* conversation is refused with `organization-required` (the host asks the
|
|
935
|
+
* person to choose), and a message or member added to an EXISTING
|
|
936
|
+
* conversation is stamped with that conversation's own organization.
|
|
925
937
|
*
|
|
926
938
|
* 🚨 THERE IS NO `userId` HERE, AND THERE WILL NOT BE ONE (DD-241). Who is
|
|
927
939
|
* acting is read from `client`'s own session — see `session-identity.ts` for
|
|
928
940
|
* the class that closed and why a handed-in id could not be made safe by
|
|
929
941
|
* convention. A caller that still passes `identity` is IGNORED and told so.
|
|
930
942
|
*/
|
|
931
|
-
organizationId
|
|
943
|
+
organizationId?: string | null | undefined;
|
|
932
944
|
/**
|
|
933
945
|
* INTERNAL. The React provider already runs one session reader for its own
|
|
934
946
|
* render loop and hands it here so the two cannot answer differently and so
|
|
@@ -949,8 +961,11 @@ interface RepositoryOptions {
|
|
|
949
961
|
now?: (() => number) | undefined;
|
|
950
962
|
}
|
|
951
963
|
interface MessagingRepository {
|
|
952
|
-
/**
|
|
953
|
-
|
|
964
|
+
/**
|
|
965
|
+
* The active organization new conversations land in, or `null` when none is
|
|
966
|
+
* selected — reads work either way.
|
|
967
|
+
*/
|
|
968
|
+
readonly organizationId: OrganizationId | null;
|
|
954
969
|
/**
|
|
955
970
|
* Who the session says is acting — awaited, because the first `getSession()`
|
|
956
971
|
* is a real read. Rejects with `session-unavailable` when nobody is signed
|
|
@@ -1107,11 +1122,18 @@ interface MessagingSnapshot {
|
|
|
1107
1122
|
} | null;
|
|
1108
1123
|
/** Conversations whose last open failed, keyed by id. Cleared on the next open. */
|
|
1109
1124
|
readonly conversationReadFailures: ReadonlyMap<ConversationId, ConversationReadFailure>;
|
|
1125
|
+
/**
|
|
1126
|
+
* The inbox read FAILED (and no later read succeeded). Without it a failed
|
|
1127
|
+
* first read left `hasLoadedConversations` false forever — a skeleton that
|
|
1128
|
+
* never resolves is a screen telling a lie (RC-B12 round 11).
|
|
1129
|
+
*/
|
|
1130
|
+
readonly conversationsLoadError: string | null;
|
|
1110
1131
|
}
|
|
1111
1132
|
interface MessagingStore {
|
|
1112
1133
|
snapshot(): MessagingSnapshot;
|
|
1113
1134
|
subscribe(listener: (snapshot: MessagingSnapshot) => void): () => void;
|
|
1114
1135
|
setConversations(items: readonly ConversationSummary[], hasMore: boolean): void;
|
|
1136
|
+
setConversationsLoadError(message: string | null): void;
|
|
1115
1137
|
/**
|
|
1116
1138
|
* Switch the archive axis. Clears the list and marks it UNLOADED: the rows
|
|
1117
1139
|
* on screen belong to the old filter, and leaving them there while the new
|
|
@@ -1221,6 +1243,8 @@ interface MessagingEngine {
|
|
|
1221
1243
|
* refreshes the archived count the reveal label prints.
|
|
1222
1244
|
*/
|
|
1223
1245
|
setArchiveFilter(next: MessagingArchiveFilter): Promise<void>;
|
|
1246
|
+
/** Read the inbox again (the failed-read view's Try again). Never throws; a failure lands in the snapshot. */
|
|
1247
|
+
refreshInbox(): Promise<void>;
|
|
1224
1248
|
/** Re-read how many archived conversations exist, for the reveal's label. */
|
|
1225
1249
|
refreshArchivedCount(): Promise<void>;
|
|
1226
1250
|
/** Open a conversation: load its thread and subscribe to its channel. */
|
|
@@ -1311,9 +1335,13 @@ declare function formatLastSeen(lastSeenMs: number | null, now?: number): string
|
|
|
1311
1335
|
|
|
1312
1336
|
declare function projectUserSummary(row: Record<string, unknown>): UserSummary;
|
|
1313
1337
|
declare function projectMessageAction(value: unknown): MessageAction | null;
|
|
1314
|
-
declare function projectMessage(row: Record<string, unknown>,
|
|
1338
|
+
declare function projectMessage(row: Record<string, unknown>,
|
|
1339
|
+
/** The active organization, or `null` when none is selected (reads never need one). */
|
|
1340
|
+
fallbackOrganizationId: OrganizationId | null): Message;
|
|
1315
1341
|
declare function projectParticipantRole(value: unknown): ParticipantRole;
|
|
1316
|
-
declare function projectConversationSummary(row: Record<string, unknown>, viewerId: UserId,
|
|
1342
|
+
declare function projectConversationSummary(row: Record<string, unknown>, viewerId: UserId,
|
|
1343
|
+
/** The active organization, or `null` when none is selected (reads never need one). */
|
|
1344
|
+
fallbackOrganizationId: OrganizationId | null): ConversationSummary;
|
|
1317
1345
|
|
|
1318
1346
|
/**
|
|
1319
1347
|
* MATRX REFERENCES — a message can name a platform entity, and everything it
|
package/dist/index.js
CHANGED
|
@@ -861,7 +861,7 @@ function projectMessage(row, fallbackOrganizationId) {
|
|
|
861
861
|
id: requiredStr(row, "id", operation),
|
|
862
862
|
conversationId: requiredStr(row, "conversation_id", operation),
|
|
863
863
|
senderId: requiredStr(row, "sender_id", operation),
|
|
864
|
-
organizationId: str(row, "organization_id") ?? fallbackOrganizationId,
|
|
864
|
+
organizationId: str(row, "organization_id") ?? fallbackOrganizationId ?? "",
|
|
865
865
|
content: typeof row["content"] === "string" ? row["content"] : "",
|
|
866
866
|
kind: MESSAGE_KINDS.has(kindRaw) ? kindRaw : "text",
|
|
867
867
|
// A row read from the DB is at least `sent`. `sending`/`failed` are outbox
|
|
@@ -909,7 +909,7 @@ function projectConversationSummary(row, viewerId, fallbackOrganizationId) {
|
|
|
909
909
|
groupName,
|
|
910
910
|
groupImageUrl,
|
|
911
911
|
createdBy: createdBy === null ? null : createdBy,
|
|
912
|
-
organizationId: str(row, "organization_id") ?? fallbackOrganizationId,
|
|
912
|
+
organizationId: str(row, "organization_id") ?? fallbackOrganizationId ?? "",
|
|
913
913
|
createdAt,
|
|
914
914
|
updatedAt,
|
|
915
915
|
metadata: jsonObject(row["metadata"])
|
|
@@ -975,6 +975,7 @@ function createMessagingStore() {
|
|
|
975
975
|
let threads = /* @__PURE__ */ new Map();
|
|
976
976
|
let activeConversationId = null;
|
|
977
977
|
let conversationReadFailures = /* @__PURE__ */ new Map();
|
|
978
|
+
let conversationsLoadError = null;
|
|
978
979
|
const listeners = /* @__PURE__ */ new Set();
|
|
979
980
|
let cached = null;
|
|
980
981
|
function snapshot() {
|
|
@@ -988,7 +989,8 @@ function createMessagingStore() {
|
|
|
988
989
|
totalUnreadConversations: conversations.filter((item) => item.unreadCount > 0).length,
|
|
989
990
|
archiveFilter,
|
|
990
991
|
archivedCount,
|
|
991
|
-
conversationReadFailures
|
|
992
|
+
conversationReadFailures,
|
|
993
|
+
conversationsLoadError
|
|
992
994
|
};
|
|
993
995
|
return cached;
|
|
994
996
|
}
|
|
@@ -1033,6 +1035,12 @@ function createMessagingStore() {
|
|
|
1033
1035
|
conversations = normalizeConversations(items);
|
|
1034
1036
|
hasMoreConversations = hasMore;
|
|
1035
1037
|
hasLoadedConversations = true;
|
|
1038
|
+
conversationsLoadError = null;
|
|
1039
|
+
emit();
|
|
1040
|
+
},
|
|
1041
|
+
setConversationsLoadError(message) {
|
|
1042
|
+
if (message === conversationsLoadError) return;
|
|
1043
|
+
conversationsLoadError = message;
|
|
1036
1044
|
emit();
|
|
1037
1045
|
},
|
|
1038
1046
|
setArchiveFilter(next) {
|
|
@@ -1242,7 +1250,11 @@ function createMessagingEngine(options) {
|
|
|
1242
1250
|
optimisticMessage({
|
|
1243
1251
|
conversationId: entry.draft.conversationId,
|
|
1244
1252
|
senderId: identity.userId,
|
|
1245
|
-
|
|
1253
|
+
// Client-only until the confirmed row replaces it: the active
|
|
1254
|
+
// organization, else the conversation's own.
|
|
1255
|
+
organizationId: identity.organizationId ?? store.snapshot().conversations.find(
|
|
1256
|
+
(item) => item.conversation.id === entry.draft.conversationId
|
|
1257
|
+
)?.conversation.organizationId ?? "",
|
|
1246
1258
|
content: entry.draft.content,
|
|
1247
1259
|
clientMessageId: entry.clientMessageId,
|
|
1248
1260
|
...entry.draft.kind !== void 0 ? { kind: entry.draft.kind } : {},
|
|
@@ -1261,10 +1273,20 @@ function createMessagingEngine(options) {
|
|
|
1261
1273
|
}
|
|
1262
1274
|
async function reloadInbox() {
|
|
1263
1275
|
if (disposed) return;
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1276
|
+
let page;
|
|
1277
|
+
try {
|
|
1278
|
+
page = await repository.listConversations({
|
|
1279
|
+
limit: conversationPageSize,
|
|
1280
|
+
archived: store.snapshot().archiveFilter
|
|
1281
|
+
});
|
|
1282
|
+
} catch (error) {
|
|
1283
|
+
if (!disposed) {
|
|
1284
|
+
store.setConversationsLoadError(
|
|
1285
|
+
error instanceof Error ? error.message : typeof error === "string" ? error : "The conversation list could not be read."
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
throw error;
|
|
1289
|
+
}
|
|
1268
1290
|
if (disposed) return;
|
|
1269
1291
|
conversationCursor = page.nextCursor;
|
|
1270
1292
|
store.setConversations(page.items, page.hasMore);
|
|
@@ -1349,6 +1371,13 @@ function createMessagingEngine(options) {
|
|
|
1349
1371
|
}
|
|
1350
1372
|
});
|
|
1351
1373
|
},
|
|
1374
|
+
async refreshInbox() {
|
|
1375
|
+
try {
|
|
1376
|
+
await reloadInbox();
|
|
1377
|
+
} catch (error) {
|
|
1378
|
+
reportError(error, "inboxRefresh");
|
|
1379
|
+
}
|
|
1380
|
+
},
|
|
1352
1381
|
async setArchiveFilter(next) {
|
|
1353
1382
|
if (next === store.snapshot().archiveFilter) return;
|
|
1354
1383
|
store.setArchiveFilter(next);
|
|
@@ -1774,12 +1803,15 @@ var RPCS = {
|
|
|
1774
1803
|
};
|
|
1775
1804
|
var MESSAGE_COLUMNS = "id,conversation_id,sender_id,organization_id,content,message_type,status,reply_to_id,client_message_id,action_data,media_url,media_thumbnail_url,media_metadata,created_at,edited_at,deleted_at,deleted_for_everyone,metadata";
|
|
1776
1805
|
var CONVERSATION_COLUMNS = "id,type,group_name,group_image_url,created_by,organization_id,created_at,updated_at,metadata";
|
|
1806
|
+
function optionalOrg(organizationId) {
|
|
1807
|
+
return typeof organizationId === "string" && organizationId.length > 0 ? organizationId : null;
|
|
1808
|
+
}
|
|
1777
1809
|
function requireOrg(organizationId, operation) {
|
|
1778
|
-
if (
|
|
1810
|
+
if (organizationId === null) {
|
|
1779
1811
|
throw new MessagingError(
|
|
1780
|
-
"
|
|
1781
|
-
`${operation}: no
|
|
1782
|
-
"
|
|
1812
|
+
"organization-required",
|
|
1813
|
+
`${operation}: no organization selected`,
|
|
1814
|
+
"A new conversation is filed under one organization. Ask the person to choose one, then pass it as organizationId on <MessagingProvider>. Reading and replying to existing conversations never needs it."
|
|
1783
1815
|
);
|
|
1784
1816
|
}
|
|
1785
1817
|
return organizationId;
|
|
@@ -1791,7 +1823,7 @@ function sleep(ms) {
|
|
|
1791
1823
|
return new Promise((resolve) => setTimeout(resolve, jittered));
|
|
1792
1824
|
}
|
|
1793
1825
|
function createMessagingRepository(options) {
|
|
1794
|
-
const org =
|
|
1826
|
+
const org = optionalOrg(options.organizationId);
|
|
1795
1827
|
const handedIn = options.identity;
|
|
1796
1828
|
if (handedIn !== void 0 && handedIn !== null && handedIn.userId !== void 0) {
|
|
1797
1829
|
options.onDiagnostic?.(
|
|
@@ -1861,6 +1893,11 @@ function createMessagingRepository(options) {
|
|
|
1861
1893
|
});
|
|
1862
1894
|
return found;
|
|
1863
1895
|
}
|
|
1896
|
+
async function organizationOfExisting(conversationId) {
|
|
1897
|
+
if (org !== null) return org;
|
|
1898
|
+
const conversation = await repository.getConversation(conversationId);
|
|
1899
|
+
return conversation.organizationId;
|
|
1900
|
+
}
|
|
1864
1901
|
const repository = {
|
|
1865
1902
|
organizationId: org,
|
|
1866
1903
|
actingUserId,
|
|
@@ -1993,6 +2030,7 @@ function createMessagingRepository(options) {
|
|
|
1993
2030
|
},
|
|
1994
2031
|
async getOrCreateDirectConversation(otherUserId) {
|
|
1995
2032
|
const operation = "getOrCreateDirectConversation";
|
|
2033
|
+
const destination = requireOrg(org, operation);
|
|
1996
2034
|
const me = await actingUser(operation);
|
|
1997
2035
|
const id = await withSessionRetry(
|
|
1998
2036
|
operation,
|
|
@@ -2004,7 +2042,7 @@ function createMessagingRepository(options) {
|
|
|
2004
2042
|
// not a preference.
|
|
2005
2043
|
p_user1_id: me,
|
|
2006
2044
|
p_user2_id: otherUserId,
|
|
2007
|
-
p_organization_id:
|
|
2045
|
+
p_organization_id: destination
|
|
2008
2046
|
},
|
|
2009
2047
|
operation
|
|
2010
2048
|
)
|
|
@@ -2016,13 +2054,14 @@ function createMessagingRepository(options) {
|
|
|
2016
2054
|
},
|
|
2017
2055
|
async createGroupConversation({ name, memberIds }) {
|
|
2018
2056
|
const operation = "createGroupConversation";
|
|
2057
|
+
const destination = requireOrg(org, operation);
|
|
2019
2058
|
const me = await actingUser(operation);
|
|
2020
2059
|
const { data, error } = await withSessionRetry(
|
|
2021
2060
|
operation,
|
|
2022
2061
|
async () => db().from(TABLES.conversations).insert({
|
|
2023
2062
|
type: "group",
|
|
2024
2063
|
group_name: name,
|
|
2025
|
-
organization_id:
|
|
2064
|
+
organization_id: destination,
|
|
2026
2065
|
created_by: me
|
|
2027
2066
|
}).select("id").single()
|
|
2028
2067
|
);
|
|
@@ -2037,7 +2076,7 @@ function createMessagingRepository(options) {
|
|
|
2037
2076
|
conversation_id: conversationId,
|
|
2038
2077
|
user_id: userId,
|
|
2039
2078
|
role: userId === me ? "owner" : "member",
|
|
2040
|
-
organization_id:
|
|
2079
|
+
organization_id: destination,
|
|
2041
2080
|
created_by: me
|
|
2042
2081
|
}))
|
|
2043
2082
|
);
|
|
@@ -2047,10 +2086,11 @@ function createMessagingRepository(options) {
|
|
|
2047
2086
|
async insertMessage(draft, clientMessageId) {
|
|
2048
2087
|
const operation = "insertMessage";
|
|
2049
2088
|
const me = await actingUser(operation);
|
|
2089
|
+
const destination = await organizationOfExisting(draft.conversationId);
|
|
2050
2090
|
const { data, error } = await db().from(TABLES.messages).insert({
|
|
2051
2091
|
conversation_id: draft.conversationId,
|
|
2052
2092
|
sender_id: me,
|
|
2053
|
-
organization_id:
|
|
2093
|
+
organization_id: destination,
|
|
2054
2094
|
created_by: me,
|
|
2055
2095
|
content: draft.content,
|
|
2056
2096
|
message_type: draft.kind ?? "text",
|
|
@@ -2111,12 +2151,13 @@ function createMessagingRepository(options) {
|
|
|
2111
2151
|
async addMembers(conversationId, memberIds) {
|
|
2112
2152
|
const operation = "addMembers";
|
|
2113
2153
|
const me = await actingUser(operation);
|
|
2154
|
+
const destination = await organizationOfExisting(conversationId);
|
|
2114
2155
|
const { error } = await db().from(TABLES.participants).insert(
|
|
2115
2156
|
memberIds.map((userId) => ({
|
|
2116
2157
|
conversation_id: conversationId,
|
|
2117
2158
|
user_id: userId,
|
|
2118
2159
|
role: "member",
|
|
2119
|
-
organization_id:
|
|
2160
|
+
organization_id: destination,
|
|
2120
2161
|
created_by: me
|
|
2121
2162
|
}))
|
|
2122
2163
|
);
|