@fluid-app/portal-sdk 0.1.98 → 0.1.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/{MessagingScreen-DA62lyrN.mjs → MessagingScreen-84R1a-lD.mjs} +2 -1
  2. package/dist/{MessagingScreen-DTXSZ-Oa.mjs → MessagingScreen-CBuI3fu6.mjs} +1204 -1112
  3. package/dist/MessagingScreen-CBuI3fu6.mjs.map +1 -0
  4. package/dist/{MessagingScreen-5jw8KSLQ.cjs → MessagingScreen-CGS7aG1A.cjs} +1 -1
  5. package/dist/{MessagingScreen-B9CCsimy.cjs → MessagingScreen-Cgx3jwpr.cjs} +1204 -1112
  6. package/dist/MessagingScreen-Cgx3jwpr.cjs.map +1 -0
  7. package/dist/{ProductsScreen-C0o1gVNw.mjs → ProductsScreen-6J79mnIB.mjs} +2 -2
  8. package/dist/{ProductsScreen-C0o1gVNw.mjs.map → ProductsScreen-6J79mnIB.mjs.map} +1 -1
  9. package/dist/{ProductsScreen-CL16lwsI.cjs → ProductsScreen-DZnKtPBp.cjs} +2 -2
  10. package/dist/{ProductsScreen-CigOdDOU.mjs → ProductsScreen-DbHS3p4U.mjs} +6 -2
  11. package/dist/{ProductsScreen-BTrBbxX6.cjs → ProductsScreen-PJ95OcSX.cjs} +2 -2
  12. package/dist/{ProductsScreen-BTrBbxX6.cjs.map → ProductsScreen-PJ95OcSX.cjs.map} +1 -1
  13. package/dist/ShareablesScreen-CVT7u2hN.cjs +398 -0
  14. package/dist/ShareablesScreen-CVT7u2hN.cjs.map +1 -0
  15. package/dist/ShareablesScreen-Cy7w85IH.mjs +380 -0
  16. package/dist/ShareablesScreen-Cy7w85IH.mjs.map +1 -0
  17. package/dist/{ShareablesScreen-CSLB0ZXS.mjs → ShareablesScreen-DUpaH8VU.mjs} +6 -2
  18. package/dist/{ShareablesScreen-DufZz22d.cjs → ShareablesScreen-Dk5EQGMa.cjs} +2 -2
  19. package/dist/index.cjs +10 -10
  20. package/dist/index.d.cts +429 -9
  21. package/dist/index.d.cts.map +1 -1
  22. package/dist/index.d.mts +429 -9
  23. package/dist/index.d.mts.map +1 -1
  24. package/dist/index.mjs +10 -10
  25. package/dist/{src-D9jWnRlX.mjs → src-BrwLqPPh.mjs} +90 -269
  26. package/dist/src-BrwLqPPh.mjs.map +1 -0
  27. package/dist/{src-C3R03o4d.cjs → src-Dlsw83js.cjs} +95 -268
  28. package/dist/src-Dlsw83js.cjs.map +1 -0
  29. package/package.json +12 -12
  30. package/dist/MessagingScreen-B9CCsimy.cjs.map +0 -1
  31. package/dist/MessagingScreen-DTXSZ-Oa.mjs.map +0 -1
  32. package/dist/ShareablesScreen-B9c5Mw5b.mjs +0 -159
  33. package/dist/ShareablesScreen-B9c5Mw5b.mjs.map +0 -1
  34. package/dist/ShareablesScreen-Bldl8tta.cjs +0 -177
  35. package/dist/ShareablesScreen-Bldl8tta.cjs.map +0 -1
  36. package/dist/src-C3R03o4d.cjs.map +0 -1
  37. package/dist/src-D9jWnRlX.mjs.map +0 -1
@@ -955,742 +955,6 @@ function useMessagingAuth() {
955
955
  };
956
956
  }
957
957
  //#endregion
