@ai-matrx/messaging 0.10.5 → 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));
@@ -1209,11 +1238,20 @@ function createMessagingEngine(options) {
1209
1238
  }
1210
1239
  async function reloadInbox() {
1211
1240
  if (disposed) return;
1212
- const page = await repository.listConversations({ limit: conversationPageSize });
1241
+ const page = await repository.listConversations({
1242
+ limit: conversationPageSize,
1243
+ archived: store.snapshot().archiveFilter
1244
+ });
1213
1245
  if (disposed) return;
1214
1246
  conversationCursor = page.nextCursor;
1215
1247
  store.setConversations(page.items, page.hasMore);
1216
1248
  }
1249
+ async function reloadArchivedCount() {
1250
+ if (disposed) return;
1251
+ const value = await repository.countArchivedConversations();
1252
+ if (disposed) return;
1253
+ store.setArchivedCount(value);
1254
+ }
1217
1255
  async function backfillConversation(id) {
1218
1256
  const thread = store.snapshot().threads.get(id);
1219
1257
  const since = thread?.latestAt ?? null;
@@ -1240,7 +1278,13 @@ function createMessagingEngine(options) {
1240
1278
  outbox,
1241
1279
  identity,
1242
1280
  async start() {
1281
+ if (options.archiveFilter !== void 0) {
1282
+ store.setArchiveFilter(options.archiveFilter);
1283
+ }
1243
1284
  await reloadInbox();
1285
+ void reloadArchivedCount().catch(
1286
+ (error) => reportError(error, "refreshArchivedCount")
1287
+ );
1244
1288
  if (disposed || inboxChannel !== null) return;
1245
1289
  inboxChannel = manager.open({
1246
1290
  topic: inboxTopic(identity.userId),
@@ -1282,12 +1326,33 @@ function createMessagingEngine(options) {
1282
1326
  }
1283
1327
  });
1284
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
+ },
1285
1349
  async loadMoreConversations() {
1286
1350
  if (conversationCursor === null) return;
1287
1351
  try {
1288
1352
  const page = await repository.listConversations({
1289
1353
  limit: conversationPageSize,
1290
- cursor: conversationCursor
1354
+ cursor: conversationCursor,
1355
+ archived: store.snapshot().archiveFilter
1291
1356
  });
1292
1357
  conversationCursor = page.nextCursor;
1293
1358
  store.appendConversations(page.items, page.hasMore);
@@ -1551,6 +1616,7 @@ function createMessagingRepository(options) {
1551
1616
  identity,
1552
1617
  async listConversations(args = {}) {
1553
1618
  const limit = args.limit ?? 30;
1619
+ const archived = args.archived ?? DEFAULT_MESSAGING_ARCHIVE_FILTER;
1554
1620
  const operation = "listConversations";
1555
1621
  const rows = await withSessionRetry(
1556
1622
  operation,
@@ -1560,7 +1626,12 @@ function createMessagingRepository(options) {
1560
1626
  p_user_id: identity.userId,
1561
1627
  p_limit: limit + 1,
1562
1628
  p_before_sort_at: args.cursor?.beforeSortAt ?? null,
1563
- 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
1564
1635
  },
1565
1636
  operation
1566
1637
  )
@@ -1580,6 +1651,29 @@ function createMessagingRepository(options) {
1580
1651
  nextCursor: hasMore && last !== void 0 ? { beforeSortAt: last.sortAt, beforeConversationId: last.conversation.id } : null
1581
1652
  };
1582
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
+ },
1583
1677
  async getConversation(id) {
1584
1678
  const operation = "getConversation";
1585
1679
  const { data, error } = await withSessionRetry(
@@ -1888,6 +1982,7 @@ function MessagingRuntime(props) {
1888
1982
  onFallback: (message) => report({ level: "warn", message })
1889
1983
  }),
1890
1984
  onDiagnostic: report,
1985
+ ...props.archiveFilter !== void 0 ? { archiveFilter: props.archiveFilter } : {},
1891
1986
  onIncoming: (message) => {
1892
1987
  const snapshot = built?.store.snapshot();
1893
1988
  if (snapshot === void 0) return;
@@ -2118,6 +2213,11 @@ function useConversations() {
2118
2213
  },
2119
2214
  select,
2120
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,
2121
2221
  startDirect: async (otherUserId) => {
2122
2222
  if (host === null) {
2123
2223
  throw new Error("[@ai-matrx/messaging] startDirect called before the host was ready.");
@@ -2689,9 +2789,37 @@ var AI_LABELS = {
2689
2789
  draftReply: "Draft a reply"
2690
2790
  };
2691
2791
  function ConversationList(props) {
2692
- 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();
2693
2803
  const RowChrome = useMessagingHost()?.wrapConversationRow ?? null;
2694
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]);
2695
2823
  const visible = (0, import_react5.useMemo)(() => {
2696
2824
  const needle = query.trim().toLowerCase();
2697
2825
  if (needle.length === 0) return conversations;
@@ -2724,12 +2852,22 @@ function ConversationList(props) {
2724
2852
  }
2725
2853
  ) : null
2726
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,
2727
2865
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mx-msg__scroll", children: [
2728
2866
  isInitialLoading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ConversationSkeleton, {}) : visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2729
2867
  EmptyState,
2730
2868
  {
2731
- title: query.length > 0 ? "No matches" : "No conversations yet",
2732
- 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."
2733
2871
  }
2734
2872
  ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "mx-msg__rows", children: visible.map((item) => {
2735
2873
  const row = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -3224,11 +3362,4 @@ function MessagingInbox(props) {
3224
3362
  }
3225
3363
  );
3226
3364
  }
3227
-
3228
- // src/core/types.ts
3229
- var asConversationId = (value) => value;
3230
- var asMessageId = (value) => value;
3231
- var asUserId = (value) => value;
3232
- var asOrganizationId = (value) => value;
3233
- var asClientMessageId = (value) => value;
3234
3365
  //# sourceMappingURL=react.cjs.map