@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/CHANGELOG.md +51 -0
- package/README.md +33 -0
- package/dist/index.cjs +98 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +101 -1
- package/dist/index.d.ts +101 -1
- package/dist/index.js +98 -11
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +145 -14
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +126 -1
- package/dist/react.d.ts +126 -1
- package/dist/react.js +145 -14
- package/dist/react.js.map +1 -1
- package/dist/styles.css +7 -0
- package/package.json +1 -1
package/dist/react.d.cts
CHANGED
|
@@ -138,6 +138,39 @@ interface Message {
|
|
|
138
138
|
/** Set only while this message is in the outbox and its last send failed. */
|
|
139
139
|
readonly failureReason?: string | undefined;
|
|
140
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* THE ARCHIVED-ITEMS LAW's three states (Arman, 2026-09-09 —
|
|
143
|
+
* `common-docs/policies/archived-items.md`):
|
|
144
|
+
*
|
|
145
|
+
* > "everything should have an archive filter, and the default should always
|
|
146
|
+
* > hide archived, but seeing archived items should be one or two clicks
|
|
147
|
+
* > away … this is a system wide decision for every single item everywhere in
|
|
148
|
+
* > our system, for every single table and every single page."
|
|
149
|
+
*
|
|
150
|
+
* | value | what the list shows |
|
|
151
|
+
* |------------|--------------------------|
|
|
152
|
+
* | `active` | only un-archived rows |
|
|
153
|
+
* | `archived` | only archived rows |
|
|
154
|
+
* | `all` | every row |
|
|
155
|
+
*
|
|
156
|
+
* The same three words the rest of the platform uses — `ArchivedFilter` in
|
|
157
|
+
* matrx-frontend's `lib/entity-list`, `p_archived` on the `agx_*`/`wfx_*` and
|
|
158
|
+
* DM list RPCs, `ArchiveFilter` in `@ai-matrx/design-system`. A person meets
|
|
159
|
+
* ONE control, not one per app.
|
|
160
|
+
*
|
|
161
|
+
* 🚨 Three states, never a boolean. A boolean cannot say "archived only",
|
|
162
|
+
* which is exactly the view someone looking for a conversation they archived
|
|
163
|
+
* last month needs.
|
|
164
|
+
*/
|
|
165
|
+
type MessagingArchiveFilter = "active" | "archived" | "all";
|
|
166
|
+
/** The platform default: a list hides archived rows until asked. */
|
|
167
|
+
declare const DEFAULT_MESSAGING_ARCHIVE_FILTER: MessagingArchiveFilter;
|
|
168
|
+
/**
|
|
169
|
+
* Narrow an untrusted value (a stored preference, a URL param, a host knob) to
|
|
170
|
+
* the tri-state. Anything unrecognised falls back to the default rather than
|
|
171
|
+
* silently widening a list to archived conversations.
|
|
172
|
+
*/
|
|
173
|
+
declare function toMessagingArchiveFilter(value: unknown, fallback?: MessagingArchiveFilter): MessagingArchiveFilter;
|
|
141
174
|
interface ConversationSummary {
|
|
142
175
|
readonly conversation: Conversation;
|
|
143
176
|
readonly participants: readonly UserSummary[];
|
|
@@ -667,7 +700,30 @@ interface MessagingRepository {
|
|
|
667
700
|
listConversations(args?: {
|
|
668
701
|
limit?: number;
|
|
669
702
|
cursor?: ConversationCursor | null;
|
|
703
|
+
/**
|
|
704
|
+
* THE ARCHIVED-ITEMS LAW. Omitted means `active` — archived conversations
|
|
705
|
+
* are hidden until a caller asks for them. This is a REQUEST to the reader
|
|
706
|
+
* (the RPC's `p_archived`), never a client-side sieve, so `hasMore` and the
|
|
707
|
+
* cursor describe the rows the caller actually asked for.
|
|
708
|
+
*/
|
|
709
|
+
archived?: MessagingArchiveFilter;
|
|
670
710
|
}): Promise<Page<ConversationSummary, ConversationCursor>>;
|
|
711
|
+
/**
|
|
712
|
+
* How many ARCHIVED conversations the caller has — the number a reveal
|
|
713
|
+
* control prints ("Archived (12)").
|
|
714
|
+
*
|
|
715
|
+
* 🚨 It is capped, and it says so. A count is read by listing archived rows
|
|
716
|
+
* up to `limit`; if that many come back the result is `exact: false` and the
|
|
717
|
+
* UI must render "12+", never a confident "12". A screen never lies: a cap
|
|
718
|
+
* silently presented as a total is the same defect as a badge counting rows
|
|
719
|
+
* the list hides.
|
|
720
|
+
*/
|
|
721
|
+
countArchivedConversations(args?: {
|
|
722
|
+
limit?: number;
|
|
723
|
+
}): Promise<{
|
|
724
|
+
count: number;
|
|
725
|
+
exact: boolean;
|
|
726
|
+
}>;
|
|
671
727
|
getConversation(id: ConversationId): Promise<Conversation>;
|
|
672
728
|
listMessages(conversationId: ConversationId, args?: {
|
|
673
729
|
limit?: number;
|
|
@@ -754,11 +810,38 @@ interface MessagingSnapshot {
|
|
|
754
810
|
readonly activeConversationId: ConversationId | null;
|
|
755
811
|
/** Conversations WITH unread, not total unread messages (the origin's semantics). */
|
|
756
812
|
readonly totalUnreadConversations: number;
|
|
813
|
+
/**
|
|
814
|
+
* THE ARCHIVED-ITEMS LAW's state for this inbox. `conversations` above is
|
|
815
|
+
* ALWAYS the rows this filter asked the server for — never a superset a
|
|
816
|
+
* surface is expected to sieve, so counts and pagination cannot disagree
|
|
817
|
+
* with what renders.
|
|
818
|
+
*/
|
|
819
|
+
readonly archiveFilter: MessagingArchiveFilter;
|
|
820
|
+
/**
|
|
821
|
+
* How many archived conversations exist, for the reveal control's label, or
|
|
822
|
+
* `null` before the first count lands. `exact: false` means the count hit its
|
|
823
|
+
* cap and the label must read "N+" — a capped number shown as a total is the
|
|
824
|
+
* same lie as a badge counting hidden rows.
|
|
825
|
+
*/
|
|
826
|
+
readonly archivedCount: {
|
|
827
|
+
readonly count: number;
|
|
828
|
+
readonly exact: boolean;
|
|
829
|
+
} | null;
|
|
757
830
|
}
|
|
758
831
|
interface MessagingStore {
|
|
759
832
|
snapshot(): MessagingSnapshot;
|
|
760
833
|
subscribe(listener: (snapshot: MessagingSnapshot) => void): () => void;
|
|
761
834
|
setConversations(items: readonly ConversationSummary[], hasMore: boolean): void;
|
|
835
|
+
/**
|
|
836
|
+
* Switch the archive axis. Clears the list and marks it UNLOADED: the rows
|
|
837
|
+
* on screen belong to the old filter, and leaving them there while the new
|
|
838
|
+
* page is in flight shows active conversations under an "Archived" heading.
|
|
839
|
+
*/
|
|
840
|
+
setArchiveFilter(next: MessagingArchiveFilter): void;
|
|
841
|
+
setArchivedCount(value: {
|
|
842
|
+
count: number;
|
|
843
|
+
exact: boolean;
|
|
844
|
+
} | null): void;
|
|
762
845
|
appendConversations(items: readonly ConversationSummary[], hasMore: boolean): void;
|
|
763
846
|
upsertConversation(item: ConversationSummary): void;
|
|
764
847
|
removeConversation(id: ConversationId): void;
|
|
@@ -833,6 +916,15 @@ interface MessagingEngineOptions {
|
|
|
833
916
|
onIncoming?: ((message: Message) => void) | undefined;
|
|
834
917
|
conversationPageSize?: number | undefined;
|
|
835
918
|
messagePageSize?: number | undefined;
|
|
919
|
+
/**
|
|
920
|
+
* 🚨 OPINIONS BECOME KNOBS. THE ARCHIVED-ITEMS LAW's clause 6 says the
|
|
921
|
+
* filter's initial state is an org/user-configurable setting, not code
|
|
922
|
+
* taste. The PLATFORM default is `active` (hide archived) and a host that
|
|
923
|
+
* passes nothing gets exactly that; a host wires this to whatever its own
|
|
924
|
+
* setting resolves to. The person can always change it from the control on
|
|
925
|
+
* the list.
|
|
926
|
+
*/
|
|
927
|
+
archiveFilter?: MessagingArchiveFilter | undefined;
|
|
836
928
|
}
|
|
837
929
|
interface MessagingEngine {
|
|
838
930
|
readonly store: MessagingStore;
|
|
@@ -841,6 +933,14 @@ interface MessagingEngine {
|
|
|
841
933
|
/** Load page one of the inbox and start the inbox channel. */
|
|
842
934
|
start(): Promise<void>;
|
|
843
935
|
loadMoreConversations(): Promise<void>;
|
|
936
|
+
/**
|
|
937
|
+
* THE ARCHIVED-ITEMS LAW's control. Re-reads page one under the new state
|
|
938
|
+
* (the filter is a REQUEST to the server, never a client-side sieve) and
|
|
939
|
+
* refreshes the archived count the reveal label prints.
|
|
940
|
+
*/
|
|
941
|
+
setArchiveFilter(next: MessagingArchiveFilter): Promise<void>;
|
|
942
|
+
/** Re-read how many archived conversations exist, for the reveal's label. */
|
|
943
|
+
refreshArchivedCount(): Promise<void>;
|
|
844
944
|
/** Open a conversation: load its thread and subscribe to its channel. */
|
|
845
945
|
openConversation(id: ConversationId): Promise<void>;
|
|
846
946
|
closeConversation(id: ConversationId): void;
|
|
@@ -1037,6 +1137,15 @@ interface MessagingProviderProps {
|
|
|
1037
1137
|
resolveSession?: SessionResolver | undefined;
|
|
1038
1138
|
/** Override outbox persistence. Defaults to localStorage, then memory. */
|
|
1039
1139
|
outboxStorage?: OutboxStorage | undefined;
|
|
1140
|
+
/**
|
|
1141
|
+
* 🚨 OPINIONS BECOME KNOBS. THE ARCHIVED-ITEMS LAW (Arman, 2026-09-09 —
|
|
1142
|
+
* `common-docs/policies/archived-items.md`) clause 6: the archive filter's
|
|
1143
|
+
* initial state is an org/user setting, not code taste. The PLATFORM default
|
|
1144
|
+
* is `active` — archived conversations hidden — and a host that passes
|
|
1145
|
+
* nothing gets exactly that. Pass whatever your own setting resolves to; the
|
|
1146
|
+
* person can still flip it from the control on the list.
|
|
1147
|
+
*/
|
|
1148
|
+
archiveFilter?: MessagingArchiveFilter | undefined;
|
|
1040
1149
|
children: ReactNode;
|
|
1041
1150
|
}
|
|
1042
1151
|
declare function MessagingProvider(props: MessagingProviderProps): ReactNode;
|
|
@@ -1061,6 +1170,22 @@ interface UseConversationsResult {
|
|
|
1061
1170
|
select: (id: ConversationId) => void;
|
|
1062
1171
|
activeConversationId: ConversationId | null;
|
|
1063
1172
|
startDirect: (otherUserId: UserId) => Promise<ConversationId>;
|
|
1173
|
+
/**
|
|
1174
|
+
* THE ARCHIVED-ITEMS LAW's state. `conversations` above is ALWAYS the rows
|
|
1175
|
+
* this filter asked the SERVER for — never a superset to sieve — so a count
|
|
1176
|
+
* taken over it can never disagree with what renders.
|
|
1177
|
+
*/
|
|
1178
|
+
archiveFilter: MessagingArchiveFilter;
|
|
1179
|
+
setArchiveFilter: (next: MessagingArchiveFilter) => void;
|
|
1180
|
+
/**
|
|
1181
|
+
* How many archived conversations exist, for a reveal label, or `null`
|
|
1182
|
+
* before the first count lands. `exact: false` means the count hit its cap
|
|
1183
|
+
* and the label must read "N+".
|
|
1184
|
+
*/
|
|
1185
|
+
archivedCount: {
|
|
1186
|
+
count: number;
|
|
1187
|
+
exact: boolean;
|
|
1188
|
+
} | null;
|
|
1064
1189
|
}
|
|
1065
1190
|
declare function useConversations(): UseConversationsResult;
|
|
1066
1191
|
interface UseConversationResult {
|
|
@@ -1538,4 +1663,4 @@ declare function summarizeText(content: string, maxLength?: number): string;
|
|
|
1538
1663
|
/** Serialize picked references into a fence the platform's other readers accept. */
|
|
1539
1664
|
declare function composeFence(references: readonly MatrxReference[]): string;
|
|
1540
1665
|
|
|
1541
|
-
export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, type AiCallArgs, type AiCapability, type AiResult, AlertIcon, type Attachment, Avatar, BotIcon, CheckIcon, ChevronLeftIcon, type ClientMessageId, ClockIcon, CloseIcon, Composer, type ComposerInputRenderProps, type Conversation, type ConversationCursor, type ConversationId, ConversationList, type ConversationListProps, type ConversationRowWrapperProps, ConversationSkeleton, type ConversationSummary, type ConversationThread, type ConversationType, ConversationView, type ConversationViewProps, type DeliveryState, DeliveryTick, DoubleCheckIcon, type DraftMessage, EmptyState, type EngineDiagnostic, type IncomingMessageContext, type JsonObject, type JsonValue, LinkIcon, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, MessageActionChips, type MessageActionRenderProps, type MessageActionRenderer, MessageBubble, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessageWrapperProps, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingHost, type MessagingIdentity, MessagingInbox, type MessagingInboxProps, MessagingProvider, type MessagingProviderProps, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, PaperclipIcon, type Participant, type ParticipantRole, PlusIcon, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, ReferenceCard, type ReferenceRenderer, ReplyIcon, type RepositoryOptions, type SchemaLike, SearchIcon, SendIcon, type SessionResolver, SparklesIcon, type SupabaseLike, TABLES, type TextSegment, TypingDots, type UseComposerResult, type UseConversationResult, type UseConversationsResult, type UseMessageActionResult, type UseMessagingAiResult, type UseTypistsResult, type UserId, type UserSummary, UsersIcon, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createWebOutboxStorage, effectiveActorKey, extractReferences, formatConversationTime, formatDateSeparator, formatLastSeen, formatMessageTime, formatTypists, groupMessages, inboxTopic, invalidResponse, isSameDay, messagingClientId, normalizeMessagingError, optimisticMessage, participantNames, projectConversationSummary, projectMessage, projectMessageAction, projectParticipantRole, projectUserSummary, resolveActor, splitText, summarizeText, unreadCutoff, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists };
|
|
1666
|
+
export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, type AiCallArgs, type AiCapability, type AiResult, AlertIcon, type Attachment, Avatar, BotIcon, CheckIcon, ChevronLeftIcon, type ClientMessageId, ClockIcon, CloseIcon, Composer, type ComposerInputRenderProps, type Conversation, type ConversationCursor, type ConversationId, ConversationList, type ConversationListProps, type ConversationRowWrapperProps, ConversationSkeleton, type ConversationSummary, type ConversationThread, type ConversationType, ConversationView, type ConversationViewProps, DEFAULT_MESSAGING_ARCHIVE_FILTER, type DeliveryState, DeliveryTick, DoubleCheckIcon, type DraftMessage, EmptyState, type EngineDiagnostic, type IncomingMessageContext, type JsonObject, type JsonValue, LinkIcon, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, MessageActionChips, type MessageActionRenderProps, type MessageActionRenderer, MessageBubble, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessageWrapperProps, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingArchiveFilter, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingHost, type MessagingIdentity, MessagingInbox, type MessagingInboxProps, MessagingProvider, type MessagingProviderProps, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, PaperclipIcon, type Participant, type ParticipantRole, PlusIcon, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, ReferenceCard, type ReferenceRenderer, ReplyIcon, type RepositoryOptions, type SchemaLike, SearchIcon, SendIcon, type SessionResolver, SparklesIcon, type SupabaseLike, TABLES, type TextSegment, TypingDots, type UseComposerResult, type UseConversationResult, type UseConversationsResult, type UseMessageActionResult, type UseMessagingAiResult, type UseTypistsResult, type UserId, type UserSummary, UsersIcon, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createWebOutboxStorage, effectiveActorKey, extractReferences, formatConversationTime, formatDateSeparator, formatLastSeen, formatMessageTime, formatTypists, groupMessages, inboxTopic, invalidResponse, isSameDay, messagingClientId, normalizeMessagingError, optimisticMessage, participantNames, projectConversationSummary, projectMessage, projectMessageAction, projectParticipantRole, projectUserSummary, resolveActor, splitText, summarizeText, toMessagingArchiveFilter, unreadCutoff, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists };
|
package/dist/react.d.ts
CHANGED
|
@@ -138,6 +138,39 @@ interface Message {
|
|
|
138
138
|
/** Set only while this message is in the outbox and its last send failed. */
|
|
139
139
|
readonly failureReason?: string | undefined;
|
|
140
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* THE ARCHIVED-ITEMS LAW's three states (Arman, 2026-09-09 —
|
|
143
|
+
* `common-docs/policies/archived-items.md`):
|
|
144
|
+
*
|
|
145
|
+
* > "everything should have an archive filter, and the default should always
|
|
146
|
+
* > hide archived, but seeing archived items should be one or two clicks
|
|
147
|
+
* > away … this is a system wide decision for every single item everywhere in
|
|
148
|
+
* > our system, for every single table and every single page."
|
|
149
|
+
*
|
|
150
|
+
* | value | what the list shows |
|
|
151
|
+
* |------------|--------------------------|
|
|
152
|
+
* | `active` | only un-archived rows |
|
|
153
|
+
* | `archived` | only archived rows |
|
|
154
|
+
* | `all` | every row |
|
|
155
|
+
*
|
|
156
|
+
* The same three words the rest of the platform uses — `ArchivedFilter` in
|
|
157
|
+
* matrx-frontend's `lib/entity-list`, `p_archived` on the `agx_*`/`wfx_*` and
|
|
158
|
+
* DM list RPCs, `ArchiveFilter` in `@ai-matrx/design-system`. A person meets
|
|
159
|
+
* ONE control, not one per app.
|
|
160
|
+
*
|
|
161
|
+
* 🚨 Three states, never a boolean. A boolean cannot say "archived only",
|
|
162
|
+
* which is exactly the view someone looking for a conversation they archived
|
|
163
|
+
* last month needs.
|
|
164
|
+
*/
|
|
165
|
+
type MessagingArchiveFilter = "active" | "archived" | "all";
|
|
166
|
+
/** The platform default: a list hides archived rows until asked. */
|
|
167
|
+
declare const DEFAULT_MESSAGING_ARCHIVE_FILTER: MessagingArchiveFilter;
|
|
168
|
+
/**
|
|
169
|
+
* Narrow an untrusted value (a stored preference, a URL param, a host knob) to
|
|
170
|
+
* the tri-state. Anything unrecognised falls back to the default rather than
|
|
171
|
+
* silently widening a list to archived conversations.
|
|
172
|
+
*/
|
|
173
|
+
declare function toMessagingArchiveFilter(value: unknown, fallback?: MessagingArchiveFilter): MessagingArchiveFilter;
|
|
141
174
|
interface ConversationSummary {
|
|
142
175
|
readonly conversation: Conversation;
|
|
143
176
|
readonly participants: readonly UserSummary[];
|
|
@@ -667,7 +700,30 @@ interface MessagingRepository {
|
|
|
667
700
|
listConversations(args?: {
|
|
668
701
|
limit?: number;
|
|
669
702
|
cursor?: ConversationCursor | null;
|
|
703
|
+
/**
|
|
704
|
+
* THE ARCHIVED-ITEMS LAW. Omitted means `active` — archived conversations
|
|
705
|
+
* are hidden until a caller asks for them. This is a REQUEST to the reader
|
|
706
|
+
* (the RPC's `p_archived`), never a client-side sieve, so `hasMore` and the
|
|
707
|
+
* cursor describe the rows the caller actually asked for.
|
|
708
|
+
*/
|
|
709
|
+
archived?: MessagingArchiveFilter;
|
|
670
710
|
}): Promise<Page<ConversationSummary, ConversationCursor>>;
|
|
711
|
+
/**
|
|
712
|
+
* How many ARCHIVED conversations the caller has — the number a reveal
|
|
713
|
+
* control prints ("Archived (12)").
|
|
714
|
+
*
|
|
715
|
+
* 🚨 It is capped, and it says so. A count is read by listing archived rows
|
|
716
|
+
* up to `limit`; if that many come back the result is `exact: false` and the
|
|
717
|
+
* UI must render "12+", never a confident "12". A screen never lies: a cap
|
|
718
|
+
* silently presented as a total is the same defect as a badge counting rows
|
|
719
|
+
* the list hides.
|
|
720
|
+
*/
|
|
721
|
+
countArchivedConversations(args?: {
|
|
722
|
+
limit?: number;
|
|
723
|
+
}): Promise<{
|
|
724
|
+
count: number;
|
|
725
|
+
exact: boolean;
|
|
726
|
+
}>;
|
|
671
727
|
getConversation(id: ConversationId): Promise<Conversation>;
|
|
672
728
|
listMessages(conversationId: ConversationId, args?: {
|
|
673
729
|
limit?: number;
|
|
@@ -754,11 +810,38 @@ interface MessagingSnapshot {
|
|
|
754
810
|
readonly activeConversationId: ConversationId | null;
|
|
755
811
|
/** Conversations WITH unread, not total unread messages (the origin's semantics). */
|
|
756
812
|
readonly totalUnreadConversations: number;
|
|
813
|
+
/**
|
|
814
|
+
* THE ARCHIVED-ITEMS LAW's state for this inbox. `conversations` above is
|
|
815
|
+
* ALWAYS the rows this filter asked the server for — never a superset a
|
|
816
|
+
* surface is expected to sieve, so counts and pagination cannot disagree
|
|
817
|
+
* with what renders.
|
|
818
|
+
*/
|
|
819
|
+
readonly archiveFilter: MessagingArchiveFilter;
|
|
820
|
+
/**
|
|
821
|
+
* How many archived conversations exist, for the reveal control's label, or
|
|
822
|
+
* `null` before the first count lands. `exact: false` means the count hit its
|
|
823
|
+
* cap and the label must read "N+" — a capped number shown as a total is the
|
|
824
|
+
* same lie as a badge counting hidden rows.
|
|
825
|
+
*/
|
|
826
|
+
readonly archivedCount: {
|
|
827
|
+
readonly count: number;
|
|
828
|
+
readonly exact: boolean;
|
|
829
|
+
} | null;
|
|
757
830
|
}
|
|
758
831
|
interface MessagingStore {
|
|
759
832
|
snapshot(): MessagingSnapshot;
|
|
760
833
|
subscribe(listener: (snapshot: MessagingSnapshot) => void): () => void;
|
|
761
834
|
setConversations(items: readonly ConversationSummary[], hasMore: boolean): void;
|
|
835
|
+
/**
|
|
836
|
+
* Switch the archive axis. Clears the list and marks it UNLOADED: the rows
|
|
837
|
+
* on screen belong to the old filter, and leaving them there while the new
|
|
838
|
+
* page is in flight shows active conversations under an "Archived" heading.
|
|
839
|
+
*/
|
|
840
|
+
setArchiveFilter(next: MessagingArchiveFilter): void;
|
|
841
|
+
setArchivedCount(value: {
|
|
842
|
+
count: number;
|
|
843
|
+
exact: boolean;
|
|
844
|
+
} | null): void;
|
|
762
845
|
appendConversations(items: readonly ConversationSummary[], hasMore: boolean): void;
|
|
763
846
|
upsertConversation(item: ConversationSummary): void;
|
|
764
847
|
removeConversation(id: ConversationId): void;
|
|
@@ -833,6 +916,15 @@ interface MessagingEngineOptions {
|
|
|
833
916
|
onIncoming?: ((message: Message) => void) | undefined;
|
|
834
917
|
conversationPageSize?: number | undefined;
|
|
835
918
|
messagePageSize?: number | undefined;
|
|
919
|
+
/**
|
|
920
|
+
* 🚨 OPINIONS BECOME KNOBS. THE ARCHIVED-ITEMS LAW's clause 6 says the
|
|
921
|
+
* filter's initial state is an org/user-configurable setting, not code
|
|
922
|
+
* taste. The PLATFORM default is `active` (hide archived) and a host that
|
|
923
|
+
* passes nothing gets exactly that; a host wires this to whatever its own
|
|
924
|
+
* setting resolves to. The person can always change it from the control on
|
|
925
|
+
* the list.
|
|
926
|
+
*/
|
|
927
|
+
archiveFilter?: MessagingArchiveFilter | undefined;
|
|
836
928
|
}
|
|
837
929
|
interface MessagingEngine {
|
|
838
930
|
readonly store: MessagingStore;
|
|
@@ -841,6 +933,14 @@ interface MessagingEngine {
|
|
|
841
933
|
/** Load page one of the inbox and start the inbox channel. */
|
|
842
934
|
start(): Promise<void>;
|
|
843
935
|
loadMoreConversations(): Promise<void>;
|
|
936
|
+
/**
|
|
937
|
+
* THE ARCHIVED-ITEMS LAW's control. Re-reads page one under the new state
|
|
938
|
+
* (the filter is a REQUEST to the server, never a client-side sieve) and
|
|
939
|
+
* refreshes the archived count the reveal label prints.
|
|
940
|
+
*/
|
|
941
|
+
setArchiveFilter(next: MessagingArchiveFilter): Promise<void>;
|
|
942
|
+
/** Re-read how many archived conversations exist, for the reveal's label. */
|
|
943
|
+
refreshArchivedCount(): Promise<void>;
|
|
844
944
|
/** Open a conversation: load its thread and subscribe to its channel. */
|
|
845
945
|
openConversation(id: ConversationId): Promise<void>;
|
|
846
946
|
closeConversation(id: ConversationId): void;
|
|
@@ -1037,6 +1137,15 @@ interface MessagingProviderProps {
|
|
|
1037
1137
|
resolveSession?: SessionResolver | undefined;
|
|
1038
1138
|
/** Override outbox persistence. Defaults to localStorage, then memory. */
|
|
1039
1139
|
outboxStorage?: OutboxStorage | undefined;
|
|
1140
|
+
/**
|
|
1141
|
+
* 🚨 OPINIONS BECOME KNOBS. THE ARCHIVED-ITEMS LAW (Arman, 2026-09-09 —
|
|
1142
|
+
* `common-docs/policies/archived-items.md`) clause 6: the archive filter's
|
|
1143
|
+
* initial state is an org/user setting, not code taste. The PLATFORM default
|
|
1144
|
+
* is `active` — archived conversations hidden — and a host that passes
|
|
1145
|
+
* nothing gets exactly that. Pass whatever your own setting resolves to; the
|
|
1146
|
+
* person can still flip it from the control on the list.
|
|
1147
|
+
*/
|
|
1148
|
+
archiveFilter?: MessagingArchiveFilter | undefined;
|
|
1040
1149
|
children: ReactNode;
|
|
1041
1150
|
}
|
|
1042
1151
|
declare function MessagingProvider(props: MessagingProviderProps): ReactNode;
|
|
@@ -1061,6 +1170,22 @@ interface UseConversationsResult {
|
|
|
1061
1170
|
select: (id: ConversationId) => void;
|
|
1062
1171
|
activeConversationId: ConversationId | null;
|
|
1063
1172
|
startDirect: (otherUserId: UserId) => Promise<ConversationId>;
|
|
1173
|
+
/**
|
|
1174
|
+
* THE ARCHIVED-ITEMS LAW's state. `conversations` above is ALWAYS the rows
|
|
1175
|
+
* this filter asked the SERVER for — never a superset to sieve — so a count
|
|
1176
|
+
* taken over it can never disagree with what renders.
|
|
1177
|
+
*/
|
|
1178
|
+
archiveFilter: MessagingArchiveFilter;
|
|
1179
|
+
setArchiveFilter: (next: MessagingArchiveFilter) => void;
|
|
1180
|
+
/**
|
|
1181
|
+
* How many archived conversations exist, for a reveal label, or `null`
|
|
1182
|
+
* before the first count lands. `exact: false` means the count hit its cap
|
|
1183
|
+
* and the label must read "N+".
|
|
1184
|
+
*/
|
|
1185
|
+
archivedCount: {
|
|
1186
|
+
count: number;
|
|
1187
|
+
exact: boolean;
|
|
1188
|
+
} | null;
|
|
1064
1189
|
}
|
|
1065
1190
|
declare function useConversations(): UseConversationsResult;
|
|
1066
1191
|
interface UseConversationResult {
|
|
@@ -1538,4 +1663,4 @@ declare function summarizeText(content: string, maxLength?: number): string;
|
|
|
1538
1663
|
/** Serialize picked references into a fence the platform's other readers accept. */
|
|
1539
1664
|
declare function composeFence(references: readonly MatrxReference[]): string;
|
|
1540
1665
|
|
|
1541
|
-
export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, type AiCallArgs, type AiCapability, type AiResult, AlertIcon, type Attachment, Avatar, BotIcon, CheckIcon, ChevronLeftIcon, type ClientMessageId, ClockIcon, CloseIcon, Composer, type ComposerInputRenderProps, type Conversation, type ConversationCursor, type ConversationId, ConversationList, type ConversationListProps, type ConversationRowWrapperProps, ConversationSkeleton, type ConversationSummary, type ConversationThread, type ConversationType, ConversationView, type ConversationViewProps, type DeliveryState, DeliveryTick, DoubleCheckIcon, type DraftMessage, EmptyState, type EngineDiagnostic, type IncomingMessageContext, type JsonObject, type JsonValue, LinkIcon, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, MessageActionChips, type MessageActionRenderProps, type MessageActionRenderer, MessageBubble, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessageWrapperProps, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingHost, type MessagingIdentity, MessagingInbox, type MessagingInboxProps, MessagingProvider, type MessagingProviderProps, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, PaperclipIcon, type Participant, type ParticipantRole, PlusIcon, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, ReferenceCard, type ReferenceRenderer, ReplyIcon, type RepositoryOptions, type SchemaLike, SearchIcon, SendIcon, type SessionResolver, SparklesIcon, type SupabaseLike, TABLES, type TextSegment, TypingDots, type UseComposerResult, type UseConversationResult, type UseConversationsResult, type UseMessageActionResult, type UseMessagingAiResult, type UseTypistsResult, type UserId, type UserSummary, UsersIcon, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createWebOutboxStorage, effectiveActorKey, extractReferences, formatConversationTime, formatDateSeparator, formatLastSeen, formatMessageTime, formatTypists, groupMessages, inboxTopic, invalidResponse, isSameDay, messagingClientId, normalizeMessagingError, optimisticMessage, participantNames, projectConversationSummary, projectMessage, projectMessageAction, projectParticipantRole, projectUserSummary, resolveActor, splitText, summarizeText, unreadCutoff, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists };
|
|
1666
|
+
export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, type AiCallArgs, type AiCapability, type AiResult, AlertIcon, type Attachment, Avatar, BotIcon, CheckIcon, ChevronLeftIcon, type ClientMessageId, ClockIcon, CloseIcon, Composer, type ComposerInputRenderProps, type Conversation, type ConversationCursor, type ConversationId, ConversationList, type ConversationListProps, type ConversationRowWrapperProps, ConversationSkeleton, type ConversationSummary, type ConversationThread, type ConversationType, ConversationView, type ConversationViewProps, DEFAULT_MESSAGING_ARCHIVE_FILTER, type DeliveryState, DeliveryTick, DoubleCheckIcon, type DraftMessage, EmptyState, type EngineDiagnostic, type IncomingMessageContext, type JsonObject, type JsonValue, LinkIcon, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, MessageActionChips, type MessageActionRenderProps, type MessageActionRenderer, MessageBubble, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessageWrapperProps, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingArchiveFilter, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingHost, type MessagingIdentity, MessagingInbox, type MessagingInboxProps, MessagingProvider, type MessagingProviderProps, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, PaperclipIcon, type Participant, type ParticipantRole, PlusIcon, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, ReferenceCard, type ReferenceRenderer, ReplyIcon, type RepositoryOptions, type SchemaLike, SearchIcon, SendIcon, type SessionResolver, SparklesIcon, type SupabaseLike, TABLES, type TextSegment, TypingDots, type UseComposerResult, type UseConversationResult, type UseConversationsResult, type UseMessageActionResult, type UseMessagingAiResult, type UseTypistsResult, type UserId, type UserSummary, UsersIcon, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createWebOutboxStorage, effectiveActorKey, extractReferences, formatConversationTime, formatDateSeparator, formatLastSeen, formatMessageTime, formatTypists, groupMessages, inboxTopic, invalidResponse, isSameDay, messagingClientId, normalizeMessagingError, optimisticMessage, participantNames, projectConversationSummary, projectMessage, projectMessageAction, projectParticipantRole, projectUserSummary, resolveActor, splitText, summarizeText, toMessagingArchiveFilter, unreadCutoff, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists };
|
package/dist/react.js
CHANGED
|
@@ -821,6 +821,17 @@ function projectConversationSummary(row, viewerId, fallbackOrganizationId) {
|
|
|
821
821
|
};
|
|
822
822
|
}
|
|
823
823
|
|
|
824
|
+
// src/core/types.ts
|
|
825
|
+
var asConversationId = (value) => value;
|
|
826
|
+
var asMessageId = (value) => value;
|
|
827
|
+
var asUserId = (value) => value;
|
|
828
|
+
var asOrganizationId = (value) => value;
|
|
829
|
+
var asClientMessageId = (value) => value;
|
|
830
|
+
var DEFAULT_MESSAGING_ARCHIVE_FILTER = "active";
|
|
831
|
+
function toMessagingArchiveFilter(value, fallback = DEFAULT_MESSAGING_ARCHIVE_FILTER) {
|
|
832
|
+
return value === "active" || value === "archived" || value === "all" ? value : fallback;
|
|
833
|
+
}
|
|
834
|
+
|
|
824
835
|
// src/core/store.ts
|
|
825
836
|
function timeOf(message) {
|
|
826
837
|
const stamp = message.editedAt ?? message.createdAt;
|
|
@@ -851,6 +862,8 @@ function createMessagingStore() {
|
|
|
851
862
|
let conversations = [];
|
|
852
863
|
let hasMoreConversations = false;
|
|
853
864
|
let hasLoadedConversations = false;
|
|
865
|
+
let archiveFilter = DEFAULT_MESSAGING_ARCHIVE_FILTER;
|
|
866
|
+
let archivedCount = null;
|
|
854
867
|
let threads = /* @__PURE__ */ new Map();
|
|
855
868
|
let activeConversationId = null;
|
|
856
869
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -863,7 +876,9 @@ function createMessagingStore() {
|
|
|
863
876
|
hasLoadedConversations,
|
|
864
877
|
threads,
|
|
865
878
|
activeConversationId,
|
|
866
|
-
totalUnreadConversations: conversations.filter((item) => item.unreadCount > 0).length
|
|
879
|
+
totalUnreadConversations: conversations.filter((item) => item.unreadCount > 0).length,
|
|
880
|
+
archiveFilter,
|
|
881
|
+
archivedCount
|
|
867
882
|
};
|
|
868
883
|
return cached;
|
|
869
884
|
}
|
|
@@ -910,6 +925,18 @@ function createMessagingStore() {
|
|
|
910
925
|
hasLoadedConversations = true;
|
|
911
926
|
emit();
|
|
912
927
|
},
|
|
928
|
+
setArchiveFilter(next) {
|
|
929
|
+
if (next === archiveFilter) return;
|
|
930
|
+
archiveFilter = next;
|
|
931
|
+
conversations = [];
|
|
932
|
+
hasMoreConversations = false;
|
|
933
|
+
hasLoadedConversations = false;
|
|
934
|
+
emit();
|
|
935
|
+
},
|
|
936
|
+
setArchivedCount(value) {
|
|
937
|
+
archivedCount = value;
|
|
938
|
+
emit();
|
|
939
|
+
},
|
|
913
940
|
appendConversations(items, hasMore) {
|
|
914
941
|
const byId = new Map(conversations.map((item) => [item.conversation.id, item]));
|
|
915
942
|
items.forEach((item) => byId.set(item.conversation.id, item));
|
|
@@ -1116,11 +1143,20 @@ function createMessagingEngine(options) {
|
|
|
1116
1143
|
}
|
|
1117
1144
|
async function reloadInbox() {
|
|
1118
1145
|
if (disposed) return;
|
|
1119
|
-
const page = await repository.listConversations({
|
|
1146
|
+
const page = await repository.listConversations({
|
|
1147
|
+
limit: conversationPageSize,
|
|
1148
|
+
archived: store.snapshot().archiveFilter
|
|
1149
|
+
});
|
|
1120
1150
|
if (disposed) return;
|
|
1121
1151
|
conversationCursor = page.nextCursor;
|
|
1122
1152
|
store.setConversations(page.items, page.hasMore);
|
|
1123
1153
|
}
|
|
1154
|
+
async function reloadArchivedCount() {
|
|
1155
|
+
if (disposed) return;
|
|
1156
|
+
const value = await repository.countArchivedConversations();
|
|
1157
|
+
if (disposed) return;
|
|
1158
|
+
store.setArchivedCount(value);
|
|
1159
|
+
}
|
|
1124
1160
|
async function backfillConversation(id) {
|
|
1125
1161
|
const thread = store.snapshot().threads.get(id);
|
|
1126
1162
|
const since = thread?.latestAt ?? null;
|
|
@@ -1147,7 +1183,13 @@ function createMessagingEngine(options) {
|
|
|
1147
1183
|
outbox,
|
|
1148
1184
|
identity,
|
|
1149
1185
|
async start() {
|
|
1186
|
+
if (options.archiveFilter !== void 0) {
|
|
1187
|
+
store.setArchiveFilter(options.archiveFilter);
|
|
1188
|
+
}
|
|
1150
1189
|
await reloadInbox();
|
|
1190
|
+
void reloadArchivedCount().catch(
|
|
1191
|
+
(error) => reportError(error, "refreshArchivedCount")
|
|
1192
|
+
);
|
|
1151
1193
|
if (disposed || inboxChannel !== null) return;
|
|
1152
1194
|
inboxChannel = manager.open({
|
|
1153
1195
|
topic: inboxTopic(identity.userId),
|
|
@@ -1189,12 +1231,33 @@ function createMessagingEngine(options) {
|
|
|
1189
1231
|
}
|
|
1190
1232
|
});
|
|
1191
1233
|
},
|
|
1234
|
+
async setArchiveFilter(next) {
|
|
1235
|
+
if (next === store.snapshot().archiveFilter) return;
|
|
1236
|
+
store.setArchiveFilter(next);
|
|
1237
|
+
conversationCursor = null;
|
|
1238
|
+
try {
|
|
1239
|
+
await reloadInbox();
|
|
1240
|
+
} catch (error) {
|
|
1241
|
+
reportError(error, "setArchiveFilter");
|
|
1242
|
+
}
|
|
1243
|
+
await reloadArchivedCount().catch(
|
|
1244
|
+
(error) => reportError(error, "refreshArchivedCount")
|
|
1245
|
+
);
|
|
1246
|
+
},
|
|
1247
|
+
async refreshArchivedCount() {
|
|
1248
|
+
try {
|
|
1249
|
+
await reloadArchivedCount();
|
|
1250
|
+
} catch (error) {
|
|
1251
|
+
reportError(error, "refreshArchivedCount");
|
|
1252
|
+
}
|
|
1253
|
+
},
|
|
1192
1254
|
async loadMoreConversations() {
|
|
1193
1255
|
if (conversationCursor === null) return;
|
|
1194
1256
|
try {
|
|
1195
1257
|
const page = await repository.listConversations({
|
|
1196
1258
|
limit: conversationPageSize,
|
|
1197
|
-
cursor: conversationCursor
|
|
1259
|
+
cursor: conversationCursor,
|
|
1260
|
+
archived: store.snapshot().archiveFilter
|
|
1198
1261
|
});
|
|
1199
1262
|
conversationCursor = page.nextCursor;
|
|
1200
1263
|
store.appendConversations(page.items, page.hasMore);
|
|
@@ -1458,6 +1521,7 @@ function createMessagingRepository(options) {
|
|
|
1458
1521
|
identity,
|
|
1459
1522
|
async listConversations(args = {}) {
|
|
1460
1523
|
const limit = args.limit ?? 30;
|
|
1524
|
+
const archived = args.archived ?? DEFAULT_MESSAGING_ARCHIVE_FILTER;
|
|
1461
1525
|
const operation = "listConversations";
|
|
1462
1526
|
const rows = await withSessionRetry(
|
|
1463
1527
|
operation,
|
|
@@ -1467,7 +1531,12 @@ function createMessagingRepository(options) {
|
|
|
1467
1531
|
p_user_id: identity.userId,
|
|
1468
1532
|
p_limit: limit + 1,
|
|
1469
1533
|
p_before_sort_at: args.cursor?.beforeSortAt ?? null,
|
|
1470
|
-
p_before_conversation_id: args.cursor?.beforeConversationId ?? null
|
|
1534
|
+
p_before_conversation_id: args.cursor?.beforeConversationId ?? null,
|
|
1535
|
+
// THE ARCHIVED-ITEMS LAW, SERVER-side. `get_dm_conversations_with_details`
|
|
1536
|
+
// used to hardcode `is_archived IS FALSE` with no parameter at all,
|
|
1537
|
+
// so an archived conversation was not hidden — it was unreachable.
|
|
1538
|
+
// The RPC gained `p_archived` on 2026-09-09 (register row R1).
|
|
1539
|
+
p_archived: archived
|
|
1471
1540
|
},
|
|
1472
1541
|
operation
|
|
1473
1542
|
)
|
|
@@ -1487,6 +1556,29 @@ function createMessagingRepository(options) {
|
|
|
1487
1556
|
nextCursor: hasMore && last !== void 0 ? { beforeSortAt: last.sortAt, beforeConversationId: last.conversation.id } : null
|
|
1488
1557
|
};
|
|
1489
1558
|
},
|
|
1559
|
+
async countArchivedConversations(args = {}) {
|
|
1560
|
+
const limit = args.limit ?? 100;
|
|
1561
|
+
const operation = "countArchivedConversations";
|
|
1562
|
+
const rows = await withSessionRetry(
|
|
1563
|
+
operation,
|
|
1564
|
+
() => rpc(
|
|
1565
|
+
RPCS.conversationsWithDetails,
|
|
1566
|
+
{
|
|
1567
|
+
p_user_id: identity.userId,
|
|
1568
|
+
p_limit: limit + 1,
|
|
1569
|
+
p_before_sort_at: null,
|
|
1570
|
+
p_before_conversation_id: null,
|
|
1571
|
+
p_archived: "archived"
|
|
1572
|
+
},
|
|
1573
|
+
operation
|
|
1574
|
+
)
|
|
1575
|
+
);
|
|
1576
|
+
if (rows !== null && !Array.isArray(rows)) {
|
|
1577
|
+
throw invalidResponse(operation, `${RPCS.conversationsWithDetails} did not return rows`);
|
|
1578
|
+
}
|
|
1579
|
+
const found = (rows ?? []).length;
|
|
1580
|
+
return found > limit ? { count: limit, exact: false } : { count: found, exact: true };
|
|
1581
|
+
},
|
|
1490
1582
|
async getConversation(id) {
|
|
1491
1583
|
const operation = "getConversation";
|
|
1492
1584
|
const { data, error } = await withSessionRetry(
|
|
@@ -1795,6 +1887,7 @@ function MessagingRuntime(props) {
|
|
|
1795
1887
|
onFallback: (message) => report({ level: "warn", message })
|
|
1796
1888
|
}),
|
|
1797
1889
|
onDiagnostic: report,
|
|
1890
|
+
...props.archiveFilter !== void 0 ? { archiveFilter: props.archiveFilter } : {},
|
|
1798
1891
|
onIncoming: (message) => {
|
|
1799
1892
|
const snapshot = built?.store.snapshot();
|
|
1800
1893
|
if (snapshot === void 0) return;
|
|
@@ -2025,6 +2118,11 @@ function useConversations() {
|
|
|
2025
2118
|
},
|
|
2026
2119
|
select,
|
|
2027
2120
|
activeConversationId: snapshot?.activeConversationId ?? null,
|
|
2121
|
+
archiveFilter: snapshot?.archiveFilter ?? DEFAULT_MESSAGING_ARCHIVE_FILTER,
|
|
2122
|
+
setArchiveFilter: (next) => {
|
|
2123
|
+
void host?.engine.setArchiveFilter(next);
|
|
2124
|
+
},
|
|
2125
|
+
archivedCount: snapshot?.archivedCount ?? null,
|
|
2028
2126
|
startDirect: async (otherUserId) => {
|
|
2029
2127
|
if (host === null) {
|
|
2030
2128
|
throw new Error("[@ai-matrx/messaging] startDirect called before the host was ready.");
|
|
@@ -2601,9 +2699,37 @@ var AI_LABELS = {
|
|
|
2601
2699
|
draftReply: "Draft a reply"
|
|
2602
2700
|
};
|
|
2603
2701
|
function ConversationList(props) {
|
|
2604
|
-
const {
|
|
2702
|
+
const {
|
|
2703
|
+
conversations,
|
|
2704
|
+
hasMore,
|
|
2705
|
+
isInitialLoading,
|
|
2706
|
+
loadMore,
|
|
2707
|
+
select,
|
|
2708
|
+
activeConversationId,
|
|
2709
|
+
archiveFilter,
|
|
2710
|
+
setArchiveFilter,
|
|
2711
|
+
archivedCount
|
|
2712
|
+
} = useConversations();
|
|
2605
2713
|
const RowChrome = useMessagingHost()?.wrapConversationRow ?? null;
|
|
2606
2714
|
const [query, setQuery] = useState2("");
|
|
2715
|
+
const showingArchive = archiveFilter === "archived";
|
|
2716
|
+
const archiveReveal = useMemo3(() => {
|
|
2717
|
+
if (showingArchive) {
|
|
2718
|
+
return {
|
|
2719
|
+
next: "active",
|
|
2720
|
+
label: "\u2190 Back to active conversations",
|
|
2721
|
+
ariaLabel: "Hide archived conversations"
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
if (archivedCount === null) return null;
|
|
2725
|
+
if (archivedCount.count === 0) return null;
|
|
2726
|
+
const printed = archivedCount.exact ? `${archivedCount.count}` : `${archivedCount.count}+`;
|
|
2727
|
+
return {
|
|
2728
|
+
next: "archived",
|
|
2729
|
+
label: `Archived (${printed})`,
|
|
2730
|
+
ariaLabel: "Show archived conversations"
|
|
2731
|
+
};
|
|
2732
|
+
}, [showingArchive, archivedCount]);
|
|
2607
2733
|
const visible = useMemo3(() => {
|
|
2608
2734
|
const needle = query.trim().toLowerCase();
|
|
2609
2735
|
if (needle.length === 0) return conversations;
|
|
@@ -2636,12 +2762,22 @@ function ConversationList(props) {
|
|
|
2636
2762
|
}
|
|
2637
2763
|
) : null
|
|
2638
2764
|
] }),
|
|
2765
|
+
archiveReveal !== null ? /* @__PURE__ */ jsx4("div", { className: "mx-msg__list-archive", children: /* @__PURE__ */ jsx4(
|
|
2766
|
+
"button",
|
|
2767
|
+
{
|
|
2768
|
+
type: "button",
|
|
2769
|
+
className: "mx-msg__chip",
|
|
2770
|
+
onClick: () => setArchiveFilter(archiveReveal.next),
|
|
2771
|
+
"aria-label": archiveReveal.ariaLabel,
|
|
2772
|
+
children: archiveReveal.label
|
|
2773
|
+
}
|
|
2774
|
+
) }) : null,
|
|
2639
2775
|
/* @__PURE__ */ jsxs2("div", { className: "mx-msg__scroll", children: [
|
|
2640
2776
|
isInitialLoading ? /* @__PURE__ */ jsx4(ConversationSkeleton, {}) : visible.length === 0 ? /* @__PURE__ */ jsx4(
|
|
2641
2777
|
EmptyState,
|
|
2642
2778
|
{
|
|
2643
|
-
title: query.length > 0 ? "No matches" : "No conversations yet",
|
|
2644
|
-
body: query.length > 0 ? "Try a different name or word." : "Start one and it will appear here."
|
|
2779
|
+
title: query.length > 0 ? "No matches" : showingArchive ? "No archived conversations" : "No conversations yet",
|
|
2780
|
+
body: query.length > 0 ? "Try a different name or word." : showingArchive ? "Archiving a conversation moves it here." : "Start one and it will appear here."
|
|
2645
2781
|
}
|
|
2646
2782
|
) : /* @__PURE__ */ jsx4("ul", { className: "mx-msg__rows", children: visible.map((item) => {
|
|
2647
2783
|
const row = /* @__PURE__ */ jsx4(
|
|
@@ -3136,13 +3272,6 @@ function MessagingInbox(props) {
|
|
|
3136
3272
|
}
|
|
3137
3273
|
);
|
|
3138
3274
|
}
|
|
3139
|
-
|
|
3140
|
-
// src/core/types.ts
|
|
3141
|
-
var asConversationId = (value) => value;
|
|
3142
|
-
var asMessageId = (value) => value;
|
|
3143
|
-
var asUserId = (value) => value;
|
|
3144
|
-
var asOrganizationId = (value) => value;
|
|
3145
|
-
var asClientMessageId = (value) => value;
|
|
3146
3275
|
export {
|
|
3147
3276
|
AgentTag,
|
|
3148
3277
|
AlertIcon,
|
|
@@ -3156,6 +3285,7 @@ export {
|
|
|
3156
3285
|
ConversationList,
|
|
3157
3286
|
ConversationSkeleton,
|
|
3158
3287
|
ConversationView,
|
|
3288
|
+
DEFAULT_MESSAGING_ARCHIVE_FILTER,
|
|
3159
3289
|
DeliveryTick,
|
|
3160
3290
|
DoubleCheckIcon,
|
|
3161
3291
|
EmptyState,
|
|
@@ -3218,6 +3348,7 @@ export {
|
|
|
3218
3348
|
resolveActor,
|
|
3219
3349
|
splitText,
|
|
3220
3350
|
summarizeText,
|
|
3351
|
+
toMessagingArchiveFilter,
|
|
3221
3352
|
unreadCutoff,
|
|
3222
3353
|
useComposer,
|
|
3223
3354
|
useConversation,
|