958
- //#region src/messaging/use-messaging-config.ts
959
- /**
960
- * Hook that derives MessagingApiConfig from the portal SDK's FluidProvider context.
961
- *
962
- * Maps FluidSDKConfig fields to the shape expected by MessagingApp:
963
- * - baseUrl -> from config.baseUrl
964
- * - getHeaders -> builds Authorization header from config.getAuthToken()
965
- * - onAuthError -> from config.onAuthError
966
- * - websocketUrl -> config.websocketUrl or derived from baseUrl
967
- * - token -> from auth context
968
- */
969
- function deriveWebsocketUrl(baseUrl) {
970
- return `${baseUrl.replace(/\/+$/, "").replace(/\/api$/, "")}/cable`;
971
- }
972
- function useMessagingConfig() {
973
- const { config } = useFluidContext();
974
- const auth = useFluidAuthContext();
975
- const getHeaders = useCallback(async () => {
976
- const headers = {
977
- "Content-Type": "application/json",
978
- ...config.defaultHeaders
979
- };
980
- if (config.getAuthToken) {
981
- const token = await config.getAuthToken();
982
- if (token) headers.Authorization = `Bearer ${token}`;
983
- }
984
- return headers;
985
- }, [config]);
986
- const apiBaseUrl = useMemo(() => {
987
- const base = config.baseUrl.replace(/\/+$/, "");
988
- return base.endsWith("/api") ? base : `${base}/api`;
989
- }, [config.baseUrl]);
990
- return {
991
- apiConfig: useMemo(() => ({
992
- baseUrl: apiBaseUrl,
993
- getHeaders,
994
- ...config.onAuthError != null && { onAuthError: config.onAuthError }
995
- }), [
996
- apiBaseUrl,
997
- config.onAuthError,
998
- getHeaders
999
- ]),
1000
- websocketUrl: useMemo(() => config.websocketUrl ?? deriveWebsocketUrl(config.baseUrl), [config.websocketUrl, config.baseUrl]),
1001
- token: auth.token
1002
- };
1003
- }
1004
- //#endregion
1005
- //#region ../../messaging/core/src/query-keys.ts
1006
- /**
1007
- * Query key factory for all messaging-related TanStack Query queries.
1008
- * Ensures consistent key shapes across hooks and cache helpers.
1009
- */
1010
- const MESSAGES_QUERY_KEYS = {
1011
- all: () => ["messages"],
1012
- conversationMetadata: (conversationId) => [
1013
- ...MESSAGES_QUERY_KEYS.all(),
1014
- "conversation",
1015
- conversationId
1016
- ],
1017
- newMessageFlag: (conversationId) => [
1018
- ...MESSAGES_QUERY_KEYS.all(),
1019
- "conversation",
1020
- conversationId,
1021
- "new_message_flag"
1022
- ],
1023
- listConversations: () => [...MESSAGES_QUERY_KEYS.all(), "conversations"],
1024
- listMessages: (conversationId, messageId, startAtISO) => [
1025
- ...MESSAGES_QUERY_KEYS.all(),
1026
- "conversation",
1027
- conversationId,
1028
- "messages",
1029
- messageId ? { startFromMessage: messageId } : startAtISO ? { startFromDate: startAtISO } : { startFromBottom: true }
1030
- ],
1031
- listPinnedMessages: (conversationId) => [
1032
- ...MESSAGES_QUERY_KEYS.all(),
1033
- "conversation",
1034
- conversationId,
1035
- "pinned"
1036
- ],
1037
- outbox: (conversationId) => [
1038
- ...MESSAGES_QUERY_KEYS.all(),
1039
- "conversation",
1040
- conversationId,
1041
- "outbox"
1042
- ],
1043
- listScheduledMessages: (conversationId) => [
1044
- ...MESSAGES_QUERY_KEYS.all(),
1045
- "conversation",
1046
- conversationId,
1047
- "scheduled"
1048
- ],
1049
- listMyScheduledMessages: () => [
1050
- ...MESSAGES_QUERY_KEYS.all(),
1051
- "scheduled",
1052
- "mine"
1053
- ],
1054
- draft: (conversationId) => [
1055
- ...MESSAGES_QUERY_KEYS.all(),
1056
- "draft",
1057
- conversationId
1058
- ],
1059
- announcementChannel: () => [...MESSAGES_QUERY_KEYS.all(), "announcement-channel"],
1060
- downline: () => [...MESSAGES_QUERY_KEYS.all(), "downline"]
1061
- };
1062
- //#endregion
1063
- //#region ../../messaging/core/src/constants.ts
1064
- const WEBSOCKET_QUERY_OPTIONS = {
1065
- staleTime: Infinity,
1066
- refetchOnMount: "always",
1067
- refetchOnReconnect: "always",
1068
- refetchOnWindowFocus: false
1069
- };
1070
- /**
1071
- * Scheduled messages are not websocket-driven; poll to keep fresh.
1072
- */
1073
- const SCHEDULED_MESSAGES_STALE_TIME = 6e4;
1074
- const SCHEDULED_MESSAGES_REFETCH_INTERVAL = 6e4;
1075
- /**
1076
- * Pinned messages are not websocket-driven; poll to keep fresh.
1077
- */
1078
- const PINNED_MESSAGES_STALE_TIME = 6e4;
1079
- const PINNED_MESSAGES_REFETCH_INTERVAL = 6e4;
1080
- //#endregion
1081
- //#region ../../messaging/core/src/cache-helpers.ts
1082
- /**
1083
- * seed conversation metadata caches from any conversations list queries.
1084
- * returns the matched conversation if conversationId is provided and present in any list cache.
1085
- */
1086
- function seedConversationMetadataFromLists(queryClient, conversationId) {
1087
- let matched;
1088
- const seedFromData = (data) => {
1089
- const pages = data?.pages ?? [];
1090
- for (const page of pages) {
1091
- const items = page?.[1]?.items ?? [];
1092
- for (const item of items) {
1093
- queryClient.setQueryData(MESSAGES_QUERY_KEYS.conversationMetadata(item.id), item);
1094
- if (!matched && typeof conversationId === "number" && item.id === conversationId) matched = item;
1095
- }
1096
- }
1097
- };
1098
- seedFromData(queryClient.getQueryData(MESSAGES_QUERY_KEYS.listConversations()));
1099
- const listQueries = queryClient.getQueryCache().findAll({ predicate: (q) => {
1100
- const key = q.queryKey;
1101
- return Array.isArray(key) && key.length >= 2 && key[0] === MESSAGES_QUERY_KEYS.all()[0] && key[1] === MESSAGES_QUERY_KEYS.listConversations()[1];
1102
- } });
1103
- for (const q of listQueries) seedFromData(queryClient.getQueryData(q.queryKey));
1104
- return matched;
1105
- }
1106
- function isMessagesQueryKeyForConversation(queryKey, conversationId) {
1107
- if (!Array.isArray(queryKey)) return false;
1108
- return queryKey.length >= 4 && queryKey[0] === MESSAGES_QUERY_KEYS.all()[0] && queryKey[1] === "conversation" && queryKey[2] === conversationId && queryKey[3] === "messages";
1109
- }
1110
- function findMessageInCache(queryClient, conversationId, messageId) {
1111
- const queries = queryClient.getQueryCache().findAll({ predicate: (q) => isMessagesQueryKeyForConversation(q.queryKey, conversationId) });
1112
- for (const query of queries) {
1113
- const data = queryClient.getQueryData(query.queryKey);
1114
- if (data) for (const page of data.pages) {
1115
- const message = page[1].items.find((m) => m.id === messageId);
1116
- if (message) return message;
1117
- }
1118
- }
1119
- }
1120
- function forEachMessagesQuery(queryClient, conversationId, fn) {
1121
- queryClient.getQueryCache().findAll({ predicate: (q) => isMessagesQueryKeyForConversation(q.queryKey, conversationId) }).forEach((q) => fn(q.queryKey));
1122
- }
1123
- function isBottomAnchorKey(queryKey) {
1124
- if (!Array.isArray(queryKey)) return false;
1125
- const anchor = queryKey[queryKey.length - 1];
1126
- if (typeof anchor === "object" && anchor !== null) {
1127
- if ("startFromBottom" in anchor) return anchor.startFromBottom === true;
1128
- }
1129
- return false;
1130
- }
1131
- function isPinnedQueryKeyForConversation(queryKey, conversationId) {
1132
- if (!Array.isArray(queryKey)) return false;
1133
- return queryKey.length >= 4 && queryKey[0] === MESSAGES_QUERY_KEYS.all()[0] && queryKey[1] === "conversation" && queryKey[2] === conversationId && queryKey[3] === "pinned";
1134
- }
1135
- function forEachPinnedQuery(queryClient, conversationId, fn) {
1136
- queryClient.getQueryCache().findAll({ predicate: (q) => isPinnedQueryKeyForConversation(q.queryKey, conversationId) }).forEach((q) => fn(q.queryKey));
1137
- }
1138
- async function cancelMessageQueries(queryClient, conversationId) {
1139
- const queries = queryClient.getQueryCache().findAll({ predicate: (q) => isMessagesQueryKeyForConversation(q.queryKey, conversationId) });
1140
- await Promise.all(queries.map((q) => queryClient.cancelQueries({ queryKey: q.queryKey })));
1141
- }
1142
- async function cancelPinnedQueries(queryClient, conversationId) {
1143
- const queries = queryClient.getQueryCache().findAll({ predicate: (q) => isPinnedQueryKeyForConversation(q.queryKey, conversationId) });
1144
- await Promise.all(queries.map((q) => queryClient.cancelQueries({ queryKey: q.queryKey })));
1145
- }
1146
- function applyMessagesOptimisticSend({ queryClient, conversationId, optimisticMessage }) {
1147
- forEachMessagesQuery(queryClient, conversationId, (key) => {
1148
- if (!isBottomAnchorKey(key)) return;
1149
- queryClient.setQueryData(key, (oldData) => {
1150
- const newOptimisticPage = [{ pagination: {
1151
- current: 1,
1152
- previous: null,
1153
- next: null,
1154
- per_page: oldData?.pages?.[0]?.[0]?.pagination?.per_page ?? 20,
1155
- pages: oldData?.pages?.[0]?.[0]?.pagination?.pages ?? 1,
1156
- count: oldData?.pages?.[0]?.[0]?.pagination?.count ?? 0
1157
- } }, { items: [optimisticMessage] }];
1158
- if (!oldData) return {
1159
- pages: [newOptimisticPage],
1160
- pageParams: [1]
1161
- };
1162
- const pages = oldData.pages.map((page, idx) => {
1163
- if (idx === 0) {
1164
- const currentItems = page?.[1]?.items ?? [];
1165
- return [{ pagination: {
1166
- ...page?.[0]?.pagination ?? {},
1167
- count: page?.[0]?.pagination?.count ?? 0
1168
- } }, { items: [optimisticMessage, ...currentItems] }];
1169
- }
1170
- return page;
1171
- });
1172
- return {
1173
- ...oldData,
1174
- pages
1175
- };
1176
- });
1177
- });
1178
- queryClient.setQueryData(MESSAGES_QUERY_KEYS.outbox(conversationId), (old) => {
1179
- const prev = old?.items ?? [];
1180
- if (prev.some((m) => m.id === optimisticMessage.id)) return old;
1181
- return { items: [...prev, optimisticMessage] };
1182
- });
1183
- }
1184
- /**
1185
- * markNewMessageFlagForConversation
1186
- *
1187
- * sets a lightweight flag query for a conversation indicating a new message
1188
- * arrived while the user may not be at the bottom. UI can reset it after
1189
- * scrolling or showing a pill.
1190
- */
1191
- function markNewMessageFlagForConversation(queryClient, conversationId) {
1192
- queryClient.setQueryData([
1193
- MESSAGES_QUERY_KEYS.all()[0],
1194
- "conversation",
1195
- conversationId,
1196
- "new_message_flag"
1197
- ], true);
1198
- }
1199
- function clearNewMessageFlagForConversation(queryClient, conversationId) {
1200
- queryClient.setQueryData([
1201
- MESSAGES_QUERY_KEYS.all()[0],
1202
- "conversation",
1203
- conversationId,
1204
- "new_message_flag"
1205
- ], false);
1206
- }
1207
- function applyMessageUpsertToCache({ queryClient, conversationId, message, replaceOptimisticId, clientMessageId }) {
1208
- forEachMessagesQuery(queryClient, conversationId, (key) => {
1209
- queryClient.setQueryData(key, (oldData) => {
1210
- if (!oldData?.pages?.length) {
1211
- if (!isBottomAnchorKey(key)) return oldData;
1212
- return {
1213
- pages: [[{ pagination: {
1214
- current: 1,
1215
- previous: null,
1216
- next: null,
1217
- per_page: 20,
1218
- pages: 1,
1219
- count: 1
1220
- } }, { items: [message] }]],
1221
- pageParams: [1]
1222
- };
1223
- }
1224
- const normalizedClientMessageId = (() => {
1225
- if (typeof clientMessageId === "string" && clientMessageId.length > 0) return clientMessageId;
1226
- const fromMeta = (() => {
1227
- const meta = message.metadata;
1228
- if (meta && typeof meta === "object" && "client_message_id" in meta) {
1229
- const id = meta.client_message_id;
1230
- if (typeof id === "string" || typeof id === "number" && Number.isFinite(id)) return id;
1231
- }
1232
- })();
1233
- if (typeof fromMeta === "string" || typeof fromMeta === "number" && Number.isFinite(fromMeta)) return String(fromMeta);
1234
- })();
1235
- const finalMessage = (() => {
1236
- if (!normalizedClientMessageId) return message;
1237
- const currentMeta = message.metadata;
1238
- if (currentMeta != null && typeof currentMeta === "object" && (typeof currentMeta.client_message_id === "string" || typeof currentMeta.client_message_id === "number")) return message;
1239
- return {
1240
- ...message,
1241
- metadata: {
1242
- ...currentMeta && typeof currentMeta === "object" ? currentMeta : {},
1243
- client_message_id: normalizedClientMessageId
1244
- }
1245
- };
1246
- })();
1247
- let replaced = false;
1248
- const pages = oldData.pages.map((page) => {
1249
- const [meta, payload] = page;
1250
- const items = payload.items.slice();
1251
- const idxById = items.findIndex((m) => m.id === (replaceOptimisticId ?? message.id));
1252
- if (idxById !== -1) {
1253
- items[idxById] = finalMessage;
1254
- replaced = true;
1255
- return [meta, { items }];
1256
- }
1257
- if (normalizedClientMessageId) {
1258
- const idxByClientId = items.findIndex((m) => typeof m?.metadata === "object" && m?.metadata != null && ((id) => id != null && String(id) === normalizedClientMessageId)(m.metadata?.client_message_id));
1259
- if (idxByClientId !== -1) {
1260
- items[idxByClientId] = finalMessage;
1261
- replaced = true;
1262
- return [meta, { items }];
1263
- }
1264
- }
1265
- const idxExisting = items.findIndex((m) => m.id === message.id);
1266
- if (idxExisting !== -1) {
1267
- items[idxExisting] = finalMessage;
1268
- replaced = true;
1269
- return [meta, { items }];
1270
- }
1271
- return [meta, { items }];
1272
- });
1273
- if (!replaced) {
1274
- if (!isBottomAnchorKey(key)) return oldData;
1275
- const first = pages[0];
1276
- const nextPages = pages.slice();
1277
- const newFirstItems = [finalMessage, ...first?.[1]?.items ?? []];
1278
- const newCount = (first?.[0]?.pagination?.count ?? 0) + 1;
1279
- nextPages[0] = [{ pagination: {
1280
- ...first?.[0]?.pagination ?? {},
1281
- count: newCount
1282
- } }, { items: newFirstItems }];
1283
- return {
1284
- ...oldData,
1285
- pages: nextPages
1286
- };
1287
- }
1288
- return {
1289
- ...oldData,
1290
- pages
1291
- };
1292
- });
1293
- });
1294
- queryClient.setQueryData(MESSAGES_QUERY_KEYS.outbox(conversationId), (old) => {
1295
- if (!old?.items) return old;
1296
- return { items: old.items.filter((m) => m.id !== (replaceOptimisticId ?? -99999999)) };
1297
- });
1298
- }
1299
- function applyMessageDeleteFromCache({ queryClient, conversationId, messageId }) {
1300
- forEachMessagesQuery(queryClient, conversationId, (key) => {
1301
- queryClient.setQueryData(key, (oldData) => {
1302
- if (!oldData) return oldData;
1303
- const pages = oldData.pages.map((page) => {
1304
- const [meta, payload] = page;
1305
- const items = payload.items.filter((m) => m.id !== messageId);
1306
- const newCount = Math.max(0, (meta?.pagination?.count ?? 0) - (payload.items.length - items.length));
1307
- return [{ pagination: {
1308
- ...meta?.pagination ?? {},
1309
- count: newCount
1310
- } }, { items }];
1311
- });
1312
- return {
1313
- ...oldData,
1314
- pages
1315
- };
1316
- });
1317
- });
1318
- queryClient.setQueryData(MESSAGES_QUERY_KEYS.outbox(conversationId), (old) => {
1319
- if (!old?.items) return old;
1320
- return { items: old.items.filter((m) => m.id !== messageId) };
1321
- });
1322
- }
1323
- function applyReactionUpdateToCache({ queryClient, conversationId, messageId, reaction, reaction_stats }) {
1324
- forEachMessagesQuery(queryClient, conversationId, (key) => {
1325
- queryClient.setQueryData(key, (oldData) => {
1326
- if (!oldData) return oldData;
1327
- const pages = oldData.pages.map((page) => {
1328
- const [meta, payload] = page;
1329
- return [meta, { items: payload.items.map((m) => m.id === messageId ? {
1330
- ...m,
1331
- reaction,
1332
- reaction_stats
1333
- } : m) }];
1334
- });
1335
- return {
1336
- ...oldData,
1337
- pages
1338
- };
1339
- });
1340
- });
1341
- forEachPinnedQuery(queryClient, conversationId, (key) => {
1342
- queryClient.setQueryData(key, (oldData) => {
1343
- if (!oldData) return oldData;
1344
- const pages = oldData.pages.map((page) => {
1345
- const [meta, payload] = page;
1346
- return [meta, { items: payload.items.map((m) => m.id === messageId ? {
1347
- ...m,
1348
- reaction,
1349
- reaction_stats
1350
- } : m) }];
1351
- });
1352
- return {
1353
- ...oldData,
1354
- pages
1355
- };
1356
- });
1357
- });
1358
- }
1359
- function applyReactionStatsToCache({ queryClient, conversationId, messageId, reaction_stats }) {
1360
- forEachMessagesQuery(queryClient, conversationId, (key) => {
1361
- queryClient.setQueryData(key, (oldData) => {
1362
- if (!oldData) return;
1363
- const pages = oldData.pages.map((page) => {
1364
- const [meta, payload] = page;
1365
- return [meta, { items: payload.items.map((m) => m.id === messageId ? {
1366
- ...m,
1367
- reaction_stats
1368
- } : m) }];
1369
- });
1370
- return {
1371
- ...oldData,
1372
- pages
1373
- };
1374
- });
1375
- });
1376
- forEachPinnedQuery(queryClient, conversationId, (key) => {
1377
- queryClient.setQueryData(key, (oldData) => {
1378
- if (!oldData) return oldData;
1379
- const pages = oldData.pages.map((page) => {
1380
- const [meta, payload] = page;
1381
- return [meta, { items: payload.items.map((m) => m.id === messageId ? {
1382
- ...m,
1383
- reaction_stats
1384
- } : m) }];
1385
- });
1386
- return {
1387
- ...oldData,
1388
- pages
1389
- };
1390
- });
1391
- });
1392
- }
1393
- function applyConversationMetaToCaches({ queryClient, conversation }) {
1394
- queryClient.setQueryData(MESSAGES_QUERY_KEYS.conversationMetadata(conversation.id), (old) => {
1395
- if (!old) return old;
1396
- return {
1397
- ...old,
1398
- ...conversation
1399
- };
1400
- });
1401
- const listQueries = queryClient.getQueryCache().findAll({ predicate: (q) => {
1402
- const key = q.queryKey;
1403
- return Array.isArray(key) && key.length >= 2 && key[0] === MESSAGES_QUERY_KEYS.all()[0] && key[1] === MESSAGES_QUERY_KEYS.listConversations()[1];
1404
- } });
1405
- for (const q of listQueries) queryClient.setQueryData(q.queryKey, (oldData) => {
1406
- const data = oldData;
1407
- if (!data?.pages) return oldData;
1408
- return {
1409
- ...data,
1410
- pages: data.pages.map((page) => [page[0], {
1411
- ...page[1],
1412
- items: page[1].items.map((conv) => conv.id === conversation.id ? {
1413
- ...conv,
1414
- ...typeof conversation.unread === "boolean" ? { unread: conversation.unread } : {},
1415
- ...typeof conversation.unread_messages_count === "number" ? { unread_messages_count: conversation.unread_messages_count } : {}
1416
- } : conv)
1417
- }])
1418
- };
1419
- });
1420
- }
1421
- function applyConversationDestroyed({ queryClient, conversationId }) {
1422
- queryClient.removeQueries({ queryKey: MESSAGES_QUERY_KEYS.conversationMetadata(conversationId) });
1423
- queryClient.setQueryData(MESSAGES_QUERY_KEYS.listConversations(), (oldData) => {
1424
- const data = oldData;
1425
- if (!data?.pages) return oldData;
1426
- return {
1427
- ...data,
1428
- pages: data.pages.map((page) => [page[0], {
1429
- ...page[1],
1430
- items: page[1].items.filter((c) => c.id !== conversationId)
1431
- }])
1432
- };
1433
- });
1434
- }
1435
- function applyMessagePinnedFlag({ queryClient, conversationId, messageId, pinned, message }) {
1436
- forEachMessagesQuery(queryClient, conversationId, (key) => {
1437
- queryClient.setQueryData(key, (oldData) => {
1438
- if (!oldData) return oldData;
1439
- const pages = oldData.pages.map((page) => {
1440
- const [meta, payload] = page;
1441
- return [meta, { items: payload.items.map((m) => m.id === messageId ? {
1442
- ...m,
1443
- pinned
1444
- } : m) }];
1445
- });
1446
- return {
1447
- ...oldData,
1448
- pages
1449
- };
1450
- });
1451
- });
1452
- forEachPinnedQuery(queryClient, conversationId, (key) => {
1453
- queryClient.setQueryData(key, (oldData) => {
1454
- if (!oldData) return oldData;
1455
- const pages = oldData.pages.map((page, idx) => {
1456
- const [meta, payload] = page;
1457
- if (idx !== 0) return page;
1458
- const exists = payload.items.some((m) => m.id === messageId);
1459
- if (pinned) {
1460
- if (exists) return page;
1461
- const msg = message ?? payload.items.find((m) => m.id === messageId);
1462
- if (!msg) return page;
1463
- return [meta, { items: [msg, ...payload.items] }];
1464
- }
1465
- return [meta, { items: payload.items.filter((m) => m.id !== messageId) }];
1466
- });
1467
- return {
1468
- ...oldData,
1469
- pages
1470
- };
1471
- });
1472
- });
1473
- }
1474
- //#endregion
1475
- //#region ../../messaging/core/src/utils/reaction-utils.ts
1476
- /**
1477
- * Splits an emoji string into an array of grapheme clusters (perceived characters/emojis).
1478
- * Uses `Intl.Segmenter` when available for correct multi-codepoint emoji handling.
1479
- * Falls back to `[...string]` (code-point iteration) on browsers without `Intl.Segmenter`
1480
- * (e.g. Firefox < 119), which may incorrectly split ZWJ sequences, skin-tone modifiers,
1481
- * and flag emojis into multiple entries.
1482
- * @param emojiString The string of emojis to split.
1483
- * @returns An array of strings, where each string is a single emoji.
1484
- */
1485
- function splitEmojis(emojiString) {
1486
- if (!emojiString) return [];
1487
- if (typeof Intl !== "undefined" && typeof Intl.Segmenter === "function") {
1488
- const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
1489
- return Array.from(segmenter.segment(emojiString)).map((segment) => segment.segment);
1490
- }
1491
- return [...emojiString];
1492
- }
1493
- /**
1494
- * Flattens reaction_stats to count individual emojis.
1495
- * E.g., {"😀😂": 1, "👍": 2, "😀": 1} becomes {"😀": 2, "😂": 1, "👍": 2}
1496
- * Handles multi-codepoint emojis correctly.
1497
- * @param stats The raw reaction_stats object from the message.
1498
- * @returns A record where keys are single emojis and values are their total counts.
1499
- */
1500
- function flattenReactionStats(stats) {
1501
- if (!stats) return {};
1502
- const flattened = {};
1503
- for (const emojiKey in stats) {
1504
- const count = stats[emojiKey];
1505
- if (typeof count !== "number" || count === 0) continue;
1506
- const individualEmojis = splitEmojis(emojiKey);
1507
- for (const emoji of individualEmojis) flattened[emoji] = (flattened[emoji] || 0) + count;
1508
- }
1509
- return flattened;
1510
- }
1511
- /**
1512
- * Calculates the optimistic state for a message's reactions when a user's reaction changes.
1513
- * @param currentState The current reaction parts of the message ({ reaction, reaction_stats }).
1514
- * @param newUserReaction The user's new complete reaction string (e.g., "😀👍", "😂", or null to unreact).
1515
- * @returns An object with the updated `reaction` and `reaction_stats`.
1516
- */
1517
- function calculateOptimisticReactionUpdate(currentState, newUserReaction) {
1518
- const previousUserReaction = currentState.reaction;
1519
- const newStats = { ...currentState.reaction_stats || {} };
1520
- const finalNewUserReaction = newUserReaction === "" ? null : newUserReaction;
1521
- if (previousUserReaction === finalNewUserReaction) return {
1522
- reaction: previousUserReaction,
1523
- reaction_stats: newStats
1524
- };
1525
- if (previousUserReaction && typeof newStats[previousUserReaction] === "number") newStats[previousUserReaction] = Math.max(0, newStats[previousUserReaction] - 1);
1526
- if (finalNewUserReaction) newStats[finalNewUserReaction] = (typeof newStats[finalNewUserReaction] === "number" ? newStats[finalNewUserReaction] : 0) + 1;
1527
- return {
1528
- reaction: finalNewUserReaction,
1529
- reaction_stats: newStats
1530
- };
1531
- }
1532
- /**
1533
- * Generates a unique, sorted string from an array of emoji characters.
1534
- * Each string in the input array is assumed to be a single, correctly segmented emoji.
1535
- * E.g., ["😂", "😀", "😂"] becomes "😀😂"
1536
- * @param emojis An array of single emoji strings (grapheme clusters).
1537
- * @returns A sorted string of unique emojis, or an empty string if no emojis are provided.
1538
- */
1539
- function generateSortedEmojiStringFromArray(emojis) {
1540
- if (!emojis || emojis.length === 0) return "";
1541
- return [...new Set(emojis)].sort((a, b) => a.localeCompare(b, "en")).join("");
1542
- }
1543
- //#endregion
1544
- //#region ../../messaging/core/src/utils/messaging.ts
1545
- function mapConversationTypeToMessageType(conversationType) {
1546
- switch (conversationType) {
1547
- case "DirectConversation": return "DirectMessage";
1548
- case "SmsConversation": return "SmsMessage";
1549
- case "EmailConversation": return "EmailMessage";
1550
- case "ChannelConversation":
1551
- case "GroupConversation": return "DirectMessage";
1552
- case "BotConversation": return "BotMessage";
1553
- default: return "DirectMessage";
1554
- }
1555
- }
1556
- /**
1557
- * Determines the category of a chat. This is slightly different from the backend's
1558
- * category field.
1559
- *
1560
- * @param item - Conversation item to check
1561
- * @returns the type of channel
1562
- */
1563
- const getMessageType = (item) => {
1564
- if (item.type === "GroupConversation" && item.name.toLowerCase().includes("other")) return {
1565
- isChannel: false,
1566
- type: "group-direct-message"
1567
- };
1568
- if (item.broadcasting) return {
1569
- isChannel: true,
1570
- type: "broadcast"
1571
- };
1572
- if (item.type === "GroupConversation") return {
1573
- isChannel: true,
1574
- type: "private-channel"
1575
- };
1576
- if (item.type === "DirectConversation") return {
1577
- isChannel: false,
1578
- type: "direct-message"
1579
- };
1580
- if (item.type === "EmailConversation") return {
1581
- isChannel: false,
1582
- type: "email"
1583
- };
1584
- if (item.type === "SmsConversation") return {
1585
- isChannel: false,
1586
- type: "sms"
1587
- };
1588
- if (item.type === "ChannelConversation") return {
1589
- isChannel: true,
1590
- type: "public-channel"
1591
- };
1592
- if (item.type === "BotConversation") return {
1593
- isChannel: false,
1594
- type: "bot"
1595
- };
1596
- if (item.type === "DownlineConversation") return {
1597
- isChannel: true,
1598
- type: "downline"
1599
- };
1600
- throw new Error("Invalid conversation type");
1601
- };
1602
- const getUserInitials = (conversation, user) => {
1603
- if (user.phone === conversation.name) return "#";
1604
- if (user.email === conversation.name) return "@";
1605
- return [user.first_name?.charAt(0), user.last_name?.charAt(0)].filter(Boolean).join("") || "?";
1606
- };
1607
- const getFileTypeFromMimetype = (mimetype) => {
1608
- if (mimetype.startsWith("image/")) return "image";
1609
- if (mimetype.startsWith("video/")) return "video";
1610
- if (mimetype === "application/pdf") return "pdf";
1611
- if (mimetype.includes("spreadsheet") || mimetype.includes("excel") || mimetype.includes("sheet") || mimetype === "text/csv") return "sheets";
1612
- if (mimetype.includes("powerpoint") || mimetype.includes("presentation")) return "powerpoint";
1613
- if (mimetype.includes("document") || mimetype.includes("word") || mimetype.includes("rtf")) return "docx";
1614
- if (mimetype.startsWith("text/")) return "txt";
1615
- if (mimetype.includes("zip") || mimetype.includes("compressed") || mimetype.includes("archive") || mimetype.includes("tar") || mimetype.includes("gzip")) return "compressed";
1616
- if (mimetype.startsWith("audio/")) return "audio";
1617
- return "unknown";
1618
- };
1619
- //#endregion
1620
- //#region ../../messaging/core/src/utils/string-utils.ts
1621
- /**
1622
- * Extracts the leading emoji from a string.
1623
- * @param text The string to process.
1624
- * @returns An object containing the emoji and the remaining text.
1625
- */
1626
- function extractEmoji$1(text) {
1627
- const match = text.match(/^\p{Emoji_Presentation}/u);
1628
- if (match) {
1629
- const emoji = match[0];
1630
- return {
1631
- emoji,
1632
- text: text.substring(emoji.length).trimStart()
1633
- };
1634
- }
1635
- return {
1636
- emoji: null,
1637
- text
1638
- };
1639
- }
1640
- /**
1641
- * Formats a message channel name by replacing spaces and underscores with hyphens,
1642
- * converting to lowercase, and removing any non-alphanumeric characters except hyphens.
1643
- * @param name - The original channel name
1644
- * @return The formatted channel name
1645
- */
1646
- function formatMessageChannelName$1(name) {
1647
- return name.toLowerCase().replace(/[ _]/g, "-").replace(/[^a-z0-9-]/g, "");
1648
- }
1649
- const MAX_GROUP_NAME_LENGTH$1 = 30;
1650
- function formatGroupDisplayName$1(names, maxLength = MAX_GROUP_NAME_LENGTH$1) {
1651
- const fullDisplayName = names.join(", ");
1652
- if (fullDisplayName.length <= maxLength) return { displayName: fullDisplayName };
1653
- for (let i = 0; i < names.length; i++) {
1654
- const currentNames = names.slice(0, i + 1);
1655
- const remainingCount = names.length - currentNames.length;
1656
- if (remainingCount > 0) {
1657
- const suffix = `+ ${remainingCount} other${remainingCount > 1 ? "s" : ""}`;
1658
- if (`${currentNames.join(", ")} ${suffix}`.length > maxLength && i > 0) {
1659
- const namesToShow = names.slice(0, i);
1660
- const othersCount = names.length - i;
1661
- const othersSuffix = `+${othersCount} other${othersCount > 1 ? "s" : ""}`;
1662
- const truncatedDisplayName = namesToShow.join(", ");
1663
- if (othersCount === 1) {
1664
- const nameWithOneMore = names.slice(0, i + 1).join(", ");
1665
- if (nameWithOneMore.length < truncatedDisplayName.length) return { displayName: nameWithOneMore };
1666
- }
1667
- return {
1668
- displayName: truncatedDisplayName,
1669
- remainder: othersSuffix
1670
- };
1671
- }
1672
- }
1673
- }
1674
- const firstName = names[0] || "";
1675
- const othersCount = names.length - 1;
1676
- if (othersCount > 0) return {
1677
- displayName: firstName,
1678
- remainder: `+ ${othersCount} other${othersCount > 1 ? "s" : ""}`
1679
- };
1680
- return { displayName: firstName };
1681
- }
1682
- //#endregion
1683
- //#region ../../messaging/core/src/utils/file-kinds.ts
1684
- const ServerFileKindEnum$1 = z.enum([
1685
- "image",
1686
- "video",
1687
- "pdf",
1688
- "sheets",
1689
- "powerpoint",
1690
- "docx"
1691
- ]);
1692
- const fallbackFileType$1 = "docx";
1693
- //#endregion
1694
958
  //#region ../../messaging/api-client/src/client.ts
