@ai-matrx/messaging 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.cjs CHANGED
@@ -983,6 +983,7 @@ function createMessagingStore() {
983
983
  let threads = /* @__PURE__ */ new Map();
984
984
  let activeConversationId = null;
985
985
  let conversationReadFailures = /* @__PURE__ */ new Map();
986
+ let conversationsLoadError = null;
986
987
  const listeners = /* @__PURE__ */ new Set();
987
988
  let cached = null;
988
989
  function snapshot() {
@@ -996,7 +997,8 @@ function createMessagingStore() {
996
997
  totalUnreadConversations: conversations.filter((item) => item.unreadCount > 0).length,
997
998
  archiveFilter,
998
999
  archivedCount,
999
- conversationReadFailures
1000
+ conversationReadFailures,
1001
+ conversationsLoadError
1000
1002
  };
1001
1003
  return cached;
1002
1004
  }
@@ -1041,6 +1043,12 @@ function createMessagingStore() {
1041
1043
  conversations = normalizeConversations(items);
1042
1044
  hasMoreConversations = hasMore;
1043
1045
  hasLoadedConversations = true;
1046
+ conversationsLoadError = null;
1047
+ emit();
1048
+ },
1049
+ setConversationsLoadError(message) {
1050
+ if (message === conversationsLoadError) return;
1051
+ conversationsLoadError = message;
1044
1052
  emit();
1045
1053
  },
1046
1054
  setArchiveFilter(next) {
@@ -1273,10 +1281,20 @@ function createMessagingEngine(options) {
1273
1281
  }
1274
1282
  async function reloadInbox() {
1275
1283
  if (disposed) return;
1276
- const page = await repository.listConversations({
1277
- limit: conversationPageSize,
1278
- archived: store.snapshot().archiveFilter
1279
- });
1284
+ let page;
1285
+ try {
1286
+ page = await repository.listConversations({
1287
+ limit: conversationPageSize,
1288
+ archived: store.snapshot().archiveFilter
1289
+ });
1290
+ } catch (error) {
1291
+ if (!disposed) {
1292
+ store.setConversationsLoadError(
1293
+ error instanceof Error ? error.message : typeof error === "string" ? error : "The conversation list could not be read."
1294
+ );
1295
+ }
1296
+ throw error;
1297
+ }
1280
1298
  if (disposed) return;
1281
1299
  conversationCursor = page.nextCursor;
1282
1300
  store.setConversations(page.items, page.hasMore);
@@ -1361,6 +1379,13 @@ function createMessagingEngine(options) {
1361
1379
  }
1362
1380
  });
1363
1381
  },
1382
+ async refreshInbox() {
1383
+ try {
1384
+ await reloadInbox();
1385
+ } catch (error) {
1386
+ reportError(error, "inboxRefresh");
1387
+ }
1388
+ },
1364
1389
  async setArchiveFilter(next) {
1365
1390
  if (next === store.snapshot().archiveFilter) return;
1366
1391
  store.setArchiveFilter(next);
@@ -2504,7 +2529,11 @@ function useConversations() {
2504
2529
  hasMore: snapshot?.hasMoreConversations ?? false,
2505
2530
  // "Not loaded yet" is a FACT from the store, never a guess. Showing
2506
2531
  // "No conversations yet" before the first read is a screen telling a lie.
2507
- isInitialLoading: snapshot === null || !snapshot.hasLoadedConversations,
2532
+ isInitialLoading: snapshot === null || !snapshot.hasLoadedConversations && snapshot.conversationsLoadError === null,
2533
+ loadError: snapshot?.conversationsLoadError ?? null,
2534
+ retry: () => {
2535
+ void host?.engine.refreshInbox();
2536
+ },
2508
2537
  totalUnreadConversations: snapshot?.totalUnreadConversations ?? 0,
2509
2538
  loadMore: () => {
2510
2539
  void host?.engine.loadMoreConversations();
@@ -3086,6 +3115,7 @@ function EmptyState(props) {
3086
3115
  }
3087
3116
 
3088
3117
  // src/react/components.tsx
3118
+ var import_design_system = require("@ai-matrx/design-system");
3089
3119
  var import_jsx_runtime4 = require("react/jsx-runtime");
3090
3120
  var AI_LABELS = {
3091
3121
  catchUp: "Catch me up",
@@ -3103,7 +3133,9 @@ function ConversationList(props) {
3103
3133
  activeConversationId,
3104
3134
  archiveFilter,
3105
3135
  setArchiveFilter,
3106
- archivedCount
3136
+ archivedCount,
3137
+ loadError,
3138
+ retry
3107
3139
  } = useConversations();
3108
3140
  const RowChrome = useMessagingHost()?.wrapConversationRow ?? null;
3109
3141
  const [query, setQuery] = (0, import_react5.useState)("");
@@ -3168,7 +3200,20 @@ function ConversationList(props) {
3168
3200
  }
3169
3201
  ) }) : null,
3170
3202
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mx-msg__scroll", children: [
3171
- isInitialLoading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ConversationSkeleton, {}) : visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3203
+ isInitialLoading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ConversationSkeleton, {}) : loadError !== null && visible.length === 0 ? (
3204
+ // A failed read resolves to the failure — never a skeleton forever,
3205
+ // never "No conversations yet" (RC-B12 round 11). ErrorBox is the
3206
+ // design-system error slot: the host fills it with its menu.
3207
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3208
+ ReadFailureBox,
3209
+ {
3210
+ title: "Couldn't load your conversations",
3211
+ message: loadError,
3212
+ operation: "List your conversations",
3213
+ onRetry: retry
3214
+ }
3215
+ )
3216
+ ) : visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3172
3217
  EmptyState,
3173
3218
  {
3174
3219
  title: query.length > 0 ? "No matches" : showingArchive ? "No archived conversations" : "No conversations yet",
@@ -3345,10 +3390,11 @@ function ConversationView(props) {
3345
3390
  body: "It doesn't exist, or you aren't a member of it."
3346
3391
  }
3347
3392
  ) : conversation.error !== null && messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3348
- EmptyState,
3393
+ ReadFailureBox,
3349
3394
  {
3350
3395
  title: "Couldn't load this conversation",
3351
- body: "Check your connection, then open it again."
3396
+ message: conversation.error.message || "Check your connection, then open it again.",
3397
+ operation: "Open this conversation"
3352
3398
  }
3353
3399
  ) : messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(EmptyState, { title: "No messages yet", body: "Say something to get started." }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mx-msg__messages", children: [
3354
3400
  conversation.hasMoreOlder ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -3679,4 +3725,27 @@ function MessagingInbox(props) {
3679
3725
  }
3680
3726
  );
3681
3727
  }
3728
+ function ReadFailureBox({
3729
+ title,
3730
+ message,
3731
+ operation,
3732
+ onRetry
3733
+ }) {
3734
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mx-msg__empty", role: "alert", children: [
3735
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3736
+ import_design_system.ErrorBox,
3737
+ {
3738
+ as: "div",
3739
+ title,
3740
+ message,
3741
+ operation,
3742
+ origin: "messaging",
3743
+ style: { color: "var(--mx-msg-danger)", fontWeight: 600 },
3744
+ children: title
3745
+ }
3746
+ ),
3747
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: message }),
3748
+ onRetry ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mx-msg__button mx-msg__button--ghost", onClick: onRetry, children: "Try again" }) : null
3749
+ ] });
3750
+ }
3682
3751
  //# sourceMappingURL=react.cjs.map