@ai-matrx/messaging 0.10.4 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.cjs CHANGED
@@ -34,6 +34,7 @@ __export(react_exports, {
34
34
  ConversationList: () => ConversationList,
35
35
  ConversationSkeleton: () => ConversationSkeleton,
36
36
  ConversationView: () => ConversationView,
37
+ DEFAULT_MESSAGING_ARCHIVE_FILTER: () => DEFAULT_MESSAGING_ARCHIVE_FILTER,
37
38
  DeliveryTick: () => DeliveryTick,
38
39
  DoubleCheckIcon: () => DoubleCheckIcon,
39
40
  EmptyState: () => EmptyState,
@@ -96,6 +97,7 @@ __export(react_exports, {
96
97
  resolveActor: () => resolveActor,
97
98
  splitText: () => splitText,
98
99
  summarizeText: () => summarizeText,
100
+ toMessagingArchiveFilter: () => toMessagingArchiveFilter,
99
101
  unreadCutoff: () => unreadCutoff,
100
102
  useComposer: () => useComposer,
101
103
  useConversation: () => useConversation,
@@ -914,6 +916,17 @@ function projectConversationSummary(row, viewerId, fallbackOrganizationId) {
914
916
  };
915
917
  }
916
918
 
919
+ // src/core/types.ts
920
+ var asConversationId = (value) => value;
921
+ var asMessageId = (value) => value;
922
+ var asUserId = (value) => value;
923
+ var asOrganizationId = (value) => value;
924
+ var asClientMessageId = (value) => value;
925
+ var DEFAULT_MESSAGING_ARCHIVE_FILTER = "active";
926
+ function toMessagingArchiveFilter(value, fallback = DEFAULT_MESSAGING_ARCHIVE_FILTER) {
927
+ return value === "active" || value === "archived" || value === "all" ? value : fallback;
928
+ }
929
+
917
930
  // src/core/store.ts
918
931
  function timeOf(message) {
919
932
  const stamp = message.editedAt ?? message.createdAt;
@@ -944,6 +957,8 @@ function createMessagingStore() {
944
957
  let conversations = [];
945
958
  let hasMoreConversations = false;
946
959
  let hasLoadedConversations = false;
960
+ let archiveFilter = DEFAULT_MESSAGING_ARCHIVE_FILTER;
961
+ let archivedCount = null;
947
962
  let threads = /* @__PURE__ */ new Map();
948
963
  let activeConversationId = null;
949
964
  const listeners = /* @__PURE__ */ new Set();
@@ -956,7 +971,9 @@ function createMessagingStore() {
956
971
  hasLoadedConversations,
957
972
  threads,
958
973
  activeConversationId,
959
- totalUnreadConversations: conversations.filter((item) => item.unreadCount > 0).length
974
+ totalUnreadConversations: conversations.filter((item) => item.unreadCount > 0).length,
975
+ archiveFilter,
976
+ archivedCount
960
977
  };
961
978
  return cached;
962
979
  }
@@ -1003,6 +1020,18 @@ function createMessagingStore() {
1003
1020
  hasLoadedConversations = true;
1004
1021
  emit();
1005
1022
  },
1023
+ setArchiveFilter(next) {
1024
+ if (next === archiveFilter) return;
1025
+ archiveFilter = next;
1026
+ conversations = [];
1027
+ hasMoreConversations = false;
1028
+ hasLoadedConversations = false;
1029
+ emit();
1030
+ },
1031
+ setArchivedCount(value) {
1032
+ archivedCount = value;
1033
+ emit();
1034
+ },
1006
1035
  appendConversations(items, hasMore) {
1007
1036
  const byId = new Map(conversations.map((item) => [item.conversation.id, item]));
1008
1037
  items.forEach((item) => byId.set(item.conversation.id, item));
@@ -1208,10 +1237,21 @@ function createMessagingEngine(options) {
1208
1237
  openChannels.get(message.conversationId)?.send(MESSAGING_EVENTS.message, message);
1209
1238
  }
1210
1239
  async function reloadInbox() {
1211
- const page = await repository.listConversations({ limit: conversationPageSize });
1240
+ if (disposed) return;
1241
+ const page = await repository.listConversations({
1242
+ limit: conversationPageSize,
1243
+ archived: store.snapshot().archiveFilter
1244
+ });
1245
+ if (disposed) return;
1212
1246
  conversationCursor = page.nextCursor;
1213
1247
  store.setConversations(page.items, page.hasMore);
1214
1248
  }
1249
+ async function reloadArchivedCount() {
1250
+ if (disposed) return;
1251
+ const value = await repository.countArchivedConversations();
1252
+ if (disposed) return;
1253
+ store.setArchivedCount(value);
1254
+ }
1215
1255
  async function backfillConversation(id) {
1216
1256
  const thread = store.snapshot().threads.get(id);
1217
1257
  const since = thread?.latestAt ?? null;
@@ -1238,7 +1278,13 @@ function createMessagingEngine(options) {
1238
1278
  outbox,
1239
1279
  identity,
1240
1280
  async start() {
1281
+ if (options.archiveFilter !== void 0) {
1282
+ store.setArchiveFilter(options.archiveFilter);
1283
+ }
1241
1284
  await reloadInbox();
1285
+ void reloadArchivedCount().catch(
1286
+ (error) => reportError(error, "refreshArchivedCount")
1287
+ );
1242
1288
  if (disposed || inboxChannel !== null) return;
1243
1289
  inboxChannel = manager.open({
1244
1290
  topic: inboxTopic(identity.userId),
@@ -1253,7 +1299,7 @@ function createMessagingEngine(options) {
1253
1299
  const message = projectMessage(row, identity.organizationId);
1254
1300
  const known = store.snapshot().conversations.some((item) => item.conversation.id === message.conversationId);
1255
1301
  if (!known) {
1256
- void reloadInbox();
1302
+ void reloadInbox().catch((error) => reportError(error, "inboxRefresh"));
1257
1303
  return;
1258
1304
  }
1259
1305
  store.ingest(message);
@@ -1269,7 +1315,7 @@ function createMessagingEngine(options) {
1269
1315
  filter: `user_id=eq.${identity.userId}`,
1270
1316
  rowId: (row) => typeof row["id"] === "string" ? row["id"] : void 0,
1271
1317
  onChange: () => {
1272
- void reloadInbox();
1318
+ void reloadInbox().catch((error) => reportError(error, "inboxRefresh"));
1273
1319
  }
1274
1320
  }
1275
1321
  ],
@@ -1280,12 +1326,33 @@ function createMessagingEngine(options) {
1280
1326
  }
1281
1327
  });
1282
1328
  },
1329
+ async setArchiveFilter(next) {
1330
+ if (next === store.snapshot().archiveFilter) return;
1331
+ store.setArchiveFilter(next);
1332
+ conversationCursor = null;
1333
+ try {
1334
+ await reloadInbox();
1335
+ } catch (error) {
1336
+ reportError(error, "setArchiveFilter");
1337
+ }
1338
+ await reloadArchivedCount().catch(
1339
+ (error) => reportError(error, "refreshArchivedCount")
1340
+ );
1341
+ },
1342
+ async refreshArchivedCount() {
1343
+ try {
1344
+ await reloadArchivedCount();
1345
+ } catch (error) {
1346
+ reportError(error, "refreshArchivedCount");
1347
+ }
1348
+ },
1283
1349
  async loadMoreConversations() {
1284
1350
  if (conversationCursor === null) return;
1285
1351
  try {
1286
1352
  const page = await repository.listConversations({
1287
1353
  limit: conversationPageSize,
1288
- cursor: conversationCursor
1354
+ cursor: conversationCursor,
1355
+ archived: store.snapshot().archiveFilter
1289
1356
  });
1290
1357
  conversationCursor = page.nextCursor;
1291
1358
  store.appendConversations(page.items, page.hasMore);
@@ -1549,6 +1616,7 @@ function createMessagingRepository(options) {
1549
1616
  identity,
1550
1617
  async listConversations(args = {}) {
1551
1618
  const limit = args.limit ?? 30;
1619
+ const archived = args.archived ?? DEFAULT_MESSAGING_ARCHIVE_FILTER;
1552
1620
  const operation = "listConversations";
1553
1621
  const rows = await withSessionRetry(
1554
1622
  operation,
@@ -1558,7 +1626,12 @@ function createMessagingRepository(options) {
1558
1626
  p_user_id: identity.userId,
1559
1627
  p_limit: limit + 1,
1560
1628
  p_before_sort_at: args.cursor?.beforeSortAt ?? null,
1561
- p_before_conversation_id: args.cursor?.beforeConversationId ?? null
1629
+ p_before_conversation_id: args.cursor?.beforeConversationId ?? null,
1630
+ // THE ARCHIVED-ITEMS LAW, SERVER-side. `get_dm_conversations_with_details`
1631
+ // used to hardcode `is_archived IS FALSE` with no parameter at all,
1632
+ // so an archived conversation was not hidden — it was unreachable.
1633
+ // The RPC gained `p_archived` on 2026-09-09 (register row R1).
1634
+ p_archived: archived
1562
1635
  },
1563
1636
  operation
1564
1637
  )
@@ -1578,6 +1651,29 @@ function createMessagingRepository(options) {
1578
1651
  nextCursor: hasMore && last !== void 0 ? { beforeSortAt: last.sortAt, beforeConversationId: last.conversation.id } : null
1579
1652
  };
1580
1653
  },
1654
+ async countArchivedConversations(args = {}) {
1655
+ const limit = args.limit ?? 100;
1656
+ const operation = "countArchivedConversations";
1657
+ const rows = await withSessionRetry(
1658
+ operation,
1659
+ () => rpc(
1660
+ RPCS.conversationsWithDetails,
1661
+ {
1662
+ p_user_id: identity.userId,
1663
+ p_limit: limit + 1,
1664
+ p_before_sort_at: null,
1665
+ p_before_conversation_id: null,
1666
+ p_archived: "archived"
1667
+ },
1668
+ operation
1669
+ )
1670
+ );
1671
+ if (rows !== null && !Array.isArray(rows)) {
1672
+ throw invalidResponse(operation, `${RPCS.conversationsWithDetails} did not return rows`);
1673
+ }
1674
+ const found = (rows ?? []).length;
1675
+ return found > limit ? { count: limit, exact: false } : { count: found, exact: true };
1676
+ },
1581
1677
  async getConversation(id) {
1582
1678
  const operation = "getConversation";
1583
1679
  const { data, error } = await withSessionRetry(
@@ -1886,6 +1982,7 @@ function MessagingRuntime(props) {
1886
1982
  onFallback: (message) => report({ level: "warn", message })
1887
1983
  }),
1888
1984
  onDiagnostic: report,
1985
+ ...props.archiveFilter !== void 0 ? { archiveFilter: props.archiveFilter } : {},
1889
1986
  onIncoming: (message) => {
1890
1987
  const snapshot = built?.store.snapshot();
1891
1988
  if (snapshot === void 0) return;
@@ -2116,6 +2213,11 @@ function useConversations() {
2116
2213
  },
2117
2214
  select,
2118
2215
  activeConversationId: snapshot?.activeConversationId ?? null,
2216
+ archiveFilter: snapshot?.archiveFilter ?? DEFAULT_MESSAGING_ARCHIVE_FILTER,
2217
+ setArchiveFilter: (next) => {
2218
+ void host?.engine.setArchiveFilter(next);
2219
+ },
2220
+ archivedCount: snapshot?.archivedCount ?? null,
2119
2221
  startDirect: async (otherUserId) => {
2120
2222
  if (host === null) {
2121
2223
  throw new Error("[@ai-matrx/messaging] startDirect called before the host was ready.");
@@ -2687,9 +2789,37 @@ var AI_LABELS = {
2687
2789
  draftReply: "Draft a reply"
2688
2790
  };
2689
2791
  function ConversationList(props) {
2690
- const { conversations, hasMore, isInitialLoading, loadMore, select, activeConversationId } = useConversations();
2792
+ const {
2793
+ conversations,
2794
+ hasMore,
2795
+ isInitialLoading,
2796
+ loadMore,
2797
+ select,
2798
+ activeConversationId,
2799
+ archiveFilter,
2800
+ setArchiveFilter,
2801
+ archivedCount
2802
+ } = useConversations();
2691
2803
  const RowChrome = useMessagingHost()?.wrapConversationRow ?? null;
2692
2804
  const [query, setQuery] = (0, import_react5.useState)("");
2805
+ const showingArchive = archiveFilter === "archived";
2806
+ const archiveReveal = (0, import_react5.useMemo)(() => {
2807
+ if (showingArchive) {
2808
+ return {
2809
+ next: "active",
2810
+ label: "\u2190 Back to active conversations",
2811
+ ariaLabel: "Hide archived conversations"
2812
+ };
2813
+ }
2814
+ if (archivedCount === null) return null;
2815
+ if (archivedCount.count === 0) return null;
2816
+ const printed = archivedCount.exact ? `${archivedCount.count}` : `${archivedCount.count}+`;
2817
+ return {
2818
+ next: "archived",
2819
+ label: `Archived (${printed})`,
2820
+ ariaLabel: "Show archived conversations"
2821
+ };
2822
+ }, [showingArchive, archivedCount]);
2693
2823
  const visible = (0, import_react5.useMemo)(() => {
2694
2824
  const needle = query.trim().toLowerCase();
2695
2825
  if (needle.length === 0) return conversations;
@@ -2722,12 +2852,22 @@ function ConversationList(props) {
2722
2852
  }
2723
2853
  ) : null
2724
2854
  ] }),
2855
+ archiveReveal !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mx-msg__list-archive", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2856
+ "button",
2857
+ {
2858
+ type: "button",
2859
+ className: "mx-msg__chip",
2860
+ onClick: () => setArchiveFilter(archiveReveal.next),
2861
+ "aria-label": archiveReveal.ariaLabel,
2862
+ children: archiveReveal.label
2863
+ }
2864
+ ) }) : null,
2725
2865
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mx-msg__scroll", children: [
2726
2866
  isInitialLoading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ConversationSkeleton, {}) : visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2727
2867
  EmptyState,
2728
2868
  {
2729
- title: query.length > 0 ? "No matches" : "No conversations yet",
2730
- body: query.length > 0 ? "Try a different name or word." : "Start one and it will appear here."
2869
+ title: query.length > 0 ? "No matches" : showingArchive ? "No archived conversations" : "No conversations yet",
2870
+ body: query.length > 0 ? "Try a different name or word." : showingArchive ? "Archiving a conversation moves it here." : "Start one and it will appear here."
2731
2871
  }
2732
2872
  ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "mx-msg__rows", children: visible.map((item) => {
2733
2873
  const row = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -3222,11 +3362,4 @@ function MessagingInbox(props) {
3222
3362
  }
3223
3363
  );
3224
3364
  }
3225
-
3226
- // src/core/types.ts
3227
- var asConversationId = (value) => value;
3228
- var asMessageId = (value) => value;
3229
- var asUserId = (value) => value;
3230
- var asOrganizationId = (value) => value;
3231
- var asClientMessageId = (value) => value;
3232
3365
  //# sourceMappingURL=react.cjs.map