1695
959
  /**
1696
960
  * Messaging-specific API error, extends the shared ApiError from api-client-core.
@@ -1980,7 +1244,7 @@ const WithContactRecipientSchema = z.strictObject({
1980
1244
  });
1981
1245
  createPaginatedSchema(RecipientSchema);
1982
1246
  createPaginatedSchema(ReducedRecipientSchema);
1983
- z.strictObject({ meta: z.strictObject({ count: z.number() }) });
1247
+ const RecipientSearchCountSchema = z.strictObject({ meta: z.strictObject({ count: z.number() }) });
1984
1248
  //#endregion
1985
1249
  //#region ../../messaging/api-client/src/schemas/drafts-scheduled.schema.ts
1986
1250
  const DraftMessageSchema = z.strictObject({
@@ -2126,7 +1390,7 @@ z.strictObject({ success: z.boolean() }).or(z.strictObject({
2126
1390
  *
2127
1391
  * These schemas normalize the slightly different index vs show/update shapes.
2128
1392
  */
2129
- const ReactionStatsSchema = z.record(z.string(), z.number());
1393
+ const ReactionStatsSchema$1 = z.record(z.string(), z.number());
2130
1394
  const MessageCoreSchema = z.strictObject({
2131
1395
  id: z.number(),
2132
1396
  body: z.string(),
@@ -2138,7 +1402,7 @@ const MessageCoreSchema = z.strictObject({
2138
1402
  conversation_id: z.number(),
2139
1403
  status: z.string(),
2140
1404
  attachments: z.array(AttachmentSchema),
2141
- reaction_stats: ReactionStatsSchema.nullable(),
1405
+ reaction_stats: ReactionStatsSchema$1.nullable(),
2142
1406
  reaction: z.string().nullable().optional(),
2143
1407
  metadata: MessageMetadataSchema.optional(),
2144
1408
  emoji_only_count: z.number().optional(),
@@ -2152,9 +1416,9 @@ const MessageCoreSchema = z.strictObject({
2152
1416
  seen_by_count: z.unknown()
2153
1417
  });
2154
1418
  const MessageIndexItemSchema = MessageCoreSchema;
2155
- const MessageItemSchema = MessageCoreSchema;
1419
+ const MessageItemSchema$1 = MessageCoreSchema;
2156
1420
  const MessageIndexResponseSchema = createPaginatedSchema(MessageIndexItemSchema);
2157
- const MessageListResponseSchema = createPaginatedSchema(MessageItemSchema);
1421
+ const MessageListResponseSchema = createPaginatedSchema(MessageItemSchema$1);
2158
1422
  const MessagePinResponseSchema = z.strictObject({
2159
1423
  message: z.string(),
2160
1424
  pinned: z.boolean()
@@ -2207,328 +1471,1117 @@ async function listMessages(config, conversation_id, input) {
2207
1471
  endpoint: `/v1/messaging/conversations/${conversation_id}/messages`,
2208
1472
  method: "GET",
2209
1473
  input,
2210
- outputSchema: MessageIndexResponseSchema
1474
+ outputSchema: MessageIndexResponseSchema
1475
+ });
1476
+ }
1477
+ const _createMessageInputSchema = z.strictObject({
1478
+ message: z.strictObject({
1479
+ body: z.string().optional(),
1480
+ theme: z.string().optional(),
1481
+ subject: z.string().optional(),
1482
+ type: z.string().optional(),
1483
+ attachments_attributes: z.array(AttachmentInputSchema).optional()
1484
+ }),
1485
+ client_message_id: z.union([z.string(), z.number()]).optional()
1486
+ });
1487
+ const createMessageInputSchema = _createMessageInputSchema;
1488
+ async function createMessage(config, conversation_id, input) {
1489
+ return fetchMessaging(config, {
1490
+ endpoint: `/v1/messaging/conversations/${conversation_id}/messages`,
1491
+ method: "POST",
1492
+ input,
1493
+ inputSchema: createMessageInputSchema,
1494
+ outputSchema: MessageItemSchema$1
1495
+ });
1496
+ }
1497
+ /**
1498
+ * POST /api/v1/messaging/conversations/:conversation_id/messages/reply
1499
+ * Reply to an existing message by id; thread is captured in replied_to_id.
1500
+ */
1501
+ async function replyToMessage(config, conversation_id, message_id, input) {
1502
+ return fetchMessaging(config, {
1503
+ endpoint: `/v1/messaging/conversations/${conversation_id}/messages/reply`,
1504
+ method: "POST",
1505
+ input: {
1506
+ ...input,
1507
+ message_id
1508
+ },
1509
+ inputSchema: _createMessageInputSchema.extend({ message_id: z.number() }),
1510
+ outputSchema: MessageItemSchema$1
1511
+ });
1512
+ }
1513
+ z.strictObject({ message: z.strictObject({
1514
+ body: z.string().optional(),
1515
+ theme: z.string().optional()
1516
+ }) });
1517
+ /**
1518
+ * POST /api/v1/messaging/conversations/:conversation_id/messages/:message_id/react.json
1519
+ * React to a message with an emoji.
1520
+ */
1521
+ async function reactToMessage(config, conversation_id, input) {
1522
+ const { message_id, reaction } = input;
1523
+ return fetchMessaging(config, {
1524
+ endpoint: `/v1/messaging/conversations/${conversation_id}/messages/${message_id}/react.json?reaction=${encodeURIComponent(reaction)}`,
1525
+ method: "POST",
1526
+ outputSchema: z.string()
1527
+ });
1528
+ }
1529
+ /**
1530
+ * DELETE /api/v1/messaging/conversations/:conversation_id/messages/:message_id/unreact.json
1531
+ * Remove the current recipient's reaction.
1532
+ */
1533
+ async function unreactToMessage(config, conversation_id, input) {
1534
+ const { message_id } = input;
1535
+ return fetchMessaging(config, {
1536
+ endpoint: `/v1/messaging/conversations/${conversation_id}/messages/${message_id}/unreact.json`,
1537
+ method: "DELETE",
1538
+ outputSchema: z.string()
1539
+ });
1540
+ }
1541
+ /**
1542
+ * GET /api/v1/messaging/conversations/:conversation_id/messages/pinned
1543
+ * List pinned messages in the conversation.
1544
+ */
1545
+ async function listPinnedMessages(config, conversation_id, input) {
1546
+ return fetchMessaging(config, {
1547
+ endpoint: `/v1/messaging/conversations/${conversation_id}/messages/pinned`,
1548
+ method: "GET",
1549
+ input,
1550
+ outputSchema: MessageListResponseSchema
1551
+ });
1552
+ }
1553
+ /**
1554
+ * PUT /api/v1/messaging/conversations/:conversation_id/messages/:id/pin
1555
+ * Pin a message (current recipient).
1556
+ */
1557
+ async function pinMessage(config, conversation_id, id) {
1558
+ return fetchMessaging(config, {
1559
+ endpoint: `/v1/messaging/conversations/${conversation_id}/messages/${id}/pin`,
1560
+ method: "PUT",
1561
+ outputSchema: MessagePinResponseSchema
1562
+ });
1563
+ }
1564
+ /**
1565
+ * PUT /api/v1/messaging/conversations/:conversation_id/messages/:id/unpin
1566
+ * Unpin a message (current recipient).
1567
+ */
1568
+ async function unpinMessage(config, conversation_id, id) {
1569
+ return fetchMessaging(config, {
1570
+ endpoint: `/v1/messaging/conversations/${conversation_id}/messages/${id}/unpin`,
1571
+ method: "PUT",
1572
+ outputSchema: MessagePinResponseSchema
1573
+ });
1574
+ }
1575
+ //#endregion
1576
+ //#region ../../messaging/api-client/src/api/conversations.api.ts
1577
+ /**
1578
+ * Conversations API (v1 messaging)
1579
+ *
1580
+ * Thin, fully-typed wrappers around the Rails messaging controllers.
1581
+ * All functions validate inputs/outputs with zod and use `fetchMessaging`.
1582
+ */
1583
+ /**
1584
+ * GET /api/v1/messaging/announcement_channel
1585
+ */
1586
+ async function getAnnouncementChannel(config) {
1587
+ return fetchMessaging(config, {
1588
+ endpoint: "/v1/messaging/announcement_channel",
1589
+ method: "GET",
1590
+ outputSchema: ConversationResponseSchema
1591
+ });
1592
+ }
1593
+ async function listConversations(config, input) {
1594
+ return fetchMessaging(config, {
1595
+ endpoint: "/v1/messaging/conversations",
1596
+ method: "GET",
1597
+ input,
1598
+ outputSchema: ConversationListResponseSchema
1599
+ });
1600
+ }
1601
+ /**
1602
+ * GET /api/v1/messaging/conversations/search
1603
+ */
1604
+ async function searchConversations(config, input) {
1605
+ return fetchMessaging(config, {
1606
+ endpoint: "/v1/messaging/conversations/search",
1607
+ method: "GET",
1608
+ input,
1609
+ outputSchema: NominalConversationsResponseSchema
1610
+ });
1611
+ }
1612
+ /**
1613
+ * GET /api/v1/messaging/conversations/find
1614
+ */
1615
+ async function findConversation(config, input) {
1616
+ return fetchMessaging(config, {
1617
+ endpoint: "/v1/messaging/conversations/find",
1618
+ method: "GET",
1619
+ input,
1620
+ outputSchema: ConversationResponseSchema
1621
+ });
1622
+ }
1623
+ /**
1624
+ * GET /api/v1/messaging/conversations/:id
1625
+ */
1626
+ async function getConversation(config, id) {
1627
+ return fetchMessaging(config, {
1628
+ endpoint: `/v1/messaging/conversations/${id}`,
1629
+ method: "GET",
1630
+ outputSchema: ConversationResponseSchema
1631
+ });
1632
+ }
1633
+ const conversationCreateInputSchema = z.strictObject({
1634
+ conversation: z.strictObject({
1635
+ kind: z.string().optional(),
1636
+ type: z.string().optional(),
1637
+ name: z.string().optional(),
1638
+ description: z.string().nullable().optional(),
1639
+ broadcasting: z.boolean().optional(),
1640
+ filter: z.record(z.string(), z.unknown()).optional(),
1641
+ auto_add_recipients: z.boolean().optional()
1642
+ }),
1643
+ recipients: z.array(z.strictObject({
1644
+ id: z.number().optional(),
1645
+ uid: z.number().optional(),
1646
+ email: z.string().email().optional(),
1647
+ phone: z.string().optional(),
1648
+ first_name: z.string().optional(),
1649
+ last_name: z.string().optional(),
1650
+ image_url: z.string().url().optional()
1651
+ })).optional()
1652
+ });
1653
+ async function createConversation(config, input) {
1654
+ return fetchMessaging(config, {
1655
+ endpoint: "/v1/messaging/conversations",
1656
+ method: "POST",
1657
+ input,
1658
+ inputSchema: conversationCreateInputSchema,
1659
+ outputSchema: ConversationResponseSchema
1660
+ });
1661
+ }
1662
+ z.strictObject({ conversation: z.strictObject({
1663
+ name: z.string().optional(),
1664
+ description: z.string().nullable().optional(),
1665
+ additional_recipient_params: z.strictObject({
1666
+ receivable_id: z.number(),
1667
+ receivable_type: z.string()
1668
+ }).optional(),
1669
+ auto_add_recipients: z.boolean().optional()
1670
+ }) });
1671
+ //#endregion
1672
+ //#region ../../messaging/api-client/src/api/recipients.api.ts
1673
+ /**
1674
+ * POST /api/v1/messaging/recipients/search
1675
+ */
1676
+ async function searchRecipientsCount(config, input) {
1677
+ return fetchMessaging(config, {
1678
+ endpoint: "/v1/messaging/recipients/search",
1679
+ method: "POST",
1680
+ input,
1681
+ outputSchema: RecipientSearchCountSchema
1682
+ });
1683
+ }
1684
+ /**
1685
+ * GET /api/v1/messaging/conversations/connected_recipients
1686
+ */
1687
+ async function listConnectedRecipients(config, input) {
1688
+ return fetchMessaging(config, {
1689
+ endpoint: "/v1/messaging/conversations/connected_recipients",
1690
+ method: "GET",
1691
+ input,
1692
+ outputSchema: createPaginatedSchema(input?.kind && [
1693
+ "external",
1694
+ "sms",
1695
+ "email"
1696
+ ].includes(input.kind) ? WithContactRecipientSchema : RecipientSchema)
1697
+ });
1698
+ }
1699
+ //#endregion
1700
+ //#region ../../messaging/api-client/src/api/drafts-scheduled.api.ts
1701
+ const saveDraftInputSchema = z.strictObject({ draft_message: z.strictObject({
1702
+ body: z.string().nullable().optional(),
1703
+ replied_to_id: z.number().nullable().optional(),
1704
+ attachments_attributes: z.array(z.strictObject({
1705
+ url: z.string().url(),
1706
+ filename: z.string(),
1707
+ kind: z.string(),
1708
+ content_type: z.string().optional(),
1709
+ content_size: z.number().optional(),
1710
+ metadata: AttachmentInputMetadataSchema,
1711
+ id: z.number().optional(),
1712
+ _destroy: z.boolean().optional()
1713
+ })).optional()
1714
+ }) });
1715
+ async function saveDraft(config, conversation_id, input) {
1716
+ return fetchMessaging(config, {
1717
+ endpoint: `/v1/messaging/conversations/${conversation_id}/save_draft`,
1718
+ method: "PUT",
1719
+ input,
1720
+ inputSchema: saveDraftInputSchema,
1721
+ outputSchema: DraftMessageSchema.or(z.strictObject({}))
1722
+ });
1723
+ }
1724
+ async function destroyDraft(config, conversation_id) {
1725
+ return fetchMessaging(config, {
1726
+ endpoint: `/v1/messaging/conversations/${conversation_id}/destroy_draft`,
1727
+ method: "DELETE",
1728
+ outputSchema: z.unknown()
1729
+ });
1730
+ }
1731
+ /**
1732
+ * Scheduled Messages: per conversation
1733
+ */
1734
+ async function listScheduledMessages(config, conversation_id, input) {
1735
+ return fetchMessaging(config, {
1736
+ endpoint: `/v1/messaging/conversations/${conversation_id}/scheduled_messages`,
1737
+ method: "GET",
1738
+ input,
1739
+ outputSchema: MessageListResponseSchema
1740
+ });
1741
+ }
1742
+ const scheduledMessageInputSchema = z.strictObject({ scheduled_message: z.strictObject({
1743
+ body: z.string().optional(),
1744
+ theme: z.string().optional(),
1745
+ scheduled_at: z.string().datetime({ offset: true }),
1746
+ attachments_attributes: z.array(AttachmentInputSchema).optional(),
1747
+ type: z.string().optional()
1748
+ }) });
1749
+ async function createScheduledMessage(config, conversation_id, input) {
1750
+ return fetchMessaging(config, {
1751
+ endpoint: `/v1/messaging/conversations/${conversation_id}/scheduled_messages`,
1752
+ method: "POST",
1753
+ input,
1754
+ inputSchema: scheduledMessageInputSchema,
1755
+ outputSchema: MessageItemSchema$1
2211
1756
  });
2212
1757
  }
2213
- const _createMessageInputSchema = z.strictObject({
2214
- message: z.strictObject({
2215
- body: z.string().optional(),
2216
- theme: z.string().optional(),
2217
- subject: z.string().optional(),
2218
- type: z.string().optional(),
2219
- attachments_attributes: z.array(AttachmentInputSchema).optional()
2220
- }),
2221
- client_message_id: z.union([z.string(), z.number()]).optional()
2222
- });
2223
- const createMessageInputSchema = _createMessageInputSchema;
2224
- async function createMessage(config, conversation_id, input) {
1758
+ async function updateScheduledMessage(config, conversation_id, id, input) {
2225
1759
  return fetchMessaging(config, {
2226
- endpoint: `/v1/messaging/conversations/${conversation_id}/messages`,
2227
- method: "POST",
1760
+ endpoint: `/v1/messaging/conversations/${conversation_id}/scheduled_messages/${id}`,
1761
+ method: "PUT",
2228
1762
  input,
2229
- inputSchema: createMessageInputSchema,
2230
- outputSchema: MessageItemSchema
1763
+ inputSchema: scheduledMessageInputSchema,
1764
+ outputSchema: MessageItemSchema$1
1765
+ });
1766
+ }
1767
+ async function destroyScheduledMessage(config, conversation_id, id) {
1768
+ return fetchMessaging(config, {
1769
+ endpoint: `/v1/messaging/conversations/${conversation_id}/scheduled_messages/${id}`,
1770
+ method: "DELETE",
1771
+ outputSchema: z.strictObject({
1772
+ success: z.boolean(),
1773
+ message: z.string()
1774
+ })
2231
1775
  });
2232
1776
  }
2233
1777
  /**
2234
- * POST /api/v1/messaging/conversations/:conversation_id/messages/reply
2235
- * Reply to an existing message by id; thread is captured in replied_to_id.
1778
+ * My Scheduled Messages
2236
1779
  */
2237
- async function replyToMessage(config, conversation_id, message_id, input) {
1780
+ async function listMyScheduledMessages(config, input) {
2238
1781
  return fetchMessaging(config, {
2239
- endpoint: `/v1/messaging/conversations/${conversation_id}/messages/reply`,
2240
- method: "POST",
2241
- input: {
2242
- ...input,
2243
- message_id
2244
- },
2245
- inputSchema: _createMessageInputSchema.extend({ message_id: z.number() }),
2246
- outputSchema: MessageItemSchema
1782
+ endpoint: `/v1/messaging/my_scheduled_messages`,
1783
+ method: "GET",
1784
+ input,
1785
+ outputSchema: DraftMessageListResponseSchema
2247
1786
  });
2248
1787
  }
2249
- z.strictObject({ message: z.strictObject({
2250
- body: z.string().optional(),
2251
- theme: z.string().optional()
1788
+ z.strictObject({ bot: z.strictObject({
1789
+ title: z.string(),
1790
+ description: z.string().nullable().optional(),
1791
+ external_uri: z.string().url().nullable().optional(),
1792
+ image_url: z.string().url().nullable().optional(),
1793
+ type: z.string().optional(),
1794
+ avatar_background_color: z.string().nullable().optional(),
1795
+ avatar_emoji: z.string().nullable().optional(),
1796
+ webhook_external_uri: z.string().url().nullable().optional(),
1797
+ webhook_auth_token: z.string().nullable().optional(),
1798
+ webhook_http_method: z.string().nullable().optional(),
1799
+ conversation_ids: z.array(z.number()).optional()
2252
1800
  }) });
1801
+ //#endregion
1802
+ //#region src/messaging/create-messaging-api.ts
2253
1803
  /**
2254
- * POST /api/v1/messaging/conversations/:conversation_id/messages/:message_id/react.json
2255
- * React to a message with an emoji.
1804
+ * Creates a {@link MessagingApi} adapter backed by the messaging API client.
1805
+ * This is the portal's adapter it wires the concrete client to the
1806
+ * interface that messaging-core hooks consume via context.
2256
1807
  */
2257
- async function reactToMessage(config, conversation_id, input) {
2258
- const { message_id, reaction } = input;
2259
- return fetchMessaging(config, {
2260
- endpoint: `/v1/messaging/conversations/${conversation_id}/messages/${message_id}/react.json?reaction=${encodeURIComponent(reaction)}`,
2261
- method: "POST",
2262
- outputSchema: z.string()
1808
+ function createMessagingApi(config) {
1809
+ return {
1810
+ listConversations: (input) => listConversations(config, input),
1811
+ getConversation: (id) => getConversation(config, id),
1812
+ findConversation: (input) => findConversation(config, input),
1813
+ createConversation: (input) => createConversation(config, input),
1814
+ getAnnouncementChannel: () => getAnnouncementChannel(config),
1815
+ listMessages: (cid, input) => listMessages(config, cid, input),
1816
+ createMessage: (cid, input) => createMessage(config, cid, input),
1817
+ replyToMessage: (cid, mid, input) => replyToMessage(config, cid, mid, input),
1818
+ reactToMessage: (cid, input) => reactToMessage(config, cid, input),
1819
+ unreactToMessage: (cid, input) => unreactToMessage(config, cid, input),
1820
+ pinMessage: (cid, mid) => pinMessage(config, cid, mid),
1821
+ unpinMessage: (cid, mid) => unpinMessage(config, cid, mid),
1822
+ listPinnedMessages: (cid, input) => listPinnedMessages(config, cid, input),
1823
+ listScheduledMessages: (cid, input) => listScheduledMessages(config, cid, input),
1824
+ createScheduledMessage: (cid, input) => createScheduledMessage(config, cid, input),
1825
+ updateScheduledMessage: (cid, id, input) => updateScheduledMessage(config, cid, id, input),
1826
+ destroyScheduledMessage: (cid, id) => destroyScheduledMessage(config, cid, id),
1827
+ listMyScheduledMessages: (input) => listMyScheduledMessages(config, input),
1828
+ saveDraft: (cid, input) => saveDraft(config, cid, input),
1829
+ destroyDraft: (cid) => destroyDraft(config, cid),
1830
+ searchRecipientsCount: (input) => searchRecipientsCount(config, input),
1831
+ listConnectedRecipients: (input) => listConnectedRecipients(config, input)
1832
+ };
1833
+ }
1834
+ //#endregion
1835
+ //#region src/messaging/use-messaging-config.ts
1836
+ /**
1837
+ * Hook that derives a MessagingApi adapter from the portal SDK's FluidProvider context.
1838
+ *
1839
+ * Maps FluidSDKConfig fields to a MessagingApiConfig, then wraps the
1840
+ * concrete API client functions into a MessagingApi interface instance
1841
+ * that messaging-core hooks consume via context.
1842
+ */
1843
+ function deriveWebsocketUrl(baseUrl) {
1844
+ return `${baseUrl.replace(/\/+$/, "").replace(/\/api$/, "")}/cable`;
1845
+ }
1846
+ function useMessagingConfig() {
1847
+ const { config } = useFluidContext();
1848
+ const auth = useFluidAuthContext();
1849
+ const getHeaders = useCallback(async () => {
1850
+ const headers = {
1851
+ "Content-Type": "application/json",
1852
+ ...config.defaultHeaders
1853
+ };
1854
+ if (config.getAuthToken) {
1855
+ const token = await config.getAuthToken();
1856
+ if (token) headers.Authorization = `Bearer ${token}`;
1857
+ }
1858
+ return headers;
1859
+ }, [config]);
1860
+ const apiBaseUrl = useMemo(() => {
1861
+ const base = config.baseUrl.replace(/\/+$/, "");
1862
+ return base.endsWith("/api") ? base : `${base}/api`;
1863
+ }, [config.baseUrl]);
1864
+ const apiConfig = useMemo(() => ({
1865
+ baseUrl: apiBaseUrl,
1866
+ getHeaders,
1867
+ ...config.onAuthError != null && { onAuthError: config.onAuthError }
1868
+ }), [
1869
+ apiBaseUrl,
1870
+ config.onAuthError,
1871
+ getHeaders
1872
+ ]);
1873
+ return {
1874
+ apiConfig,
1875
+ messagingApi: useMemo(() => createMessagingApi(apiConfig), [apiConfig]),
1876
+ websocketUrl: useMemo(() => config.websocketUrl ?? deriveWebsocketUrl(config.baseUrl), [config.websocketUrl, config.baseUrl]),
1877
+ token: auth.token
1878
+ };
1879
+ }
1880
+ //#endregion
1881
+ //#region ../../messaging/core/src/query-keys.ts
1882
+ /**
1883
+ * Query key factory for all messaging-related TanStack Query queries.
1884
+ * Ensures consistent key shapes across hooks and cache helpers.
1885
+ */
1886
+ const MESSAGES_QUERY_KEYS = {
1887
+ all: () => ["messages"],
1888
+ conversationMetadata: (conversationId) => [
1889
+ ...MESSAGES_QUERY_KEYS.all(),
1890
+ "conversation",
1891
+ conversationId
1892
+ ],
1893
+ newMessageFlag: (conversationId) => [
1894
+ ...MESSAGES_QUERY_KEYS.all(),
1895
+ "conversation",
1896
+ conversationId,
1897
+ "new_message_flag"
1898
+ ],
1899
+ listConversations: () => [...MESSAGES_QUERY_KEYS.all(), "conversations"],
1900
+ listMessages: (conversationId, messageId, startAtISO) => [
1901
+ ...MESSAGES_QUERY_KEYS.all(),
1902
+ "conversation",
1903
+ conversationId,
1904
+ "messages",
1905
+ messageId ? { startFromMessage: messageId } : startAtISO ? { startFromDate: startAtISO } : { startFromBottom: true }
1906
+ ],
1907
+ listPinnedMessages: (conversationId) => [
1908
+ ...MESSAGES_QUERY_KEYS.all(),
1909
+ "conversation",
1910
+ conversationId,
1911
+ "pinned"
1912
+ ],
1913
+ outbox: (conversationId) => [
1914
+ ...MESSAGES_QUERY_KEYS.all(),
1915
+ "conversation",
1916
+ conversationId,
1917
+ "outbox"
1918
+ ],
1919
+ listScheduledMessages: (conversationId) => [
1920
+ ...MESSAGES_QUERY_KEYS.all(),
1921
+ "conversation",
1922
+ conversationId,
1923
+ "scheduled"
1924
+ ],
1925
+ listMyScheduledMessages: () => [
1926
+ ...MESSAGES_QUERY_KEYS.all(),
1927
+ "scheduled",
1928
+ "mine"
1929
+ ],
1930
+ draft: (conversationId) => [
1931
+ ...MESSAGES_QUERY_KEYS.all(),
1932
+ "draft",
1933
+ conversationId
1934
+ ],
1935
+ announcementChannel: () => [...MESSAGES_QUERY_KEYS.all(), "announcement-channel"],
1936
+ downline: () => [...MESSAGES_QUERY_KEYS.all(), "downline"]
1937
+ };
1938
+ //#endregion
1939
+ //#region ../../messaging/core/src/constants.ts
1940
+ const WEBSOCKET_QUERY_OPTIONS = {
1941
+ staleTime: Infinity,
1942
+ refetchOnMount: "always",
1943
+ refetchOnReconnect: "always",
1944
+ refetchOnWindowFocus: false
1945
+ };
1946
+ /**
1947
+ * Scheduled messages are not websocket-driven; poll to keep fresh.
1948
+ */
1949
+ const SCHEDULED_MESSAGES_STALE_TIME = 6e4;
1950
+ const SCHEDULED_MESSAGES_REFETCH_INTERVAL = 6e4;
1951
+ /**
1952
+ * Pinned messages are not websocket-driven; poll to keep fresh.
1953
+ */
1954
+ const PINNED_MESSAGES_STALE_TIME = 6e4;
1955
+ const PINNED_MESSAGES_REFETCH_INTERVAL = 6e4;
1956
+ //#endregion
1957
+ //#region ../../messaging/core/src/cache-helpers.ts
1958
+ /**
1959
+ * seed conversation metadata caches from any conversations list queries.
1960
+ * returns the matched conversation if conversationId is provided and present in any list cache.
1961
+ */
1962
+ function seedConversationMetadataFromLists(queryClient, conversationId) {
1963
+ let matched;
1964
+ const seedFromData = (data) => {
1965
+ const pages = data?.pages ?? [];
1966
+ for (const page of pages) {
1967
+ const items = page?.[1]?.items ?? [];
1968
+ for (const item of items) {
1969
+ queryClient.setQueryData(MESSAGES_QUERY_KEYS.conversationMetadata(item.id), item);
1970
+ if (!matched && typeof conversationId === "number" && item.id === conversationId) matched = item;
1971
+ }
1972
+ }
1973
+ };
1974
+ seedFromData(queryClient.getQueryData(MESSAGES_QUERY_KEYS.listConversations()));
1975
+ const listQueries = queryClient.getQueryCache().findAll({ predicate: (q) => {
1976
+ const key = q.queryKey;
1977
+ return Array.isArray(key) && key.length >= 2 && key[0] === MESSAGES_QUERY_KEYS.all()[0] && key[1] === MESSAGES_QUERY_KEYS.listConversations()[1];
1978
+ } });
1979
+ for (const q of listQueries) seedFromData(queryClient.getQueryData(q.queryKey));
1980
+ return matched;
1981
+ }
1982
+ function isMessagesQueryKeyForConversation(queryKey, conversationId) {
1983
+ if (!Array.isArray(queryKey)) return false;
1984
+ return queryKey.length >= 4 && queryKey[0] === MESSAGES_QUERY_KEYS.all()[0] && queryKey[1] === "conversation" && queryKey[2] === conversationId && queryKey[3] === "messages";
1985
+ }
1986
+ function findMessageInCache(queryClient, conversationId, messageId) {
1987
+ const queries = queryClient.getQueryCache().findAll({ predicate: (q) => isMessagesQueryKeyForConversation(q.queryKey, conversationId) });
1988
+ for (const query of queries) {
1989
+ const data = queryClient.getQueryData(query.queryKey);
1990
+ if (data) for (const page of data.pages) {
1991
+ const message = page[1].items.find((m) => m.id === messageId);
1992
+ if (message) return message;
1993
+ }
1994
+ }
1995
+ }
1996
+ function forEachMessagesQuery(queryClient, conversationId, fn) {
1997
+ queryClient.getQueryCache().findAll({ predicate: (q) => isMessagesQueryKeyForConversation(q.queryKey, conversationId) }).forEach((q) => fn(q.queryKey));
1998
+ }
1999
+ function isBottomAnchorKey(queryKey) {
2000
+ if (!Array.isArray(queryKey)) return false;
2001
+ const anchor = queryKey[queryKey.length - 1];
2002
+ if (typeof anchor === "object" && anchor !== null) {
2003
+ if ("startFromBottom" in anchor) return anchor.startFromBottom === true;
2004
+ }
2005
+ return false;
2006
+ }
2007
+ function isPinnedQueryKeyForConversation(queryKey, conversationId) {
2008
+ if (!Array.isArray(queryKey)) return false;
2009
+ return queryKey.length >= 4 && queryKey[0] === MESSAGES_QUERY_KEYS.all()[0] && queryKey[1] === "conversation" && queryKey[2] === conversationId && queryKey[3] === "pinned";
2010
+ }
2011
+ function forEachPinnedQuery(queryClient, conversationId, fn) {
2012
+ queryClient.getQueryCache().findAll({ predicate: (q) => isPinnedQueryKeyForConversation(q.queryKey, conversationId) }).forEach((q) => fn(q.queryKey));
2013
+ }
2014
+ async function cancelMessageQueries(queryClient, conversationId) {
2015
+ const queries = queryClient.getQueryCache().findAll({ predicate: (q) => isMessagesQueryKeyForConversation(q.queryKey, conversationId) });
2016
+ await Promise.all(queries.map((q) => queryClient.cancelQueries({ queryKey: q.queryKey })));
2017
+ }
2018
+ async function cancelPinnedQueries(queryClient, conversationId) {
2019
+ const queries = queryClient.getQueryCache().findAll({ predicate: (q) => isPinnedQueryKeyForConversation(q.queryKey, conversationId) });
2020
+ await Promise.all(queries.map((q) => queryClient.cancelQueries({ queryKey: q.queryKey })));
2021
+ }
2022
+ function applyMessagesOptimisticSend({ queryClient, conversationId, optimisticMessage }) {
2023
+ forEachMessagesQuery(queryClient, conversationId, (key) => {
2024
+ if (!isBottomAnchorKey(key)) return;
2025
+ queryClient.setQueryData(key, (oldData) => {
2026
+ const newOptimisticPage = [{ pagination: {
2027
+ current: 1,
2028
+ previous: null,
2029
+ next: null,
2030
+ per_page: oldData?.pages?.[0]?.[0]?.pagination?.per_page ?? 20,
2031
+ pages: oldData?.pages?.[0]?.[0]?.pagination?.pages ?? 1,
2032
+ count: oldData?.pages?.[0]?.[0]?.pagination?.count ?? 0
2033
+ } }, { items: [optimisticMessage] }];
2034
+ if (!oldData) return {
2035
+ pages: [newOptimisticPage],
2036
+ pageParams: [1]
2037
+ };
2038
+ const pages = oldData.pages.map((page, idx) => {
2039
+ if (idx === 0) {
2040
+ const currentItems = page?.[1]?.items ?? [];
2041
+ return [{ pagination: {
2042
+ ...page?.[0]?.pagination ?? {},
2043
+ count: page?.[0]?.pagination?.count ?? 0
2044
+ } }, { items: [optimisticMessage, ...currentItems] }];
2045
+ }
2046
+ return page;
2047
+ });
2048
+ return {
2049
+ ...oldData,
2050
+ pages
2051
+ };
2052
+ });
2053
+ });
2054
+ queryClient.setQueryData(MESSAGES_QUERY_KEYS.outbox(conversationId), (old) => {
2055
+ const prev = old?.items ?? [];
2056
+ if (prev.some((m) => m.id === optimisticMessage.id)) return old;
2057
+ return { items: [...prev, optimisticMessage] };
2263
2058
  });
2264
2059
  }
2265
2060
  /**
2266
- * DELETE /api/v1/messaging/conversations/:conversation_id/messages/:message_id/unreact.json
2267
- * Remove the current recipient's reaction.
2061
+ * markNewMessageFlagForConversation
2062
+ *
2063
+ * sets a lightweight flag query for a conversation indicating a new message
2064
+ * arrived while the user may not be at the bottom. UI can reset it after
2065
+ * scrolling or showing a pill.
2268
2066
  */
2269
- async function unreactToMessage(config, conversation_id, input) {
2270
- const { message_id } = input;
2271
- return fetchMessaging(config, {
2272
- endpoint: `/v1/messaging/conversations/${conversation_id}/messages/${message_id}/unreact.json`,
2273
- method: "DELETE",
2274
- outputSchema: z.string()
2067
+ function markNewMessageFlagForConversation(queryClient, conversationId) {
2068
+ queryClient.setQueryData([
2069
+ MESSAGES_QUERY_KEYS.all()[0],
2070
+ "conversation",
2071
+ conversationId,
2072
+ "new_message_flag"
2073
+ ], true);
2074
+ }
2075
+ function clearNewMessageFlagForConversation(queryClient, conversationId) {
2076
+ queryClient.setQueryData([
2077
+ MESSAGES_QUERY_KEYS.all()[0],
2078
+ "conversation",
2079
+ conversationId,
2080
+ "new_message_flag"
2081
+ ], false);
2082
+ }
2083
+ function applyMessageUpsertToCache({ queryClient, conversationId, message, replaceOptimisticId, clientMessageId }) {
2084
+ forEachMessagesQuery(queryClient, conversationId, (key) => {
2085
+ queryClient.setQueryData(key, (oldData) => {
2086
+ if (!oldData?.pages?.length) {
2087
+ if (!isBottomAnchorKey(key)) return oldData;
2088
+ return {
2089
+ pages: [[{ pagination: {
2090
+ current: 1,
2091
+ previous: null,
2092
+ next: null,
2093
+ per_page: 20,
2094
+ pages: 1,
2095
+ count: 1
2096
+ } }, { items: [message] }]],
2097
+ pageParams: [1]
2098
+ };
2099
+ }
2100
+ const normalizedClientMessageId = (() => {
2101
+ if (typeof clientMessageId === "string" && clientMessageId.length > 0) return clientMessageId;
2102
+ const fromMeta = (() => {
2103
+ const meta = message.metadata;
2104
+ if (meta && typeof meta === "object" && "client_message_id" in meta) {
2105
+ const id = meta.client_message_id;
2106
+ if (typeof id === "string" || typeof id === "number" && Number.isFinite(id)) return id;
2107
+ }
2108
+ })();
2109
+ if (typeof fromMeta === "string" || typeof fromMeta === "number" && Number.isFinite(fromMeta)) return String(fromMeta);
2110
+ })();
2111
+ const finalMessage = (() => {
2112
+ if (!normalizedClientMessageId) return message;
2113
+ const currentMeta = message.metadata;
2114
+ if (currentMeta != null && typeof currentMeta === "object" && (typeof currentMeta.client_message_id === "string" || typeof currentMeta.client_message_id === "number")) return message;
2115
+ return {
2116
+ ...message,
2117
+ metadata: {
2118
+ ...currentMeta && typeof currentMeta === "object" ? currentMeta : {},
2119
+ client_message_id: normalizedClientMessageId
2120
+ }
2121
+ };
2122
+ })();
2123
+ let replaced = false;
2124
+ const pages = oldData.pages.map((page) => {
2125
+ const [meta, payload] = page;
2126
+ const items = payload.items.slice();
2127
+ const idxById = items.findIndex((m) => m.id === (replaceOptimisticId ?? message.id));
2128
+ if (idxById !== -1) {
2129
+ items[idxById] = finalMessage;
2130
+ replaced = true;
2131
+ return [meta, { items }];
2132
+ }
2133
+ if (normalizedClientMessageId) {
2134
+ const idxByClientId = items.findIndex((m) => typeof m?.metadata === "object" && m?.metadata != null && ((id) => id != null && String(id) === normalizedClientMessageId)(m.metadata?.client_message_id));
2135
+ if (idxByClientId !== -1) {
2136
+ items[idxByClientId] = finalMessage;
2137
+ replaced = true;
2138
+ return [meta, { items }];
2139
+ }
2140
+ }
2141
+ const idxExisting = items.findIndex((m) => m.id === message.id);
2142
+ if (idxExisting !== -1) {
2143
+ items[idxExisting] = finalMessage;
2144
+ replaced = true;
2145
+ return [meta, { items }];
2146
+ }
2147
+ return [meta, { items }];
2148
+ });
2149
+ if (!replaced) {
2150
+ if (!isBottomAnchorKey(key)) return oldData;
2151
+ const first = pages[0];
2152
+ const nextPages = pages.slice();
2153
+ const newFirstItems = [finalMessage, ...first?.[1]?.items ?? []];
2154
+ const newCount = (first?.[0]?.pagination?.count ?? 0) + 1;
2155
+ nextPages[0] = [{ pagination: {
2156
+ ...first?.[0]?.pagination ?? {},
2157
+ count: newCount
2158
+ } }, { items: newFirstItems }];
2159
+ return {
2160
+ ...oldData,
2161
+ pages: nextPages
2162
+ };
2163
+ }
2164
+ return {
2165
+ ...oldData,
2166
+ pages
2167
+ };
2168
+ });
2169
+ });
2170
+ queryClient.setQueryData(MESSAGES_QUERY_KEYS.outbox(conversationId), (old) => {
2171
+ if (!old?.items) return old;
2172
+ return { items: old.items.filter((m) => m.id !== (replaceOptimisticId ?? -99999999)) };
2173
+ });
2174
+ }
2175
+ function applyMessageDeleteFromCache({ queryClient, conversationId, messageId }) {
2176
+ forEachMessagesQuery(queryClient, conversationId, (key) => {
2177
+ queryClient.setQueryData(key, (oldData) => {
2178
+ if (!oldData) return oldData;
2179
+ const pages = oldData.pages.map((page) => {
2180
+ const [meta, payload] = page;
2181
+ const items = payload.items.filter((m) => m.id !== messageId);
2182
+ const newCount = Math.max(0, (meta?.pagination?.count ?? 0) - (payload.items.length - items.length));
2183
+ return [{ pagination: {
2184
+ ...meta?.pagination ?? {},
2185
+ count: newCount
2186
+ } }, { items }];
2187
+ });
2188
+ return {
2189
+ ...oldData,
2190
+ pages
2191
+ };
2192
+ });
2193
+ });
2194
+ queryClient.setQueryData(MESSAGES_QUERY_KEYS.outbox(conversationId), (old) => {
2195
+ if (!old?.items) return old;
2196
+ return { items: old.items.filter((m) => m.id !== messageId) };
2197
+ });
2198
+ }
2199
+ function applyReactionUpdateToCache({ queryClient, conversationId, messageId, reaction, reaction_stats }) {
2200
+ forEachMessagesQuery(queryClient, conversationId, (key) => {
2201
+ queryClient.setQueryData(key, (oldData) => {
2202
+ if (!oldData) return oldData;
2203
+ const pages = oldData.pages.map((page) => {
2204
+ const [meta, payload] = page;
2205
+ return [meta, { items: payload.items.map((m) => m.id === messageId ? {
2206
+ ...m,
2207
+ reaction,
2208
+ reaction_stats
2209
+ } : m) }];
2210
+ });
2211
+ return {
2212
+ ...oldData,
2213
+ pages
2214
+ };
2215
+ });
2216
+ });
2217
+ forEachPinnedQuery(queryClient, conversationId, (key) => {
2218
+ queryClient.setQueryData(key, (oldData) => {
2219
+ if (!oldData) return oldData;
2220
+ const pages = oldData.pages.map((page) => {
2221
+ const [meta, payload] = page;
2222
+ return [meta, { items: payload.items.map((m) => m.id === messageId ? {
2223
+ ...m,
2224
+ reaction,
2225
+ reaction_stats
2226
+ } : m) }];
2227
+ });
2228
+ return {
2229
+ ...oldData,
2230
+ pages
2231
+ };
2232
+ });
2233
+ });
2234
+ }
2235
+ function applyReactionStatsToCache({ queryClient, conversationId, messageId, reaction_stats }) {
2236
+ forEachMessagesQuery(queryClient, conversationId, (key) => {
2237
+ queryClient.setQueryData(key, (oldData) => {
2238
+ if (!oldData) return;
2239
+ const pages = oldData.pages.map((page) => {
2240
+ const [meta, payload] = page;
2241
+ return [meta, { items: payload.items.map((m) => m.id === messageId ? {
2242
+ ...m,
2243
+ reaction_stats
2244
+ } : m) }];
2245
+ });
2246
+ return {
2247
+ ...oldData,
2248
+ pages
2249
+ };
2250
+ });
2251
+ });
2252
+ forEachPinnedQuery(queryClient, conversationId, (key) => {
2253
+ queryClient.setQueryData(key, (oldData) => {
2254
+ if (!oldData) return oldData;
2255
+ const pages = oldData.pages.map((page) => {
2256
+ const [meta, payload] = page;
2257
+ return [meta, { items: payload.items.map((m) => m.id === messageId ? {
2258
+ ...m,
2259
+ reaction_stats
2260
+ } : m) }];
2261
+ });
2262
+ return {
2263
+ ...oldData,
2264
+ pages
2265
+ };
2266
+ });
2267
+ });
2268
+ }
2269
+ function applyConversationMetaToCaches({ queryClient, conversation }) {
2270
+ queryClient.setQueryData(MESSAGES_QUERY_KEYS.conversationMetadata(conversation.id), (old) => {
2271
+ if (!old) return old;
2272
+ return {
2273
+ ...old,
2274
+ ...conversation
2275
+ };
2276
+ });
2277
+ const listQueries = queryClient.getQueryCache().findAll({ predicate: (q) => {
2278
+ const key = q.queryKey;
2279
+ return Array.isArray(key) && key.length >= 2 && key[0] === MESSAGES_QUERY_KEYS.all()[0] && key[1] === MESSAGES_QUERY_KEYS.listConversations()[1];
2280
+ } });
2281
+ for (const q of listQueries) queryClient.setQueryData(q.queryKey, (oldData) => {
2282
+ const data = oldData;
2283
+ if (!data?.pages) return oldData;
2284
+ return {
2285
+ ...data,
2286
+ pages: data.pages.map((page) => [page[0], {
2287
+ ...page[1],
2288
+ items: page[1].items.map((conv) => conv.id === conversation.id ? {
2289
+ ...conv,
2290
+ ...typeof conversation.unread === "boolean" ? { unread: conversation.unread } : {},
2291
+ ...typeof conversation.unread_messages_count === "number" ? { unread_messages_count: conversation.unread_messages_count } : {}
2292
+ } : conv)
2293
+ }])
2294
+ };
2275
2295
  });
2276
2296
  }
2277
- /**
2278
- * GET /api/v1/messaging/conversations/:conversation_id/messages/pinned
2279
- * List pinned messages in the conversation.
2280
- */
2281
- async function listPinnedMessages(config, conversation_id, input) {
2282
- return fetchMessaging(config, {
2283
- endpoint: `/v1/messaging/conversations/${conversation_id}/messages/pinned`,
2284
- method: "GET",
2285
- input,
2286
- outputSchema: MessageListResponseSchema
2297
+ function applyConversationDestroyed({ queryClient, conversationId }) {
2298
+ queryClient.removeQueries({ queryKey: MESSAGES_QUERY_KEYS.conversationMetadata(conversationId) });
2299
+ queryClient.setQueryData(MESSAGES_QUERY_KEYS.listConversations(), (oldData) => {
2300
+ const data = oldData;
2301
+ if (!data?.pages) return oldData;
2302
+ return {
2303
+ ...data,
2304
+ pages: data.pages.map((page) => [page[0], {
2305
+ ...page[1],
2306
+ items: page[1].items.filter((c) => c.id !== conversationId)
2307
+ }])
2308
+ };
2287
2309
  });
2288
2310
  }
2289
- /**
2290
- * PUT /api/v1/messaging/conversations/:conversation_id/messages/:id/pin
2291
- * Pin a message (current recipient).
2292
- */
2293
- async function pinMessage(config, conversation_id, id) {
2294
- return fetchMessaging(config, {
2295
- endpoint: `/v1/messaging/conversations/${conversation_id}/messages/${id}/pin`,
2296
- method: "PUT",
2297
- outputSchema: MessagePinResponseSchema
2311
+ function applyMessagePinnedFlag({ queryClient, conversationId, messageId, pinned, message }) {
2312
+ forEachMessagesQuery(queryClient, conversationId, (key) => {
2313
+ queryClient.setQueryData(key, (oldData) => {
2314
+ if (!oldData) return oldData;
2315
+ const pages = oldData.pages.map((page) => {
2316
+ const [meta, payload] = page;
2317
+ return [meta, { items: payload.items.map((m) => m.id === messageId ? {
2318
+ ...m,
2319
+ pinned
2320
+ } : m) }];
2321
+ });
2322
+ return {
2323
+ ...oldData,
2324
+ pages
2325
+ };
2326
+ });
2298
2327
  });
2299
- }
2300
- /**
2301
- * PUT /api/v1/messaging/conversations/:conversation_id/messages/:id/unpin
2302
- * Unpin a message (current recipient).
2303
- */
2304
- async function unpinMessage(config, conversation_id, id) {
2305
- return fetchMessaging(config, {
2306
- endpoint: `/v1/messaging/conversations/${conversation_id}/messages/${id}/unpin`,
2307
- method: "PUT",
2308
- outputSchema: MessagePinResponseSchema
2328
+ forEachPinnedQuery(queryClient, conversationId, (key) => {
2329
+ queryClient.setQueryData(key, (oldData) => {
2330
+ if (!oldData) return oldData;
2331
+ const pages = oldData.pages.map((page, idx) => {
2332
+ const [meta, payload] = page;
2333
+ if (idx !== 0) return page;
2334
+ const exists = payload.items.some((m) => m.id === messageId);
2335
+ if (pinned) {
2336
+ if (exists) return page;
2337
+ const msg = message ?? payload.items.find((m) => m.id === messageId);
2338
+ if (!msg) return page;
2339
+ return [meta, { items: [msg, ...payload.items] }];
2340
+ }
2341
+ return [meta, { items: payload.items.filter((m) => m.id !== messageId) }];
2342
+ });
2343
+ return {
2344
+ ...oldData,
2345
+ pages
2346
+ };
2347
+ });
2309
2348
  });
2310
2349
  }
2311
2350
  //#endregion
2312
- //#region ../../messaging/api-client/src/api/conversations.api.ts
2313
- /**
2314
- * Conversations API (v1 messaging)
2315
- *
2316
- * Thin, fully-typed wrappers around the Rails messaging controllers.
2317
- * All functions validate inputs/outputs with zod and use `fetchMessaging`.
2318
- */
2351
+ //#region ../../messaging/core/src/utils/reaction-utils.ts
2319
2352
  /**
2320
- * GET /api/v1/messaging/announcement_channel
2353
+ * Splits an emoji string into an array of grapheme clusters (perceived characters/emojis).
2354
+ * Uses `Intl.Segmenter` when available for correct multi-codepoint emoji handling.
2355
+ * Falls back to `[...string]` (code-point iteration) on browsers without `Intl.Segmenter`
2356
+ * (e.g. Firefox < 119), which may incorrectly split ZWJ sequences, skin-tone modifiers,
2357
+ * and flag emojis into multiple entries.
2358
+ * @param emojiString The string of emojis to split.
2359
+ * @returns An array of strings, where each string is a single emoji.
2321
2360
  */
2322
- async function getAnnouncementChannel(config) {
2323
- return fetchMessaging(config, {
2324
- endpoint: "/v1/messaging/announcement_channel",
2325
- method: "GET",
2326
- outputSchema: ConversationResponseSchema
2327
- });
2328
- }
2329
- async function listConversations(config, input) {
2330
- return fetchMessaging(config, {
2331
- endpoint: "/v1/messaging/conversations",
2332
- method: "GET",
2333
- input,
2334
- outputSchema: ConversationListResponseSchema
2335
- });
2361
+ function splitEmojis(emojiString) {
2362
+ if (!emojiString) return [];
2363
+ if (typeof Intl !== "undefined" && typeof Intl.Segmenter === "function") {
2364
+ const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
2365
+ return Array.from(segmenter.segment(emojiString)).map((segment) => segment.segment);
2366
+ }
2367
+ return [...emojiString];
2336
2368
  }
2337
2369
  /**
2338
- * GET /api/v1/messaging/conversations/search
2370
+ * Flattens reaction_stats to count individual emojis.
2371
+ * E.g., {"😀😂": 1, "👍": 2, "😀": 1} becomes {"😀": 2, "😂": 1, "👍": 2}
2372
+ * Handles multi-codepoint emojis correctly.
2373
+ * @param stats The raw reaction_stats object from the message.
2374
+ * @returns A record where keys are single emojis and values are their total counts.
2339
2375
  */
2340
- async function searchConversations(config, input) {
2341
- return fetchMessaging(config, {
2342
- endpoint: "/v1/messaging/conversations/search",
2343
- method: "GET",
2344
- input,
2345
- outputSchema: NominalConversationsResponseSchema
2346
- });
2376
+ function flattenReactionStats(stats) {
2377
+ if (!stats) return {};
2378
+ const flattened = {};
2379
+ for (const emojiKey in stats) {
2380
+ const count = stats[emojiKey];
2381
+ if (typeof count !== "number" || count === 0) continue;
2382
+ const individualEmojis = splitEmojis(emojiKey);
2383
+ for (const emoji of individualEmojis) flattened[emoji] = (flattened[emoji] || 0) + count;
2384
+ }
2385
+ return flattened;
2347
2386
  }
2348
2387
  /**
2349
- * GET /api/v1/messaging/conversations/find
2388
+ * Calculates the optimistic state for a message's reactions when a user's reaction changes.
2389
+ * @param currentState The current reaction parts of the message ({ reaction, reaction_stats }).
2390
+ * @param newUserReaction The user's new complete reaction string (e.g., "😀👍", "😂", or null to unreact).
2391
+ * @returns An object with the updated `reaction` and `reaction_stats`.
2350
2392
  */
2351
- async function findConversation(config, input) {
2352
- return fetchMessaging(config, {
2353
- endpoint: "/v1/messaging/conversations/find",
2354
- method: "GET",
2355
- input,
2356
- outputSchema: ConversationResponseSchema
2357
- });
2393
+ function calculateOptimisticReactionUpdate(currentState, newUserReaction) {
2394
+ const previousUserReaction = currentState.reaction;
2395
+ const newStats = { ...currentState.reaction_stats || {} };
2396
+ const finalNewUserReaction = newUserReaction === "" ? null : newUserReaction;
2397
+ if (previousUserReaction === finalNewUserReaction) return {
2398
+ reaction: previousUserReaction,
2399
+ reaction_stats: newStats
2400
+ };
2401
+ if (previousUserReaction && typeof newStats[previousUserReaction] === "number") newStats[previousUserReaction] = Math.max(0, newStats[previousUserReaction] - 1);
2402
+ if (finalNewUserReaction) newStats[finalNewUserReaction] = (typeof newStats[finalNewUserReaction] === "number" ? newStats[finalNewUserReaction] : 0) + 1;
2403
+ return {
2404
+ reaction: finalNewUserReaction,
2405
+ reaction_stats: newStats
2406
+ };
2358
2407
  }
2359
2408
  /**
2360
- * GET /api/v1/messaging/conversations/:id
2409
+ * Generates a unique, sorted string from an array of emoji characters.
2410
+ * Each string in the input array is assumed to be a single, correctly segmented emoji.
2411
+ * E.g., ["😂", "😀", "😂"] becomes "😀😂"
2412
+ * @param emojis An array of single emoji strings (grapheme clusters).
2413
+ * @returns A sorted string of unique emojis, or an empty string if no emojis are provided.
2361
2414
  */
2362
- async function getConversation(config, id) {
2363
- return fetchMessaging(config, {
2364
- endpoint: `/v1/messaging/conversations/${id}`,
2365
- method: "GET",
2366
- outputSchema: ConversationResponseSchema
2367
- });
2368
- }
2369
- const conversationCreateInputSchema = z.strictObject({
2370
- conversation: z.strictObject({
2371
- kind: z.string().optional(),
2372
- type: z.string().optional(),
2373
- name: z.string().optional(),
2374
- description: z.string().nullable().optional(),
2375
- broadcasting: z.boolean().optional(),
2376
- filter: z.record(z.string(), z.unknown()).optional(),
2377
- auto_add_recipients: z.boolean().optional()
2378
- }),
2379
- recipients: z.array(z.strictObject({
2380
- id: z.number().optional(),
2381
- uid: z.number().optional(),
2382
- email: z.string().email().optional(),
2383
- phone: z.string().optional(),
2384
- first_name: z.string().optional(),
2385
- last_name: z.string().optional(),
2386
- image_url: z.string().url().optional()
2387
- })).optional()
2388
- });
2389
- async function createConversation(config, input) {
2390
- return fetchMessaging(config, {
2391
- endpoint: "/v1/messaging/conversations",
2392
- method: "POST",
2393
- input,
2394
- inputSchema: conversationCreateInputSchema,
2395
- outputSchema: ConversationResponseSchema
2396
- });
2415
+ function generateSortedEmojiStringFromArray(emojis) {
2416
+ if (!emojis || emojis.length === 0) return "";
2417
+ return [...new Set(emojis)].sort((a, b) => a.localeCompare(b, "en")).join("");
2397
2418
  }
2398
- z.strictObject({ conversation: z.strictObject({
2399
- name: z.string().optional(),
2400
- description: z.string().nullable().optional(),
2401
- additional_recipient_params: z.strictObject({
2402
- receivable_id: z.number(),
2403
- receivable_type: z.string()
2404
- }).optional(),
2405
- auto_add_recipients: z.boolean().optional()
2406
- }) });
2407
2419
  //#endregion
2408
- //#region ../../messaging/api-client/src/api/recipients.api.ts
2420
+ //#region ../../messaging/core/src/utils/messaging.ts
2421
+ function mapConversationTypeToMessageType(conversationType) {
2422
+ switch (conversationType) {
2423
+ case "DirectConversation": return "DirectMessage";
2424
+ case "SmsConversation": return "SmsMessage";
2425
+ case "EmailConversation": return "EmailMessage";
2426
+ case "ChannelConversation":
2427
+ case "GroupConversation": return "DirectMessage";
2428
+ case "BotConversation": return "BotMessage";
2429
+ default: return "DirectMessage";
2430
+ }
2431
+ }
2409
2432
  /**
2410
- * GET /api/v1/messaging/conversations/connected_recipients
2433
+ * Determines the category of a chat. This is slightly different from the backend's
2434
+ * category field.
2435
+ *
2436
+ * @param item - Conversation item to check
2437
+ * @returns the type of channel
2411
2438
  */
2412
- async function listConnectedRecipients(config, input) {
2413
- return fetchMessaging(config, {
2414
- endpoint: "/v1/messaging/conversations/connected_recipients",
2415
- method: "GET",
2416
- input,
2417
- outputSchema: createPaginatedSchema(input?.kind && [
2418
- "external",
2419
- "sms",
2420
- "email"
2421
- ].includes(input.kind) ? WithContactRecipientSchema : RecipientSchema)
2422
- });
2423
- }
2439
+ const getMessageType = (item) => {
2440
+ if (item.type === "GroupConversation" && item.name.toLowerCase().includes("other")) return {
2441
+ isChannel: false,
2442
+ type: "group-direct-message"
2443
+ };
2444
+ if (item.broadcasting) return {
2445
+ isChannel: true,
2446
+ type: "broadcast"
2447
+ };
2448
+ if (item.type === "GroupConversation") return {
2449
+ isChannel: true,
2450
+ type: "private-channel"
2451
+ };
2452
+ if (item.type === "DirectConversation") return {
2453
+ isChannel: false,
2454
+ type: "direct-message"
2455
+ };
2456
+ if (item.type === "EmailConversation") return {
2457
+ isChannel: false,
2458
+ type: "email"
2459
+ };
2460
+ if (item.type === "SmsConversation") return {
2461
+ isChannel: false,
2462
+ type: "sms"
2463
+ };
2464
+ if (item.type === "ChannelConversation") return {
2465
+ isChannel: true,
2466
+ type: "public-channel"
2467
+ };
2468
+ if (item.type === "BotConversation") return {
2469
+ isChannel: false,
2470
+ type: "bot"
2471
+ };
2472
+ if (item.type === "DownlineConversation") return {
2473
+ isChannel: true,
2474
+ type: "downline"
2475
+ };
2476
+ throw new Error("Invalid conversation type");
2477
+ };
2478
+ const getUserInitials = (conversation, user) => {
2479
+ if (user.phone === conversation.name) return "#";
2480
+ if (user.email === conversation.name) return "@";
2481
+ return [user.first_name?.charAt(0), user.last_name?.charAt(0)].filter(Boolean).join("") || "?";
2482
+ };
2483
+ const getFileTypeFromMimetype = (mimetype) => {
2484
+ if (mimetype.startsWith("image/")) return "image";
2485
+ if (mimetype.startsWith("video/")) return "video";
2486
+ if (mimetype === "application/pdf") return "pdf";
2487
+ if (mimetype.includes("spreadsheet") || mimetype.includes("excel") || mimetype.includes("sheet") || mimetype === "text/csv") return "sheets";
2488
+ if (mimetype.includes("powerpoint") || mimetype.includes("presentation")) return "powerpoint";
2489
+ if (mimetype.includes("document") || mimetype.includes("word") || mimetype.includes("rtf")) return "docx";
2490
+ if (mimetype.startsWith("text/")) return "txt";
2491
+ if (mimetype.includes("zip") || mimetype.includes("compressed") || mimetype.includes("archive") || mimetype.includes("tar") || mimetype.includes("gzip")) return "compressed";
2492
+ if (mimetype.startsWith("audio/")) return "audio";
2493
+ return "unknown";
2494
+ };
2424
2495
  //#endregion
2425
- //#region ../../messaging/api-client/src/api/drafts-scheduled.api.ts
2426
- const saveDraftInputSchema = z.strictObject({ draft_message: z.strictObject({
2427
- body: z.string().nullable().optional(),
2428
- replied_to_id: z.number().nullable().optional(),
2429
- attachments_attributes: z.array(z.strictObject({
2430
- url: z.string().url(),
2431
- filename: z.string(),
2432
- kind: z.string(),
2433
- content_type: z.string().optional(),
2434
- content_size: z.number().optional(),
2435
- metadata: AttachmentInputMetadataSchema,
2436
- id: z.number().optional(),
2437
- _destroy: z.boolean().optional()
2438
- })).optional()
2439
- }) });
2440
- async function saveDraft(config, conversation_id, input) {
2441
- return fetchMessaging(config, {
2442
- endpoint: `/v1/messaging/conversations/${conversation_id}/save_draft`,
2443
- method: "PUT",
2444
- input,
2445
- inputSchema: saveDraftInputSchema,
2446
- outputSchema: DraftMessageSchema.or(z.strictObject({}))
2447
- });
2448
- }
2449
- async function destroyDraft(config, conversation_id) {
2450
- return fetchMessaging(config, {
2451
- endpoint: `/v1/messaging/conversations/${conversation_id}/destroy_draft`,
2452
- method: "DELETE",
2453
- outputSchema: z.unknown()
2454
- });
2455
- }
2496
+ //#region ../../messaging/core/src/utils/string-utils.ts
2456
2497
  /**
2457
- * Scheduled Messages: per conversation
2498
+ * Extracts the leading emoji from a string.
2499
+ * @param text The string to process.
2500
+ * @returns An object containing the emoji and the remaining text.
2458
2501
  */
2459
- async function listScheduledMessages(config, conversation_id, input) {
2460
- return fetchMessaging(config, {
2461
- endpoint: `/v1/messaging/conversations/${conversation_id}/scheduled_messages`,
2462
- method: "GET",
2463
- input,
2464
- outputSchema: MessageListResponseSchema
2465
- });
2466
- }
2467
- const scheduledMessageInputSchema = z.strictObject({ scheduled_message: z.strictObject({
2468
- body: z.string().optional(),
2469
- theme: z.string().optional(),
2470
- scheduled_at: z.string().datetime({ offset: true }),
2471
- attachments_attributes: z.array(AttachmentInputSchema).optional(),
2472
- type: z.string().optional()
2473
- }) });
2474
- async function createScheduledMessage(config, conversation_id, input) {
2475
- return fetchMessaging(config, {
2476
- endpoint: `/v1/messaging/conversations/${conversation_id}/scheduled_messages`,
2477
- method: "POST",
2478
- input,
2479
- inputSchema: scheduledMessageInputSchema,
2480
- outputSchema: MessageItemSchema
2481
- });
2482
- }
2483
- async function updateScheduledMessage(config, conversation_id, id, input) {
2484
- return fetchMessaging(config, {
2485
- endpoint: `/v1/messaging/conversations/${conversation_id}/scheduled_messages/${id}`,
2486
- method: "PUT",
2487
- input,
2488
- inputSchema: scheduledMessageInputSchema,
2489
- outputSchema: MessageItemSchema
2490
- });
2491
- }
2492
- async function destroyScheduledMessage(config, conversation_id, id) {
2493
- return fetchMessaging(config, {
2494
- endpoint: `/v1/messaging/conversations/${conversation_id}/scheduled_messages/${id}`,
2495
- method: "DELETE",
2496
- outputSchema: z.strictObject({
2497
- success: z.boolean(),
2498
- message: z.string()
2499
- })
2500
- });
2502
+ function extractEmoji$1(text) {
2503
+ const match = text.match(/^\p{Emoji_Presentation}/u);
2504
+ if (match) {
2505
+ const emoji = match[0];
2506
+ return {
2507
+ emoji,
2508
+ text: text.substring(emoji.length).trimStart()
2509
+ };
2510
+ }
2511
+ return {
2512
+ emoji: null,
2513
+ text
2514
+ };
2501
2515
  }
2502
2516
  /**
2503
- * My Scheduled Messages
2517
+ * Formats a message channel name by replacing spaces and underscores with hyphens,
2518
+ * converting to lowercase, and removing any non-alphanumeric characters except hyphens.
2519
+ * @param name - The original channel name
2520
+ * @return The formatted channel name
2504
2521
  */
2505
- async function listMyScheduledMessages(config, input) {
2506
- return fetchMessaging(config, {
2507
- endpoint: `/v1/messaging/my_scheduled_messages`,
2508
- method: "GET",
2509
- input,
2510
- outputSchema: DraftMessageListResponseSchema
2511
- });
2522
+ function formatMessageChannelName$1(name) {
2523
+ return name.toLowerCase().replace(/[ _]/g, "-").replace(/[^a-z0-9-]/g, "");
2524
+ }
2525
+ const MAX_GROUP_NAME_LENGTH$1 = 30;
2526
+ function formatGroupDisplayName$1(names, maxLength = MAX_GROUP_NAME_LENGTH$1) {
2527
+ const fullDisplayName = names.join(", ");
2528
+ if (fullDisplayName.length <= maxLength) return { displayName: fullDisplayName };
2529
+ for (let i = 0; i < names.length; i++) {
2530
+ const currentNames = names.slice(0, i + 1);
2531
+ const remainingCount = names.length - currentNames.length;
2532
+ if (remainingCount > 0) {
2533
+ const suffix = `+ ${remainingCount} other${remainingCount > 1 ? "s" : ""}`;
2534
+ if (`${currentNames.join(", ")} ${suffix}`.length > maxLength && i > 0) {
2535
+ const namesToShow = names.slice(0, i);
2536
+ const othersCount = names.length - i;
2537
+ const othersSuffix = `+${othersCount} other${othersCount > 1 ? "s" : ""}`;
2538
+ const truncatedDisplayName = namesToShow.join(", ");
2539
+ if (othersCount === 1) {
2540
+ const nameWithOneMore = names.slice(0, i + 1).join(", ");
2541
+ if (nameWithOneMore.length < truncatedDisplayName.length) return { displayName: nameWithOneMore };
2542
+ }
2543
+ return {
2544
+ displayName: truncatedDisplayName,
2545
+ remainder: othersSuffix
2546
+ };
2547
+ }
2548
+ }
2549
+ }
2550
+ const firstName = names[0] || "";
2551
+ const othersCount = names.length - 1;
2552
+ if (othersCount > 0) return {
2553
+ displayName: firstName,
2554
+ remainder: `+ ${othersCount} other${othersCount > 1 ? "s" : ""}`
2555
+ };
2556
+ return { displayName: firstName };
2557
+ }
2558
+ //#endregion
2559
+ //#region ../../messaging/core/src/utils/file-kinds.ts
2560
+ const ServerFileKindEnum$1 = z.enum([
2561
+ "image",
2562
+ "video",
2563
+ "pdf",
2564
+ "sheets",
2565
+ "powerpoint",
2566
+ "docx"
2567
+ ]);
2568
+ const fallbackFileType$1 = "docx";
2569
+ //#endregion
2570
+ //#region ../../messaging/core/src/messaging-api.ts
2571
+ const MessagingApiContext = createContext(null);
2572
+ const MessagingApiProvider = MessagingApiContext.Provider;
2573
+ function useMessagingApi() {
2574
+ const api = useContext(MessagingApiContext);
2575
+ if (!api) throw new Error("useMessagingApi must be used within a MessagingApiProvider");
2576
+ return api;
2512
2577
  }
2513
- z.strictObject({ bot: z.strictObject({
2514
- title: z.string(),
2515
- description: z.string().nullable().optional(),
2516
- external_uri: z.string().url().nullable().optional(),
2517
- image_url: z.string().url().nullable().optional(),
2518
- type: z.string().optional(),
2519
- avatar_background_color: z.string().nullable().optional(),
2520
- avatar_emoji: z.string().nullable().optional(),
2521
- webhook_external_uri: z.string().url().nullable().optional(),
2522
- webhook_auth_token: z.string().nullable().optional(),
2523
- webhook_http_method: z.string().nullable().optional(),
2524
- conversation_ids: z.array(z.number()).optional()
2525
- }) });
2526
2578
  //#endregion
2527
2579
  //#region ../../messaging/core/src/hooks/useConversations.ts
2528
- function useConversations(config, input) {
2580
+ function useConversations(input) {
2581
+ const api = useMessagingApi();
2529
2582
  return useInfiniteQuery({
2530
2583
  queryKey: [...MESSAGES_QUERY_KEYS.listConversations(), input ?? {}],
2531
- queryFn: ({ pageParam }) => listConversations(config, {
2584
+ queryFn: ({ pageParam }) => api.listConversations({
2532
2585
  ...input || {},
2533
2586
  page: pageParam ?? 1
2534
2587
  }),
@@ -2538,14 +2591,15 @@ function useConversations(config, input) {
2538
2591
  ...WEBSOCKET_QUERY_OPTIONS
2539
2592
  });
2540
2593
  }
2541
- function useConversation(config, conversationId) {
2594
+ function useConversation(conversationId) {
2595
+ const api = useMessagingApi();
2542
2596
  const queryClient = useQueryClient();
2543
2597
  return useQuery({
2544
2598
  queryKey: MESSAGES_QUERY_KEYS.conversationMetadata(conversationId),
2545
2599
  queryFn: async () => {
2546
2600
  const fromSeed = seedConversationMetadataFromLists(queryClient, conversationId);
2547
2601
  if (fromSeed) return fromSeed;
2548
- const result = await getConversation(config, conversationId);
2602
+ const result = await api.getConversation(conversationId);
2549
2603
  applyConversationMetaToCaches({
2550
2604
  queryClient,
2551
2605
  conversation: {
@@ -2561,8 +2615,9 @@ function useConversation(config, conversationId) {
2561
2615
  }
2562
2616
  //#endregion
2563
2617
  //#region ../../messaging/core/src/hooks/useFindConversation.ts
2564
- function useFindConversation(config, params) {
2618
+ function useFindConversation(params) {
2565
2619
  const { recipients, contact_id, conversation_type, enabled } = params;
2620
+ const api = useMessagingApi();
2566
2621
  const useRecipientsFlow = Array.isArray(recipients) && recipients.length > 0;
2567
2622
  const inferredType = conversation_type || (useRecipientsFlow ? recipients.length > 1 ? "GroupConversation" : "DirectConversation" : void 0);
2568
2623
  return useQuery({
@@ -2576,11 +2631,11 @@ function useFindConversation(config, params) {
2576
2631
  conversation_type
2577
2632
  ],
2578
2633
  queryFn: async () => {
2579
- if (useRecipientsFlow) return await createConversation(config, {
2634
+ if (useRecipientsFlow) return await api.createConversation({
2580
2635
  conversation: { type: inferredType },
2581
2636
  recipients: recipients.map((r) => ({ uid: r.uid }))
2582
2637
  });
2583
- if (typeof contact_id === "number" && conversation_type) return await findConversation(config, {
2638
+ if (typeof contact_id === "number" && conversation_type) return await api.findConversation({
2584
2639
  contact_id,
2585
2640
  conversation_type
2586
2641
  });
@@ -2592,20 +2647,22 @@ function useFindConversation(config, params) {
2592
2647
  }
2593
2648
  //#endregion
2594
2649
  //#region ../../messaging/core/src/hooks/useAnnouncementChannel.ts
2595
- function useAnnouncementChannel(config) {
2650
+ function useAnnouncementChannel() {
2651
+ const api = useMessagingApi();
2596
2652
  return useQuery({
2597
2653
  queryKey: MESSAGES_QUERY_KEYS.announcementChannel(),
2598
- queryFn: () => getAnnouncementChannel(config),
2654
+ queryFn: () => api.getAnnouncementChannel(),
2599
2655
  staleTime: Infinity,
2600
2656
  refetchOnWindowFocus: false
2601
2657
  });
2602
2658
  }
2603
2659
  //#endregion
2604
2660
  //#region ../../messaging/core/src/hooks/useScheduledMessages.ts
2605
- function useScheduledMessages$1(config, conversationId, perPage = 20) {
2661
+ function useScheduledMessages$1(conversationId, perPage = 20) {
2662
+ const api = useMessagingApi();
2606
2663
  return useInfiniteQuery({
2607
2664
  queryKey: MESSAGES_QUERY_KEYS.listScheduledMessages(conversationId),
2608
- queryFn: async ({ pageParam }) => listScheduledMessages(config, conversationId, {
2665
+ queryFn: async ({ pageParam }) => api.listScheduledMessages(conversationId, {
2609
2666
  page: pageParam ?? 1,
2610
2667
  per_page: perPage
2611
2668
  }),
@@ -2620,10 +2677,11 @@ function useScheduledMessages$1(config, conversationId, perPage = 20) {
2620
2677
  }
2621
2678
  //#endregion
2622
2679
  //#region ../../messaging/core/src/hooks/useMyScheduledMessages.ts
2623
- function useMyScheduledMessages(config) {
2680
+ function useMyScheduledMessages() {
2681
+ const api = useMessagingApi();
2624
2682
  return useInfiniteQuery({
2625
2683
  queryKey: MESSAGES_QUERY_KEYS.listMyScheduledMessages(),
2626
- queryFn: async ({ pageParam }) => listMyScheduledMessages(config, {
2684
+ queryFn: async ({ pageParam }) => api.listMyScheduledMessages({
2627
2685
  page: pageParam ?? 1,
2628
2686
  per_page: 50
2629
2687
  }),
@@ -2635,12 +2693,13 @@ function useMyScheduledMessages(config) {
2635
2693
  }
2636
2694
  //#endregion
2637
2695
  //#region ../../messaging/core/src/hooks/useReactions.ts
2638
- function useReaction(config) {
2696
+ function useReaction() {
2697
+ const api = useMessagingApi();
2639
2698
  const queryClient = useQueryClient();
2640
2699
  const reactMutation = useMutation({
2641
2700
  mutationFn: async (params) => {
2642
2701
  const { conversationId, messageId, reactionString } = params;
2643
- return reactToMessage(config, conversationId, {
2702
+ return api.reactToMessage(conversationId, {
2644
2703
  message_id: messageId,
2645
2704
  reaction: reactionString
2646
2705
  });
@@ -2688,7 +2747,7 @@ function useReaction(config) {
2688
2747
  const unreactMutation = useMutation({
2689
2748
  mutationFn: async (params) => {
2690
2749
  const { conversationId, messageId } = params;
2691
- return unreactToMessage(config, conversationId, { message_id: messageId });
2750
+ return api.unreactToMessage(conversationId, { message_id: messageId });
2692
2751
  },
2693
2752
  onMutate: async (variables) => {
2694
2753
  const { conversationId, messageId } = variables;
@@ -2737,8 +2796,9 @@ function useReaction(config) {
2737
2796
  }
2738
2797
  //#endregion
2739
2798
  //#region ../../messaging/core/src/hooks/useMessages.ts
2740
- function useMessages$1(config, params, options) {
2799
+ function useMessages$1(params, options) {
2741
2800
  const { conversationId, messageId, perPage = 50 } = params;
2801
+ const api = useMessagingApi();
2742
2802
  const queryClient = useQueryClient();
2743
2803
  const isBottomAnchored = !messageId;
2744
2804
  const query = useInfiniteQuery({
@@ -2747,7 +2807,7 @@ function useMessages$1(config, params, options) {
2747
2807
  const param = pageParam;
2748
2808
  const pageNumber = param?.type === "page" ? param.number ?? 1 : void 0;
2749
2809
  const shouldMarkRead = isBottomAnchored && pageNumber === 1;
2750
- const result = await listMessages(config, conversationId, param?.type === "message" ? {
2810
+ const input = param?.type === "message" ? {
2751
2811
  preview: true,
2752
2812
  message_id: param.id,
2753
2813
  per_page: perPage
@@ -2755,7 +2815,8 @@ function useMessages$1(config, params, options) {
2755
2815
  preview: !shouldMarkRead ? true : false,
2756
2816
  page: pageNumber ?? 1,
2757
2817
  per_page: perPage
2758
- });
2818
+ };
2819
+ const result = await api.listMessages(conversationId, input);
2759
2820
  if (shouldMarkRead) {
2760
2821
  applyConversationMetaToCaches({
2761
2822
  queryClient,
@@ -2824,7 +2885,8 @@ function useMessages$1(config, params, options) {
2824
2885
  }
2825
2886
  //#endregion
2826
2887
  //#region ../../messaging/core/src/hooks/useSendMessage.ts
2827
- function useSendMessage$1(config, conversationId, user, options) {
2888
+ function useSendMessage$1(conversationId, user, options) {
2889
+ const api = useMessagingApi();
2828
2890
  const queryClient = useQueryClient();
2829
2891
  const clientMessageIdRef = useRef(null);
2830
2892
  return useMutation({
@@ -2847,8 +2909,8 @@ function useSendMessage$1(config, conversationId, user, options) {
2847
2909
  },
2848
2910
  client_message_id: clientMessageIdRef.current ?? (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`)
2849
2911
  };
2850
- if (input.replyToId) return replyToMessage(config, conversationId, input.replyToId, payload);
2851
- return createMessage(config, conversationId, payload);
2912
+ if (input.replyToId) return api.replyToMessage(conversationId, input.replyToId, payload);
2913
+ return api.createMessage(conversationId, payload);
2852
2914
  },
2853
2915
  onMutate: async (input) => {
2854
2916
  const rnd = Math.floor(Math.random() * 2147483647);
@@ -2958,8 +3020,9 @@ function useSendMessage$1(config, conversationId, user, options) {
2958
3020
  }
2959
3021
  //#endregion
2960
3022
  //#region ../../messaging/core/src/hooks/useUpdateScheduledMessage.ts
2961
- function useUpdateScheduledMessage$1(config, params, callbacks) {
3023
+ function useUpdateScheduledMessage$1(params, callbacks) {
2962
3024
  const { conversationId, editScheduledId, editScheduledMessage, conversationType } = params;
3025
+ const api = useMessagingApi();
2963
3026
  const queryClient = useQueryClient();
2964
3027
  return useMutation({
2965
3028
  mutationFn: async ({ body, attachments }) => {
@@ -2977,14 +3040,14 @@ function useUpdateScheduledMessage$1(config, params, callbacks) {
2977
3040
  const scheduledAtISO = editScheduledMessage?.scheduled_at || editScheduledMessage?.metadata?.scheduled_at || null;
2978
3041
  if (!scheduledAtISO) throw new Error("Missing scheduled time for the message");
2979
3042
  const messageType = mapConversationTypeToMessageType(conversationType);
2980
- await createScheduledMessage(config, conversationId, { scheduled_message: {
3043
+ await api.createScheduledMessage(conversationId, { scheduled_message: {
2981
3044
  body,
2982
3045
  theme: "inquiry",
2983
3046
  scheduled_at: new Date(scheduledAtISO).toISOString(),
2984
3047
  type: messageType,
2985
3048
  attachments_attributes: mappedAttachments
2986
3049
  } });
2987
- await destroyScheduledMessage(config, conversationId, editScheduledId);
3050
+ await api.destroyScheduledMessage(conversationId, editScheduledId);
2988
3051
  },
2989
3052
  onSuccess: async () => {
2990
3053
  await Promise.all([queryClient.invalidateQueries({ queryKey: MESSAGES_QUERY_KEYS.listScheduledMessages(conversationId) }), queryClient.invalidateQueries({ queryKey: MESSAGES_QUERY_KEYS.listMyScheduledMessages() })]);
@@ -3446,6 +3509,39 @@ function getConfig(name) {
3446
3509
  }
3447
3510
  //#endregion
3448
3511
  //#region ../../messaging/core/src/cable/schemas.ts
3512
+ /**
3513
+ * Locally-defined schemas for WebSocket event parsing.
3514
+ * These replace imports from the API client to keep core dependency-free.
3515
+ */
3516
+ const ReactionStatsSchema = z.record(z.string(), z.number());
3517
+ /**
3518
+ * Lenient schema for parsing incoming WebSocket message payloads.
3519
+ * Uses passthrough() to accept extra/unknown fields from the server.
3520
+ */
3521
+ const MessageItemSchema = z.object({
3522
+ id: z.number(),
3523
+ body: z.string(),
3524
+ type: z.string(),
3525
+ created_at: z.string(),
3526
+ updated_at: z.string().nullable().optional(),
3527
+ deleted_at: z.string().nullable().optional(),
3528
+ edited_at: z.string().nullable().optional(),
3529
+ conversation_id: z.number(),
3530
+ status: z.string(),
3531
+ attachments: z.array(z.any()),
3532
+ reaction_stats: z.record(z.string(), z.number()).nullable(),
3533
+ reaction: z.string().nullable().optional(),
3534
+ metadata: z.any().optional(),
3535
+ emoji_only_count: z.number().optional(),
3536
+ mentioned_recipients: z.array(z.any()),
3537
+ sent_at: z.string().optional(),
3538
+ pinned: z.boolean().optional(),
3539
+ scheduled_at: z.string().optional(),
3540
+ replied_to_message: z.any().nullable().optional(),
3541
+ sender_recipient: z.any(),
3542
+ seen_by: z.unknown(),
3543
+ seen_by_count: z.unknown()
3544
+ }).passthrough();
3449
3545
  const NewMessageSchema = z.strictObject({
3450
3546
  type: z.literal("new_message"),
3451
3547
  message: z.string(),
@@ -3999,7 +4095,7 @@ function createFluidFileUploader(apiKey) {
3999
4095
  //#endregion
4000
4096
  //#region ../../messaging/ui/src/app/MessagingAppProvider.tsx
4001
4097
  const MessagingAppContext = createContext(null);
4002
- function MessagingAppProvider({ config, auth, websocketUrl, token, renderImage, renderProfileTrigger, onNavigate, initialRoute, children }) {
4098
+ function MessagingAppProvider({ api, auth, websocketUrl, token, renderImage, renderProfileTrigger, onNavigate, initialRoute, children }) {
4003
4099
  const [currentRoute, setCurrentRoute] = useState(() => initialRoute ?? { view: "index" });
4004
4100
  const navigation = {
4005
4101
  navigate: useCallback((route) => {
@@ -4015,17 +4111,19 @@ function MessagingAppProvider({ config, auth, websocketUrl, token, renderImage,
4015
4111
  }, [currentRoute.view, onNavigate]),
4016
4112
  currentRoute
4017
4113
  };
4018
- return /* @__PURE__ */ jsx(MessagingAppContext.Provider, {
4019
- value: {
4020
- config,
4021
- auth,
4022
- websocketUrl,
4023
- token,
4024
- renderImage,
4025
- renderProfileTrigger,
4026
- navigation
4027
- },
4028
- children
4114
+ return /* @__PURE__ */ jsx(MessagingApiProvider, {
4115
+ value: api,
4116
+ children: /* @__PURE__ */ jsx(MessagingAppContext.Provider, {
4117
+ value: {
4118
+ auth,
4119
+ websocketUrl,
4120
+ token,
4121
+ renderImage,
4122
+ renderProfileTrigger,
4123
+ navigation
4124
+ },
4125
+ children
4126
+ })
4029
4127
  });
4030
4128
  }
4031
4129
  function useMessagingApp() {
@@ -4255,12 +4353,12 @@ function getPageTitleFromRoute(route) {
4255
4353
  }
4256
4354
  }
4257
4355
  function MessagesTopBar({ renderSidebarTrigger, renderBreadcrumb, history, onToast }) {
4258
- const { config, auth, renderImage: contextRenderImage } = useMessagingApp();
4356
+ const { auth, renderImage: contextRenderImage } = useMessagingApp();
4259
4357
  const navigation = useMessagingNavigation();
4260
4358
  const currentRoute = navigation.currentRoute;
4261
4359
  const renderImage = contextRenderImage ?? defaultRenderImage;
4262
4360
  const conversationId = "conversationId" in currentRoute ? currentRoute.conversationId : 0;
4263
- const { data: conversation } = useConversation(config, conversationId);
4361
+ const { data: conversation } = useConversation(conversationId);
4264
4362
  const currentUser = auth.currentUser;
4265
4363
  const affiliateId = currentUser?.affiliateId;
4266
4364
  const otherUsers = conversation?.recipients.filter((r) => r.receivable_id !== affiliateId);
@@ -4720,9 +4818,9 @@ function Channel({ item, renderImage }) {
4720
4818
  })] });
4721
4819
  }
4722
4820
  function SidebarItem({ item }) {
4723
- const { config, auth, renderImage: contextRenderImage } = useMessagingApp();
4821
+ const { auth, renderImage: contextRenderImage } = useMessagingApp();
4724
4822
  const navigation = useMessagingNavigation();
4725
- const { data: meta } = useConversation(config, item.id);
4823
+ const { data: meta } = useConversation(item.id);
4726
4824
  const renderImage = contextRenderImage ?? defaultRenderImage;
4727
4825
  const currentUser = auth.currentUser;
4728
4826
  if (!currentUser) return null;
@@ -4886,7 +4984,6 @@ function useScrollerRef() {
4886
4984
  return context;
4887
4985
  }
4888
4986
  function MessagingLayout({ children, topBar, renderLockedDownlineItem, renderNewChannelSidebar, showAdminFeatures = true, canUseMyDownline = false, downlineData }) {
4889
- const { config } = useMessagingApp();
4890
4987
  const navigation = useMessagingNavigation();
4891
4988
  const [isDragging, setIsDragging] = useState(false);
4892
4989
  const sidebarRef = useRef(null);
@@ -4924,8 +5021,8 @@ function MessagingLayout({ children, topBar, renderLockedDownlineItem, renderNew
4924
5021
  handleMouseMove,
4925
5022
  handleMouseUp
4926
5023
  ]);
4927
- const result = useConversations(config, { per_page: CHANNELS_PER_FETCH });
4928
- const { data: announcementChannel, isLoading: isLoadingAnnouncementChannel } = useAnnouncementChannel(config);
5024
+ const result = useConversations({ per_page: CHANNELS_PER_FETCH });
5025
+ const { data: announcementChannel, isLoading: isLoadingAnnouncementChannel } = useAnnouncementChannel();
4929
5026
  const hasMyDownline = downlineData?.hasDownline ?? false;
4930
5027
  const myDownlineConversation = downlineData?.conversation ?? null;
4931
5028
  const allEntries = result.data?.pages.flatMap((page) => page[1].items) ?? [];
@@ -38605,11 +38702,12 @@ function Timestamp({ children: timestamp, className = "", displayMode = "auto" }
38605
38702
  }
38606
38703
  //#endregion
38607
38704
  //#region ../../messaging/ui/src/components/Message.tsx
38608
- function Message({ conversation, message, isGrouped, highlightType = "auto", config, renderProfileTrigger, renderProfileContent, renderImage, onPinError, onCopyLinkSuccess, onCopyLinkError, getMessageLink }) {
38705
+ function Message({ conversation, message, isGrouped, highlightType = "auto", renderProfileTrigger, renderProfileContent, renderImage, onPinError, onCopyLinkSuccess, onCopyLinkError, getMessageLink }) {
38609
38706
  const [emojiPickerOpen, setEmojiPickerOpen] = useState(false);
38610
38707
  const [optionsOpen, setOptionsOpen] = useState(false);
38611
38708
  const queryClient = useQueryClient();
38612
- const { react, unreact } = useReaction(config);
38709
+ const { react, unreact } = useReaction();
38710
+ const api = useMessagingApi();
38613
38711
  const addReaction = (reaction) => {
38614
38712
  react({
38615
38713
  conversationId: conversation.id,
@@ -38635,7 +38733,8 @@ function Message({ conversation, message, isGrouped, highlightType = "auto", con
38635
38733
  });
38636
38734
  setOptimisticPinned(newPinned);
38637
38735
  try {
38638
- await (newPinned ? pinMessage : unpinMessage)(config, conversation.id, message.id);
38736
+ if (newPinned) await api.pinMessage(conversation.id, message.id);
38737
+ else await api.unpinMessage(conversation.id, message.id);
38639
38738
  } catch {
38640
38739
  applyMessagePinnedFlag({
38641
38740
  queryClient,
@@ -39299,8 +39398,8 @@ const deepEqual = (obj1, obj2) => {
39299
39398
  * Hook to manage conversation draft state using React Query as the single source of truth.
39300
39399
  * Initializes with data from conversation metadata to avoid loading delays.
39301
39400
  */
39302
- function useConversationDraft(config, conversationId) {
39303
- const { data: conversationMetadata } = useConversation(config, conversationId);
39401
+ function useConversationDraft(conversationId) {
39402
+ const { data: conversationMetadata } = useConversation(conversationId);
39304
39403
  const queryClient = useQueryClient();
39305
39404
  const isLocalDraft = !conversationId;
39306
39405
  const abortFunctionsRef = useRef(/* @__PURE__ */ new Map());
@@ -39625,13 +39724,14 @@ function useIsDragging() {
39625
39724
  }
39626
39725
  //#endregion
39627
39726
  //#region ../../messaging/ui/src/hooks/use-save-draft.ts
39628
- function useSaveDraft(config, conversationId, options) {
39727
+ function useSaveDraft(conversationId, options) {
39629
39728
  const queryClient = useQueryClient();
39729
+ const api = useMessagingApi();
39630
39730
  return useMutation({
39631
39731
  mutationFn: async () => {
39632
39732
  if (!conversationId) return null;
39633
39733
  try {
39634
- await destroyDraft(config, conversationId);
39734
+ await api.destroyDraft(conversationId);
39635
39735
  } catch (err) {
39636
39736
  if (err.status !== 404) console.warn("destroyDraft failed (continuing with save):", err);
39637
39737
  }
@@ -39649,7 +39749,7 @@ function useSaveDraft(config, conversationId, options) {
39649
39749
  const bodyIsEmpty = !latestBody.trim();
39650
39750
  const hasFiles = latestAttachments.length > 0;
39651
39751
  if (bodyIsEmpty && !hasFiles) return {};
39652
- return await saveDraft(config, conversationId, { draft_message: {
39752
+ return await api.saveDraft(conversationId, { draft_message: {
39653
39753
  body: latestBody,
39654
39754
  attachments_attributes: hasFiles ? latestAttachments : void 0
39655
39755
  } });
@@ -39685,10 +39785,10 @@ function useSaveDraft(config, conversationId, options) {
39685
39785
  }
39686
39786
  });
39687
39787
  }
39688
- function useDebouncedSaveDraft(config, conversationId, options) {
39788
+ function useDebouncedSaveDraft(conversationId, options) {
39689
39789
  const debounceMs = options?.debounceMs ?? 1e3;
39690
39790
  const debounceTimerRef = useRef(null);
39691
- const saveDraftMutation = useSaveDraft(config, conversationId, { onError: options?.onError });
39791
+ const saveDraftMutation = useSaveDraft(conversationId, { onError: options?.onError });
39692
39792
  const debouncedSave = () => {
39693
39793
  if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
39694
39794
  debounceTimerRef.current = setTimeout(() => {
@@ -52200,7 +52300,8 @@ function convertMentionTokensInJson(node, users) {
52200
52300
  }
52201
52301
  //#endregion
52202
52302
  //#region ../../messaging/ui/src/components/composer/useComposerActions.ts
52203
- function useComposerActions({ editor, onSend, onSaveScheduledEdit, isUploadingFiles, completedFiles, clearDebounce, saveImmediately, draft, mode, sendWithEnter, conversationType, conversationId, config, onScheduleSuccess, onScheduleComplete, onScheduleError }) {
52303
+ function useComposerActions({ editor, onSend, onSaveScheduledEdit, isUploadingFiles, completedFiles, clearDebounce, saveImmediately, draft, mode, sendWithEnter, conversationType, conversationId, onScheduleSuccess, onScheduleComplete, onScheduleError }) {
52304
+ const api = useMessagingApi();
52204
52305
  const handleSend = useCallback(async () => {
52205
52306
  if (!editor || !onSend) return;
52206
52307
  if (isUploadingFiles) return;
@@ -52270,7 +52371,7 @@ function useComposerActions({ editor, onSend, onSaveScheduledEdit, isUploadingFi
52270
52371
  return parsed.success ? parsed.data : fallbackFileType;
52271
52372
  };
52272
52373
  const messageType = mapConversationTypeToMessageType(conversationType);
52273
- await createScheduledMessage(config, conversationId, { scheduled_message: {
52374
+ await api.createScheduledMessage(conversationId, { scheduled_message: {
52274
52375
  body: markdown,
52275
52376
  theme: "inquiry",
52276
52377
  scheduled_at: scheduledAt.toISOString(),
@@ -52301,7 +52402,7 @@ function useComposerActions({ editor, onSend, onSaveScheduledEdit, isUploadingFi
52301
52402
  completedFiles,
52302
52403
  conversationId,
52303
52404
  conversationType,
52304
- config,
52405
+ api,
52305
52406
  draft,
52306
52407
  saveImmediately,
52307
52408
  onScheduleSuccess,
@@ -52313,11 +52414,11 @@ function useComposerActions({ editor, onSend, onSaveScheduledEdit, isUploadingFi
52313
52414
  }
52314
52415
  //#endregion
52315
52416
  //#region ../../messaging/ui/src/components/composer/MessageComposer.tsx
52316
- function MessageComposer({ onSend, onScheduleSuccess, onSaveScheduledEdit, placeholder = "Type your message...", disabled = false, mentionableUsers, conversationType, conversationId, saveDrafts = true, mode = "normal", initialEditData, config, uploader, canSchedule = false, renderUpgradePrompt, renderLinkModal, renderImage, onScheduleComplete, onScheduleError, onUploadError }) {
52417
+ function MessageComposer({ onSend, onScheduleSuccess, onSaveScheduledEdit, placeholder = "Type your message...", disabled = false, mentionableUsers, conversationType, conversationId, saveDrafts = true, mode = "normal", initialEditData, uploader, canSchedule = false, renderUpgradePrompt, renderLinkModal, renderImage, onScheduleComplete, onScheduleError, onUploadError }) {
52317
52418
  const [sendWithEnter] = use_local_storage_state_default("messages-send-with-enter", { defaultValue: true });
52318
52419
  const effectiveConversationId = saveDrafts ? conversationId : 0;
52319
- const draft = useConversationDraft(config, effectiveConversationId);
52320
- const { debouncedSave, saveImmediately, isSaving, clearDebounce } = useDebouncedSaveDraft(config, effectiveConversationId);
52420
+ const draft = useConversationDraft(effectiveConversationId);
52421
+ const { debouncedSave, saveImmediately, isSaving, clearDebounce } = useDebouncedSaveDraft(effectiveConversationId);
52321
52422
  const { handleFiles, handleRemoveFile } = useDraftFileUpload({
52322
52423
  uploader,
52323
52424
  draftApi: {
@@ -52441,7 +52542,6 @@ function MessageComposer({ onSend, onScheduleSuccess, onSaveScheduledEdit, place
52441
52542
  sendWithEnter,
52442
52543
  conversationType,
52443
52544
  conversationId,
52444
- config,
52445
52545
  onScheduleSuccess,
52446
52546
  onScheduleComplete,
52447
52547
  onScheduleError
@@ -52495,13 +52595,12 @@ function MessageComposer({ onSend, onScheduleSuccess, onSaveScheduledEdit, place
52495
52595
  }
52496
52596
  //#endregion
52497
52597
  //#region ../../messaging/ui/src/app/MessagesViewComposer.tsx
52498
- function MessagesViewComposer({ conversationId, conversationMetadata, config, uploader, saveDrafts, isEditingScheduled, editScheduledMessage, mentionableUsers, renderImage, isLoading, user, isSendPending, isUpdatePending, onSend, onSaveScheduledEdit, onScheduleSuccess }) {
52598
+ function MessagesViewComposer({ conversationId, conversationMetadata, uploader, saveDrafts, isEditingScheduled, editScheduledMessage, mentionableUsers, renderImage, isLoading, user, isSendPending, isUpdatePending, onSend, onSaveScheduledEdit, onScheduleSuccess }) {
52499
52599
  const input = /* @__PURE__ */ jsx(MessageComposer, {
52500
52600
  conversationId,
52501
52601
  conversationType: conversationMetadata?.type,
52502
52602
  saveDrafts: isEditingScheduled ? false : saveDrafts,
52503
52603
  mode: isEditingScheduled ? "editScheduled" : "normal",
52504
- config,
52505
52604
  uploader,
52506
52605
  initialEditData: isEditingScheduled && editScheduledMessage ? {
52507
52606
  body: editScheduledMessage.body || "",
@@ -52562,13 +52661,11 @@ function HeaderSlot({ render, conversation }) {
52562
52661
  return /* @__PURE__ */ jsx(Fragment$1, { children: render(conversation) });
52563
52662
  }
52564
52663
  function ViewMessage({ conversation, message, isGrouped, highlightType = "auto", renderProfileTrigger, renderProfileContent, renderImage, onToast, getMessageLink }) {
52565
- const { config } = useMessagingApp();
52566
52664
  return /* @__PURE__ */ jsx(Message, {
52567
52665
  conversation,
52568
52666
  message,
52569
52667
  isGrouped,
52570
52668
  highlightType,
52571
- config,
52572
52669
  renderProfileTrigger,
52573
52670
  renderProfileContent,
52574
52671
  renderImage,
@@ -52610,7 +52707,7 @@ const getFallbackSystemMessageText = (senderName, conversationMetadata) => {
52610
52707
  };
52611
52708
  function MessagesView({ conversationId, messageId, saveDrafts, noDateResults, renderProfileTrigger, renderProfileContent, onToast, getMessageLink, renderScheduledBanner, renderHeader, onMarkRead, uploader }) {
52612
52709
  const queryClient = useQueryClient();
52613
- const { config, auth, renderImage: contextRenderImage } = useMessagingApp();
52710
+ const { auth, renderImage: contextRenderImage } = useMessagingApp();
52614
52711
  const navigation = useMessagingNavigation();
52615
52712
  const scrollerRef = useScrollerRef();
52616
52713
  const renderImage = contextRenderImage ?? defaultRenderImage;
@@ -52649,7 +52746,6 @@ function MessagesView({ conversationId, messageId, saveDrafts, noDateResults, re
52649
52746
  scrollTo
52650
52747
  ]);
52651
52748
  const { messages, fetchNextPage, hasNextPage, isFetchingNextPage, fetchPreviousPage, hasPreviousPage, isFetchingPreviousPage, isLoading: isLoadingMessages, isError, error: messagesError } = useMessages({
52652
- config,
52653
52749
  conversationId,
52654
52750
  messageId: messageId ? Number(messageId) : void 0,
52655
52751
  perPage: 50,
@@ -52659,7 +52755,7 @@ function MessagesView({ conversationId, messageId, saveDrafts, noDateResults, re
52659
52755
  const lastId = messages[messages.length - 1]?.id ?? "x";
52660
52756
  return `${messages.length}:${String(lastId)}`;
52661
52757
  }, [messages]);
52662
- const { data: conversationMetadata, error: metadataError, isLoading: isLoadingMetadata } = useConversation(config, conversationId);
52758
+ const { data: conversationMetadata, error: metadataError, isLoading: isLoadingMetadata } = useConversation(conversationId);
52663
52759
  useEffect(() => {
52664
52760
  baselineCapturedRef.current = false;
52665
52761
  setEntryUnreadBaselineId(null);
@@ -52678,7 +52774,7 @@ function MessagesView({ conversationId, messageId, saveDrafts, noDateResults, re
52678
52774
  scrollToBottom,
52679
52775
  resetKey: conversationId
52680
52776
  });
52681
- const scheduledHook = useScheduledMessages(config, conversationId);
52777
+ const scheduledHook = useScheduledMessages(conversationId);
52682
52778
  const scheduledData = scheduledHook.data;
52683
52779
  const refetchScheduled = scheduledHook.refetch;
52684
52780
  const editScheduledId = (() => {
@@ -52717,7 +52813,6 @@ function MessagesView({ conversationId, messageId, saveDrafts, noDateResults, re
52717
52813
  };
52718
52814
  }, [currentUser]);
52719
52815
  const sendMessageMutation = useSendMessage({
52720
- config,
52721
52816
  conversationId,
52722
52817
  user,
52723
52818
  onSuccess: () => {
@@ -52731,7 +52826,6 @@ function MessagesView({ conversationId, messageId, saveDrafts, noDateResults, re
52731
52826
  }
52732
52827
  });
52733
52828
  const updateScheduledMutation = useUpdateScheduledMessage({
52734
- config,
52735
52829
  conversationId,
52736
52830
  editScheduledId,
52737
52831
  editScheduledMessage,
@@ -53029,7 +53123,6 @@ function MessagesView({ conversationId, messageId, saveDrafts, noDateResults, re
53029
53123
  const composer = /* @__PURE__ */ jsx(MessagesViewComposer, {
53030
53124
  conversationId,
53031
53125
  conversationMetadata,
53032
- config,
53033
53126
  uploader,
53034
53127
  saveDrafts,
53035
53128
  isEditingScheduled,
@@ -53161,24 +53254,24 @@ function MessagesView({ conversationId, messageId, saveDrafts, noDateResults, re
53161
53254
  }, "composer")]
53162
53255
  }, "layer-1");
53163
53256
  }
53164
- function useMessages({ config, conversationId, messageId, perPage, onMarkRead }) {
53165
- return useMessages$1(config, {
53257
+ function useMessages({ conversationId, messageId, perPage, onMarkRead }) {
53258
+ return useMessages$1({
53166
53259
  conversationId,
53167
53260
  messageId,
53168
53261
  perPage
53169
53262
  }, { onMarkRead });
53170
53263
  }
53171
- function useSendMessage({ config, conversationId, user, onSuccess, onError }) {
53172
- return useSendMessage$1(config, conversationId, user, {
53264
+ function useSendMessage({ conversationId, user, onSuccess, onError }) {
53265
+ return useSendMessage$1(conversationId, user, {
53173
53266
  onSuccess,
53174
53267
  onError
53175
53268
  });
53176
53269
  }
53177
- function useScheduledMessages(config, conversationId) {
53178
- return useScheduledMessages$1(config, conversationId, 50);
53270
+ function useScheduledMessages(conversationId) {
53271
+ return useScheduledMessages$1(conversationId, 50);
53179
53272
  }
53180
- function useUpdateScheduledMessage({ config, conversationId, editScheduledId, editScheduledMessage, conversationType, onSuccess, onError }) {
53181
- return useUpdateScheduledMessage$1(config, {
53273
+ function useUpdateScheduledMessage({ conversationId, editScheduledId, editScheduledMessage, conversationType, onSuccess, onError }) {
53274
+ return useUpdateScheduledMessage$1({
53182
53275
  conversationId,
53183
53276
  editScheduledId,
53184
53277
  editScheduledMessage,
@@ -53191,13 +53284,12 @@ function useUpdateScheduledMessage({ config, conversationId, editScheduledId, ed
53191
53284
  //#endregion
53192
53285
  //#region ../../messaging/ui/src/app/PinnedMessagesView.tsx
53193
53286
  function PinnedMessage({ conversation, message, onToast, renderProfileContent, getMessageLink }) {
53194
- const { config, renderImage, renderProfileTrigger } = useMessagingApp();
53287
+ const { renderImage, renderProfileTrigger } = useMessagingApp();
53195
53288
  return /* @__PURE__ */ jsx(Message, {
53196
53289
  conversation,
53197
53290
  message,
53198
53291
  isGrouped: false,
53199
53292
  highlightType: "never",
53200
- config,
53201
53293
  renderProfileTrigger: renderProfileTrigger ? ({ children }) => /* @__PURE__ */ jsx("div", { children }) : void 0,
53202
53294
  renderProfileContent,
53203
53295
  renderImage,
@@ -53208,12 +53300,12 @@ function PinnedMessage({ conversation, message, onToast, renderProfileContent, g
53208
53300
  });
53209
53301
  }
53210
53302
  function PinnedMessagesView({ conversationId, onToast, renderProfileContent, getMessageLink }) {
53211
- const { config } = useMessagingApp();
53212
53303
  const { navigate } = useMessagingNavigation();
53304
+ const api = useMessagingApi();
53213
53305
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isError, error } = useInfiniteQuery({
53214
53306
  queryKey: MESSAGES_QUERY_KEYS.listPinnedMessages(conversationId),
53215
53307
  queryFn: async ({ pageParam }) => {
53216
- return listPinnedMessages(config, conversationId, {
53308
+ return api.listPinnedMessages(conversationId, {
53217
53309
  page: pageParam,
53218
53310
  per_page: 50
53219
53311
  });
@@ -53224,7 +53316,7 @@ function PinnedMessagesView({ conversationId, onToast, renderProfileContent, get
53224
53316
  staleTime: PINNED_MESSAGES_STALE_TIME,
53225
53317
  refetchInterval: PINNED_MESSAGES_REFETCH_INTERVAL
53226
53318
  });
53227
- const { data: conversationMetadata } = useConversation(config, conversationId);
53319
+ const { data: conversationMetadata } = useConversation(conversationId);
53228
53320
  const messages = useMemo(() => {
53229
53321
  if (!data?.pages) return [];
53230
53322
  return data.pages.flatMap((pageData) => pageData[1].items);
@@ -53841,10 +53933,9 @@ function getConversationIdFromRecipients(recipients) {
53841
53933
  return conversationRecipient ? conversationRecipient.id : null;
53842
53934
  }
53843
53935
  function NewMessageView({ searchUsers, searchChannels, getCachedChannels, messagesViewProps }) {
53844
- const { config } = useMessagingApp();
53845
53936
  const [selectedRecipients, setSelectedRecipients] = useState([]);
53846
53937
  const selectedConversationId = useMemo(() => getConversationIdFromRecipients(selectedRecipients), [selectedRecipients]);
53847
- const { data: foundConversation } = useFindConversation(config, {
53938
+ const { data: foundConversation } = useFindConversation({
53848
53939
  recipients: selectedRecipients.length > 0 && !selectedConversationId ? selectedRecipients.map((r) => ({ uid: r.id })) : void 0,
53849
53940
  enabled: selectedRecipients.length > 0 && !selectedConversationId
53850
53941
  });
@@ -54104,9 +54195,10 @@ function formatScheduledShort(iso) {
54104
54195
  }
54105
54196
  }
54106
54197
  function ScheduledMessageRow({ message, onToast }) {
54107
- const { config, auth, renderImage } = useMessagingApp();
54198
+ const { auth, renderImage } = useMessagingApp();
54108
54199
  const { navigate } = useMessagingNavigation();
54109
54200
  const queryClient = useQueryClient();
54201
+ const api = useMessagingApi();
54110
54202
  const [openConfirm, setOpenConfirm] = useState(false);
54111
54203
  const [openDeleteConfirm, setOpenDeleteConfirm] = useState(false);
54112
54204
  const [openReschedule, setOpenReschedule] = useState(false);
@@ -54115,7 +54207,7 @@ function ScheduledMessageRow({ message, onToast }) {
54115
54207
  const conversation = message.conversation;
54116
54208
  const rawConversationId = conversation?.id ?? message.conversation_id ?? 0;
54117
54209
  const conversationId = typeof rawConversationId === "string" ? Number.parseInt(rawConversationId, 10) : Number(rawConversationId);
54118
- const { data: fullConversation } = useConversation(config, conversationId);
54210
+ const { data: fullConversation } = useConversation(conversationId);
54119
54211
  const messageType = message.type;
54120
54212
  const effectiveConversation = fullConversation || conversation;
54121
54213
  const affiliateId = auth.currentUser?.affiliateId;
@@ -54153,12 +54245,13 @@ function ScheduledMessageRow({ message, onToast }) {
54153
54245
  metadata: a.metadata ?? void 0
54154
54246
  };
54155
54247
  });
54156
- await createMessage(config, conversationId, { message: {
54157
- type: mapConversationTypeToMessageType(fullConversation.type),
54248
+ const typeFromConversation = mapConversationTypeToMessageType(fullConversation.type);
54249
+ await api.createMessage(conversationId, { message: {
54250
+ type: typeFromConversation,
54158
54251
  body: message.body || "",
54159
54252
  attachments_attributes: mappedAttachments
54160
54253
  } });
54161
- await destroyScheduledMessage(config, conversationId, message.id);
54254
+ await api.destroyScheduledMessage(conversationId, message.id);
54162
54255
  },
54163
54256
  onSuccess: async () => {
54164
54257
  await Promise.all([
@@ -54175,7 +54268,7 @@ function ScheduledMessageRow({ message, onToast }) {
54175
54268
  });
54176
54269
  const deleteMutation = useMutation({
54177
54270
  mutationFn: async () => {
54178
- await destroyScheduledMessage(config, conversationId, message.id);
54271
+ await api.destroyScheduledMessage(conversationId, message.id);
54179
54272
  },
54180
54273
  onSuccess: async () => {
54181
54274
  await Promise.all([queryClient.invalidateQueries({ queryKey: MESSAGES_QUERY_KEYS.listScheduledMessages(conversationId) }), queryClient.invalidateQueries({ queryKey: MESSAGES_QUERY_KEYS.listMyScheduledMessages() })]);
@@ -54185,7 +54278,7 @@ function ScheduledMessageRow({ message, onToast }) {
54185
54278
  });
54186
54279
  const rescheduleMutation = useMutation({
54187
54280
  mutationFn: async (isoDate) => {
54188
- await updateScheduledMessage(config, conversationId, message.id, { scheduled_message: { scheduled_at: isoDate } });
54281
+ await api.updateScheduledMessage(conversationId, message.id, { scheduled_message: { scheduled_at: isoDate } });
54189
54282
  },
54190
54283
  onSuccess: async () => {
54191
54284
  await Promise.all([queryClient.invalidateQueries({ queryKey: MESSAGES_QUERY_KEYS.listScheduledMessages(conversationId) }), queryClient.invalidateQueries({ queryKey: MESSAGES_QUERY_KEYS.listMyScheduledMessages() })]);
@@ -54335,8 +54428,7 @@ function ScheduledMessageRow({ message, onToast }) {
54335
54428
  //#endregion
54336
54429
  //#region ../../messaging/ui/src/app/ScheduledMessagesView.tsx
54337
54430
  function ScheduledMessagesView({ onToast }) {
54338
- const { config } = useMessagingApp();
54339
- const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isError, error } = useMyScheduledMessages(config);
54431
+ const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isError, error } = useMyScheduledMessages();
54340
54432
  const messages = useMemo(() => {
54341
54433
  if (!data?.pages) return [];
54342
54434
  return data.pages.flatMap((p) => p[1].items);
@@ -54401,9 +54493,9 @@ function ScheduledMessagesView({ onToast }) {
54401
54493
  }
54402
54494
  //#endregion
54403
54495
  //#region ../../messaging/ui/src/app/MessagingApp.tsx
54404
- function MessagingApp({ config, auth, websocketUrl, token, renderImage, renderProfileTrigger, onNavigate, initialRoute, renderLockedDownlineItem, renderNewChannelSidebar, showAdminFeatures, canUseMyDownline, downlineData, topBar, messagesViewProps, newMessageViewProps, onToast, getMessageLink, renderProfileContent, children }) {
54496
+ function MessagingApp({ api, auth, websocketUrl, token, renderImage, renderProfileTrigger, onNavigate, initialRoute, renderLockedDownlineItem, renderNewChannelSidebar, showAdminFeatures, canUseMyDownline, downlineData, topBar, messagesViewProps, newMessageViewProps, onToast, getMessageLink, renderProfileContent, children }) {
54405
54497
  return /* @__PURE__ */ jsxs(MessagingAppProvider, {
54406
- config,
54498
+ api,
54407
54499
  auth,
54408
54500
  websocketUrl,
54409
54501
  token,
@@ -54489,14 +54581,14 @@ function defaultToast(message, type) {
54489
54581
  }
54490
54582
  function MessagingScreen({ onToast, filestackApiKey, websocketUrl: websocketUrlOverride, background, textColor, accentColor, padding, borderRadius, ...divProps }) {
54491
54583
  const { config } = useFluidContext();
54492
- const { apiConfig, websocketUrl, token } = useMessagingConfig();
54584
+ const { apiConfig, messagingApi, websocketUrl, token } = useMessagingConfig();
54493
54585
  const messagingAuth = useMessagingAuth();
54494
54586
  const effectiveApiKey = filestackApiKey ?? config.filestackApiKey;
54495
54587
  const uploader = useMemo(() => createFluidFileUploader(effectiveApiKey), [effectiveApiKey]);
54496
54588
  const effectiveWsUrl = websocketUrlOverride ?? websocketUrl;
54497
54589
  const effectiveToast = onToast ?? defaultToast;
54498
54590
  const searchUsers = useCallback(async (query) => {
54499
- return ((await listConnectedRecipients(apiConfig, {
54591
+ return ((await messagingApi.listConnectedRecipients({
54500
54592
  filterrific: { search_query: query },
54501
54593
  per_page: 10,
54502
54594
  page: 1
@@ -54516,7 +54608,7 @@ function MessagingScreen({ onToast, filestackApiKey, websocketUrl: websocketUrlO
54516
54608
  conversationName: name
54517
54609
  };
54518
54610
  });
54519
- }, [apiConfig]);
54611
+ }, [messagingApi]);
54520
54612
  const searchChannels = useCallback(async (query) => {
54521
54613
  return (await searchConversations(apiConfig, { filterrific: { search_query: query } }) ?? []).map((channel) => {
54522
54614
  const { text: nameWithoutEmoji } = extractEmoji(channel.name);
@@ -54559,7 +54651,7 @@ function MessagingScreen({ onToast, filestackApiKey, websocketUrl: websocketUrlO
54559
54651
  ...divProps,
54560
54652
  className: `h-full ${divProps.className ?? ""}`,
54561
54653
  children: /* @__PURE__ */ jsx(MessagingApp, {
54562
- config: apiConfig,
54654
+ api: messagingApi,
54563
54655
  auth: messagingAuth,
54564
54656
  websocketUrl: effectiveWsUrl,
54565
54657
  token,
@@ -54590,4 +54682,4 @@ const messagingScreenPropertySchema = {
54590
54682
  //#endregion
54591
54683
  export { useMessagingAuth as a, useMessagingConfig as i, messagingScreenPropertySchema as n, createFluidFileUploader as r, MessagingScreen as t };
54592
54684
 
54593
- //# sourceMappingURL=MessagingScreen-DTXSZ-Oa.mjs.map
54685
+ //# sourceMappingURL=MessagingScreen-CBuI3fu6.mjs.map