@cossistant/react 0.0.28 → 0.0.30
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/README.md +1 -1
- package/_virtual/rolldown_runtime.js +9 -23
- package/hooks/index.d.ts +3 -3
- package/hooks/private/store/use-conversations-store.d.ts +2 -0
- package/hooks/private/store/use-conversations-store.d.ts.map +1 -1
- package/hooks/private/store/use-conversations-store.js +15 -8
- package/hooks/private/store/use-conversations-store.js.map +1 -1
- package/hooks/private/store/use-store-selector.d.ts +3 -0
- package/hooks/private/store/use-store-selector.d.ts.map +1 -1
- package/hooks/private/store/use-store-selector.js +4 -8
- package/hooks/private/store/use-store-selector.js.map +1 -1
- package/hooks/private/store/use-website-store.d.ts +3 -1
- package/hooks/private/store/use-website-store.d.ts.map +1 -1
- package/hooks/private/store/use-website-store.js +14 -6
- package/hooks/private/store/use-website-store.js.map +1 -1
- package/hooks/private/use-client-query.d.ts +1 -1
- package/hooks/private/use-client-query.d.ts.map +1 -1
- package/hooks/private/use-client-query.js +1 -0
- package/hooks/private/use-client-query.js.map +1 -1
- package/hooks/private/use-default-messages.d.ts +1 -1
- package/hooks/private/use-grouped-messages.d.ts +10 -7
- package/hooks/private/use-grouped-messages.d.ts.map +1 -1
- package/hooks/private/use-grouped-messages.js +44 -11
- package/hooks/private/use-grouped-messages.js.map +1 -1
- package/hooks/private/use-rest-client.d.ts +13 -3
- package/hooks/private/use-rest-client.d.ts.map +1 -1
- package/hooks/private/use-rest-client.js +49 -22
- package/hooks/private/use-rest-client.js.map +1 -1
- package/hooks/private/use-visitor-typing-reporter.d.ts +1 -1
- package/hooks/use-conversation-auto-seen.d.ts +1 -1
- package/hooks/use-conversation-page.d.ts +1 -1
- package/hooks/use-conversation-page.d.ts.map +1 -1
- package/hooks/use-conversation-page.js +10 -3
- package/hooks/use-conversation-page.js.map +1 -1
- package/hooks/use-conversation-preview.d.ts +3 -1
- package/hooks/use-conversation-preview.d.ts.map +1 -1
- package/hooks/use-conversation-preview.js +6 -3
- package/hooks/use-conversation-preview.js.map +1 -1
- package/hooks/use-conversation-seen.d.ts +1 -1
- package/hooks/use-conversation-seen.js +1 -1
- package/hooks/use-conversation-seen.js.map +1 -1
- package/hooks/use-conversation-timeline-items.d.ts +1 -1
- package/hooks/use-conversation-timeline-items.js +2 -3
- package/hooks/use-conversation-timeline-items.js.map +1 -1
- package/hooks/use-conversation-timeline.d.ts +1 -1
- package/hooks/use-conversation-timeline.d.ts.map +1 -1
- package/hooks/use-conversation-timeline.js +1 -3
- package/hooks/use-conversation-timeline.js.map +1 -1
- package/hooks/use-conversation.d.ts +1 -1
- package/hooks/use-conversation.js +2 -3
- package/hooks/use-conversation.js.map +1 -1
- package/hooks/use-conversations.d.ts +1 -1
- package/hooks/use-conversations.js +5 -3
- package/hooks/use-conversations.js.map +1 -1
- package/hooks/use-create-conversation.d.ts +3 -3
- package/hooks/use-create-conversation.js +1 -0
- package/hooks/use-create-conversation.js.map +1 -1
- package/hooks/use-file-upload.d.ts +1 -1
- package/hooks/use-file-upload.js +3 -3
- package/hooks/use-file-upload.js.map +1 -1
- package/hooks/use-home-page.js +3 -3
- package/hooks/use-home-page.js.map +1 -1
- package/hooks/use-message-composer.d.ts +10 -3
- package/hooks/use-message-composer.d.ts.map +1 -1
- package/hooks/use-message-composer.js +5 -2
- package/hooks/use-message-composer.js.map +1 -1
- package/hooks/use-realtime-support.d.ts +1 -1
- package/hooks/use-send-message.d.ts +8 -2
- package/hooks/use-send-message.d.ts.map +1 -1
- package/hooks/use-send-message.js +5 -3
- package/hooks/use-send-message.js.map +1 -1
- package/hooks/use-visitor.js +2 -2
- package/hooks/use-visitor.js.map +1 -1
- package/identify-visitor.d.ts.map +1 -1
- package/identify-visitor.js +15 -1
- package/identify-visitor.js.map +1 -1
- package/index.d.ts +3 -3
- package/index.js +1 -1
- package/package.json +5 -3
- package/{conversation.d.ts → packages/types/src/api/conversation.d.ts} +368 -64
- package/packages/types/src/api/conversation.d.ts.map +1 -0
- package/packages/types/src/api/timeline-item.d.ts +460 -0
- package/packages/types/src/api/timeline-item.d.ts.map +1 -0
- package/packages/types/src/realtime-events.d.ts +1004 -0
- package/packages/types/src/realtime-events.d.ts.map +1 -0
- package/{schemas3.d.ts → packages/types/src/schemas.d.ts} +95 -19
- package/packages/types/src/schemas.d.ts.map +1 -0
- package/primitives/avatar/avatar.js +1 -1
- package/primitives/avatar/avatar.js.map +1 -1
- package/primitives/avatar/fallback.d.ts.map +1 -1
- package/primitives/avatar/fallback.js +2 -2
- package/primitives/avatar/fallback.js.map +1 -1
- package/primitives/avatar/image.d.ts +1 -1
- package/primitives/avatar/image.js +1 -1
- package/primitives/avatar/image.js.map +1 -1
- package/primitives/button.js +1 -1
- package/primitives/button.js.map +1 -1
- package/primitives/conversation-timeline.d.ts +1 -1
- package/primitives/conversation-timeline.js +4 -4
- package/primitives/conversation-timeline.js.map +1 -1
- package/primitives/day-separator.d.ts +76 -0
- package/primitives/day-separator.d.ts.map +1 -0
- package/primitives/day-separator.js +111 -0
- package/primitives/day-separator.js.map +1 -0
- package/primitives/index.d.ts +3 -2
- package/primitives/index.js +6 -1
- package/primitives/index.parts.d.ts +2 -1
- package/primitives/index.parts.js +2 -1
- package/primitives/multimodal-input.d.ts +2 -2
- package/primitives/multimodal-input.d.ts.map +1 -1
- package/primitives/multimodal-input.js +2 -2
- package/primitives/multimodal-input.js.map +1 -1
- package/primitives/timeline-item-attachments.d.ts +1 -1
- package/primitives/timeline-item-attachments.js +6 -7
- package/primitives/timeline-item-attachments.js.map +1 -1
- package/primitives/timeline-item-group.d.ts +1 -1
- package/primitives/timeline-item-group.d.ts.map +1 -1
- package/primitives/timeline-item-group.js +8 -8
- package/primitives/timeline-item-group.js.map +1 -1
- package/primitives/timeline-item.d.ts +1 -1
- package/primitives/timeline-item.d.ts.map +1 -1
- package/primitives/timeline-item.js +33 -8
- package/primitives/timeline-item.js.map +1 -1
- package/primitives/trigger.js +1 -1
- package/primitives/trigger.js.map +1 -1
- package/primitives/window.js +1 -1
- package/primitives/window.js.map +1 -1
- package/provider.d.ts +4 -2
- package/provider.d.ts.map +1 -1
- package/provider.js +56 -8
- package/provider.js.map +1 -1
- package/realtime/event-filter.d.ts +4 -1
- package/realtime/event-filter.d.ts.map +1 -1
- package/realtime/event-filter.js +14 -0
- package/realtime/event-filter.js.map +1 -1
- package/realtime/provider.d.ts +1 -1
- package/realtime/provider.d.ts.map +1 -1
- package/realtime/provider.js +1 -2
- package/realtime/provider.js.map +1 -1
- package/realtime/seen-store.d.ts +2 -2
- package/realtime/support-provider.js +6 -1
- package/realtime/support-provider.js.map +1 -1
- package/realtime/typing-store.d.ts +1 -1
- package/realtime/use-realtime.d.ts +1 -1
- package/support/components/avatar-stack.d.ts.map +1 -1
- package/support/components/avatar-stack.js +32 -12
- package/support/components/avatar-stack.js.map +1 -1
- package/support/components/avatar.d.ts +34 -3
- package/support/components/avatar.d.ts.map +1 -1
- package/support/components/avatar.js +61 -8
- package/support/components/avatar.js.map +1 -1
- package/support/components/button.d.ts +4 -2
- package/support/components/button.d.ts.map +1 -1
- package/support/components/button.js +3 -3
- package/support/components/button.js.map +1 -1
- package/support/components/configuration-error.d.ts +16 -0
- package/support/components/configuration-error.d.ts.map +1 -0
- package/support/components/configuration-error.js +162 -0
- package/support/components/configuration-error.js.map +1 -0
- package/support/components/content.js +1 -2
- package/support/components/content.js.map +1 -1
- package/support/components/conversation-button-link.js +18 -23
- package/support/components/conversation-button-link.js.map +1 -1
- package/support/components/conversation-event.d.ts.map +1 -1
- package/support/components/conversation-event.js +7 -5
- package/support/components/conversation-event.js.map +1 -1
- package/support/components/conversation-timeline.d.ts +6 -1
- package/support/components/conversation-timeline.d.ts.map +1 -1
- package/support/components/conversation-timeline.js +22 -2
- package/support/components/conversation-timeline.js.map +1 -1
- package/support/components/header.js +1 -1
- package/support/components/image-lightbox.d.ts +1 -1
- package/support/components/image-lightbox.js +1 -2
- package/support/components/image-lightbox.js.map +1 -1
- package/support/components/index.js +1 -1
- package/support/components/multimodal-input.js +0 -1
- package/support/components/multimodal-input.js.map +1 -1
- package/support/components/navigation-tab.js +1 -1
- package/support/components/online-indicator.d.ts +50 -0
- package/support/components/online-indicator.d.ts.map +1 -0
- package/support/components/online-indicator.js +65 -0
- package/support/components/online-indicator.js.map +1 -0
- package/support/components/root.js +0 -1
- package/support/components/root.js.map +1 -1
- package/support/components/timeline-identification-tool.js +4 -4
- package/support/components/timeline-identification-tool.js.map +1 -1
- package/support/components/timeline-message-group.d.ts +1 -1
- package/support/components/timeline-message-group.d.ts.map +1 -1
- package/support/components/timeline-message-group.js +6 -4
- package/support/components/timeline-message-group.js.map +1 -1
- package/support/components/timeline-message-item.d.ts +1 -1
- package/support/components/timeline-message-item.js +4 -4
- package/support/components/timeline-message-item.js.map +1 -1
- package/support/components/trigger.js +1 -2
- package/support/components/trigger.js.map +1 -1
- package/support/components/typing-indicator.js +1 -1
- package/support/components/typing-indicator.js.map +1 -1
- package/support/context/controlled-state.js +0 -1
- package/support/context/controlled-state.js.map +1 -1
- package/support/context/events.d.ts +1 -1
- package/support/context/events.js +0 -1
- package/support/context/events.js.map +1 -1
- package/support/context/handle.js +0 -1
- package/support/context/handle.js.map +1 -1
- package/support/context/identification.d.ts +33 -0
- package/support/context/identification.d.ts.map +1 -0
- package/support/context/identification.js +34 -0
- package/support/context/identification.js.map +1 -0
- package/support/context/positioning.js +0 -1
- package/support/context/positioning.js.map +1 -1
- package/support/context/slots.js +0 -1
- package/support/context/slots.js.map +1 -1
- package/support/context/websocket.d.ts +1 -1
- package/support/context/websocket.js +0 -1
- package/support/context/websocket.js.map +1 -1
- package/support/index.d.ts.map +1 -1
- package/support/index.js +51 -18
- package/support/index.js.map +1 -1
- package/support/pages/conversation-history.js +2 -1
- package/support/pages/conversation-history.js.map +1 -1
- package/support/pages/conversation.d.ts +1 -1
- package/support/pages/conversation.js +1 -1
- package/support/pages/conversation.js.map +1 -1
- package/support/pages/home.js +5 -3
- package/support/pages/home.js.map +1 -1
- package/support/router.d.ts.map +1 -1
- package/support/router.js +4 -0
- package/support/router.js.map +1 -1
- package/support/store/support-store.js +0 -1
- package/support/store/support-store.js.map +1 -1
- package/support/{support-C7Xaw-N6.css → support-DmViRaga.css} +2 -2
- package/support/{support-C7Xaw-N6.css.map → support-DmViRaga.css.map} +1 -1
- package/support/text/index.d.ts +1 -1
- package/support/text/index.d.ts.map +1 -1
- package/support/text/index.js +1 -1
- package/support/text/index.js.map +1 -1
- package/support/text/locales/en.js +1 -1
- package/support/text/locales/en.js.map +1 -1
- package/support/text/locales/es.js +1 -1
- package/support/text/locales/es.js.map +1 -1
- package/support/text/locales/fr.js +1 -1
- package/support/text/locales/fr.js.map +1 -1
- package/support/utils/index.d.ts +1 -1
- package/support-config.js +0 -1
- package/support-config.js.map +1 -1
- package/support.css +1 -1
- package/tailwind.css +1 -1
- package/utils/conversation.d.ts.map +1 -1
- package/utils/conversation.js +1 -3
- package/utils/conversation.js.map +1 -1
- package/utils/use-render-element.d.ts.map +1 -1
- package/utils/use-render-element.js +20 -5
- package/utils/use-render-element.js.map +1 -1
- package/api.d.ts +0 -71
- package/api.d.ts.map +0 -1
- package/checks.d.ts +0 -189
- package/checks.d.ts.map +0 -1
- package/clsx.d.ts +0 -7
- package/clsx.d.ts.map +0 -1
- package/coerce.d.ts +0 -9
- package/coerce.d.ts.map +0 -1
- package/conversation.d.ts.map +0 -1
- package/core.d.ts +0 -35
- package/core.d.ts.map +0 -1
- package/errors.d.ts +0 -121
- package/errors.d.ts.map +0 -1
- package/errors2.d.ts +0 -24
- package/errors2.d.ts.map +0 -1
- package/index2.d.ts +0 -4
- package/index3.d.ts +0 -1
- package/metadata.d.ts +0 -1
- package/openapi-generator.d.ts +0 -1
- package/openapi-generator2.d.ts +0 -1
- package/openapi-generator3.d.ts +0 -1
- package/openapi30.d.ts +0 -125
- package/openapi30.d.ts.map +0 -1
- package/openapi31.d.ts +0 -131
- package/openapi31.d.ts.map +0 -1
- package/parse.d.ts +0 -17
- package/parse.d.ts.map +0 -1
- package/realtime-events.d.ts +0 -482
- package/realtime-events.d.ts.map +0 -1
- package/registries.d.ts +0 -32
- package/registries.d.ts.map +0 -1
- package/schemas.d.ts +0 -673
- package/schemas.d.ts.map +0 -1
- package/schemas2.d.ts +0 -320
- package/schemas2.d.ts.map +0 -1
- package/schemas3.d.ts.map +0 -1
- package/specification-extension.d.ts +0 -9
- package/specification-extension.d.ts.map +0 -1
- package/standard-schema.d.ts +0 -59
- package/standard-schema.d.ts.map +0 -1
- package/timeline-item.d.ts +0 -227
- package/timeline-item.d.ts.map +0 -1
- package/util.d.ts +0 -41
- package/util.d.ts.map +0 -1
- package/versions.d.ts +0 -9
- package/versions.d.ts.map +0 -1
- package/zod-extensions.d.ts +0 -39
- package/zod-extensions.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Build fully featured customer support experiences in React with the official `@cossistant/react` package. The SDK wraps the REST and WebSocket APIs, comes with a prebuilt widget, hooks, and UI primitives so you can ship your support quickly and customize later.
|
|
4
4
|
|
|
5
|
-
> 📚 **New to Cossistant?** Follow the [Quickstart guide](https://cossistant/docs/quickstart) in our official documentation.
|
|
5
|
+
> 📚 **New to Cossistant?** Follow the [Quickstart guide](https://cossistant.com/docs/quickstart) in our official documentation.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -1,32 +1,18 @@
|
|
|
1
1
|
//#region rolldown:runtime
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
|
-
var
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (all) => {
|
|
3
|
+
var __export = (all, symbols) => {
|
|
9
4
|
let target = {};
|
|
10
|
-
for (var name in all)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return target;
|
|
15
|
-
};
|
|
16
|
-
var __copyProps = (to, from, except, desc) => {
|
|
17
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
18
|
-
key = keys[i];
|
|
19
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
20
|
-
get: ((k) => from[k]).bind(null, key),
|
|
21
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
5
|
+
for (var name in all) {
|
|
6
|
+
__defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
22
9
|
});
|
|
23
10
|
}
|
|
24
|
-
|
|
11
|
+
if (symbols) {
|
|
12
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
25
15
|
};
|
|
26
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
27
|
-
value: mod,
|
|
28
|
-
enumerable: true
|
|
29
|
-
}) : target, mod));
|
|
30
16
|
|
|
31
17
|
//#endregion
|
|
32
18
|
export { __export };
|
package/hooks/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useClientQuery } from "./private/use-client-query.js";
|
|
2
2
|
import { useDefaultMessages } from "./private/use-default-messages.js";
|
|
3
|
-
import { ConversationItem, GroupedMessage, TimelineEventItem, TimelineToolItem, UseGroupedMessagesOptions, UseGroupedMessagesProps, useGroupedMessages } from "./private/use-grouped-messages.js";
|
|
3
|
+
import { ConversationItem, DaySeparatorItem, GroupedMessage, TimelineEventItem, TimelineToolItem, UseGroupedMessagesOptions, UseGroupedMessagesProps, useGroupedMessages } from "./private/use-grouped-messages.js";
|
|
4
4
|
import { UseMultimodalInputOptions, UseMultimodalInputReturn, useMultimodalInput } from "./private/use-multimodal-input.js";
|
|
5
|
-
import { UseClientResult, useClient } from "./private/use-rest-client.js";
|
|
5
|
+
import { ConfigurationError, UseClientResult, useClient } from "./private/use-rest-client.js";
|
|
6
6
|
import { UseComposerRefocusOptions, UseComposerRefocusReturn, useComposerRefocus } from "./use-composer-refocus.js";
|
|
7
7
|
import { UseConversationOptions, UseConversationResult, useConversation } from "./use-conversation.js";
|
|
8
8
|
import { CONVERSATION_AUTO_SEEN_DELAY_MS, UseConversationAutoSeenOptions, useConversationAutoSeen } from "./use-conversation-auto-seen.js";
|
|
@@ -27,4 +27,4 @@ import { UseSoundEffectOptions, UseSoundEffectReturn, useSoundEffect } from "./u
|
|
|
27
27
|
import { useTypingSound } from "./use-typing-sound.js";
|
|
28
28
|
import { UseVisitorReturn, useVisitor } from "./use-visitor.js";
|
|
29
29
|
import { WindowVisibilityFocusState, useWindowVisibilityFocus } from "./use-window-visibility-focus.js";
|
|
30
|
-
export { CONVERSATION_AUTO_SEEN_DELAY_MS, ConversationItem, ConversationLifecycleState, ConversationPreviewAssignedAgent, ConversationPreviewLastMessage, ConversationPreviewTypingParticipant, ConversationPreviewTypingState, ConversationTimelineTypingParticipant, ConversationTypingParticipant, CreateConversationVariables, FileUploadPart, GroupedMessage, SendMessageOptions, SendMessageResult, TimelineEventItem, TimelineToolItem, UseClientResult, UseComposerRefocusOptions, UseComposerRefocusReturn, UseConversationAutoSeenOptions, UseConversationHistoryPageOptions, UseConversationHistoryPageReturn, UseConversationLifecycleOptions, UseConversationLifecycleReturn, UseConversationOptions, UseConversationPageOptions, UseConversationPageReturn, UseConversationPreviewOptions, UseConversationPreviewReturn, UseConversationResult, UseConversationTimelineItemsOptions, UseConversationTimelineItemsResult, UseConversationTimelineOptions, UseConversationTimelineReturn, UseConversationsOptions, UseConversationsResult, UseCreateConversationOptions, UseCreateConversationResult, UseFileUploadOptions, UseFileUploadReturn, UseGroupedMessagesOptions, UseGroupedMessagesProps, UseHomePageOptions, UseHomePageReturn, UseMessageComposerOptions, UseMessageComposerReturn, UseMultimodalInputOptions, UseMultimodalInputReturn, UseRealtimeSupportOptions, UseRealtimeSupportResult, UseScrollMaskOptions, UseScrollMaskReturn, UseSendMessageOptions, UseSendMessageResult, UseSoundEffectOptions, UseSoundEffectReturn, UseVisitorReturn, WindowVisibilityFocusState, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useFileUpload, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useNewMessageSound, useRealtimeSupport, useScrollMask, useSendMessage, useSoundEffect, useTypingSound, useVisitor, useWindowVisibilityFocus };
|
|
30
|
+
export { CONVERSATION_AUTO_SEEN_DELAY_MS, ConfigurationError, ConversationItem, ConversationLifecycleState, ConversationPreviewAssignedAgent, ConversationPreviewLastMessage, ConversationPreviewTypingParticipant, ConversationPreviewTypingState, ConversationTimelineTypingParticipant, ConversationTypingParticipant, CreateConversationVariables, DaySeparatorItem, FileUploadPart, GroupedMessage, SendMessageOptions, SendMessageResult, TimelineEventItem, TimelineToolItem, UseClientResult, UseComposerRefocusOptions, UseComposerRefocusReturn, UseConversationAutoSeenOptions, UseConversationHistoryPageOptions, UseConversationHistoryPageReturn, UseConversationLifecycleOptions, UseConversationLifecycleReturn, UseConversationOptions, UseConversationPageOptions, UseConversationPageReturn, UseConversationPreviewOptions, UseConversationPreviewReturn, UseConversationResult, UseConversationTimelineItemsOptions, UseConversationTimelineItemsResult, UseConversationTimelineOptions, UseConversationTimelineReturn, UseConversationsOptions, UseConversationsResult, UseCreateConversationOptions, UseCreateConversationResult, UseFileUploadOptions, UseFileUploadReturn, UseGroupedMessagesOptions, UseGroupedMessagesProps, UseHomePageOptions, UseHomePageReturn, UseMessageComposerOptions, UseMessageComposerReturn, UseMultimodalInputOptions, UseMultimodalInputReturn, UseRealtimeSupportOptions, UseRealtimeSupportResult, UseScrollMaskOptions, UseScrollMaskReturn, UseSendMessageOptions, UseSendMessageResult, UseSoundEffectOptions, UseSoundEffectReturn, UseVisitorReturn, WindowVisibilityFocusState, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useFileUpload, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useNewMessageSound, useRealtimeSupport, useScrollMask, useSendMessage, useSoundEffect, useTypingSound, useVisitor, useWindowVisibilityFocus };
|
|
@@ -9,6 +9,8 @@ type ConversationSelection = {
|
|
|
9
9
|
/**
|
|
10
10
|
* Selector hook that exposes the normalized conversations list from the
|
|
11
11
|
* internal store alongside pagination metadata.
|
|
12
|
+
*
|
|
13
|
+
* Returns empty selection when client is not available (configuration error).
|
|
12
14
|
*/
|
|
13
15
|
declare function useConversationsStore(): ConversationSelection;
|
|
14
16
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-conversations-store.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-conversations-store.ts"],"sourcesContent":[],"mappings":";;;;KAQK,qBAAA;iBACW;EADX,UAAA,EAEQ,sBAFa,GACV,IAAA;
|
|
1
|
+
{"version":3,"file":"use-conversations-store.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-conversations-store.ts"],"sourcesContent":[],"mappings":";;;;KAQK,qBAAA;iBACW;EADX,UAAA,EAEQ,sBAFa,GACV,IAAA;AA+ChB,CAAA;AA2BA;;;;;;iBA3BgB,qBAAA,CAAA,GAAyB;;;;;iBA2BzB,mBAAA,iCAEb"}
|
|
@@ -2,6 +2,10 @@ import { useStoreSelector } from "./use-store-selector.js";
|
|
|
2
2
|
import { useSupport } from "../../../provider.js";
|
|
3
3
|
|
|
4
4
|
//#region src/hooks/private/store/use-conversations-store.ts
|
|
5
|
+
const EMPTY_SELECTION = {
|
|
6
|
+
conversations: [],
|
|
7
|
+
pagination: null
|
|
8
|
+
};
|
|
5
9
|
function areSelectionsEqual(a, b) {
|
|
6
10
|
if (a === b) return true;
|
|
7
11
|
if (a.pagination !== b.pagination) {
|
|
@@ -15,14 +19,18 @@ function areSelectionsEqual(a, b) {
|
|
|
15
19
|
/**
|
|
16
20
|
* Selector hook that exposes the normalized conversations list from the
|
|
17
21
|
* internal store alongside pagination metadata.
|
|
22
|
+
*
|
|
23
|
+
* Returns empty selection when client is not available (configuration error).
|
|
18
24
|
*/
|
|
19
25
|
function useConversationsStore() {
|
|
20
26
|
const { client } = useSupport();
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
return useStoreSelector(client?.conversationsStore ?? null, (state) => {
|
|
28
|
+
if (!state) return EMPTY_SELECTION;
|
|
29
|
+
return {
|
|
30
|
+
conversations: state.ids.map((id) => state.byId[id]).filter((conversation) => conversation !== void 0),
|
|
31
|
+
pagination: state.pagination
|
|
32
|
+
};
|
|
33
|
+
}, areSelectionsEqual);
|
|
26
34
|
}
|
|
27
35
|
/**
|
|
28
36
|
* Picks a single conversation entity from the store by id. Returns `null` when
|
|
@@ -30,9 +38,8 @@ function useConversationsStore() {
|
|
|
30
38
|
*/
|
|
31
39
|
function useConversationById(conversationId) {
|
|
32
40
|
const { client } = useSupport();
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (!conversationId) return null;
|
|
41
|
+
return useStoreSelector(client?.conversationsStore ?? null, (state) => {
|
|
42
|
+
if (!(state && conversationId)) return null;
|
|
36
43
|
return state.byId[conversationId] ?? null;
|
|
37
44
|
});
|
|
38
45
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-conversations-store.js","names":[],"sources":["../../../../src/hooks/private/store/use-conversations-store.ts"],"sourcesContent":["import type {\n\tConversationPagination,\n\tConversationsState,\n} from \"@cossistant/core\";\nimport type { Conversation } from \"@cossistant/types\";\nimport { useSupport } from \"../../../provider\";\nimport { useStoreSelector } from \"./use-store-selector\";\n\ntype ConversationSelection = {\n\tconversations: Conversation[];\n\tpagination: ConversationPagination | null;\n};\n\nfunction areSelectionsEqual(\n\ta: ConversationSelection,\n\tb: ConversationSelection\n): boolean {\n\tif (a === b) {\n\t\treturn true;\n\t}\n\tif (a.pagination !== b.pagination) {\n\t\tif (!(a.pagination && b.pagination)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (\n\t\t\ta.pagination.page !== b.pagination.page ||\n\t\t\ta.pagination.limit !== b.pagination.limit ||\n\t\t\ta.pagination.total !== b.pagination.total ||\n\t\t\ta.pagination.totalPages !== b.pagination.totalPages ||\n\t\t\ta.pagination.hasMore !== b.pagination.hasMore\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t}\n\tif (a.conversations.length !== b.conversations.length) {\n\t\treturn false;\n\t}\n\tfor (let index = 0; index < a.conversations.length; index += 1) {\n\t\tif (a.conversations[index] !== b.conversations[index]) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n/**\n * Selector hook that exposes the normalized conversations list from the\n * internal store alongside pagination metadata.\n */\nexport function useConversationsStore(): ConversationSelection {\n\tconst { client } = useSupport();\n\n\
|
|
1
|
+
{"version":3,"file":"use-conversations-store.js","names":["EMPTY_SELECTION: ConversationSelection"],"sources":["../../../../src/hooks/private/store/use-conversations-store.ts"],"sourcesContent":["import type {\n\tConversationPagination,\n\tConversationsState,\n} from \"@cossistant/core\";\nimport type { Conversation } from \"@cossistant/types\";\nimport { useSupport } from \"../../../provider\";\nimport { useStoreSelector } from \"./use-store-selector\";\n\ntype ConversationSelection = {\n\tconversations: Conversation[];\n\tpagination: ConversationPagination | null;\n};\n\nconst EMPTY_SELECTION: ConversationSelection = {\n\tconversations: [],\n\tpagination: null,\n};\n\nfunction areSelectionsEqual(\n\ta: ConversationSelection,\n\tb: ConversationSelection\n): boolean {\n\tif (a === b) {\n\t\treturn true;\n\t}\n\tif (a.pagination !== b.pagination) {\n\t\tif (!(a.pagination && b.pagination)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (\n\t\t\ta.pagination.page !== b.pagination.page ||\n\t\t\ta.pagination.limit !== b.pagination.limit ||\n\t\t\ta.pagination.total !== b.pagination.total ||\n\t\t\ta.pagination.totalPages !== b.pagination.totalPages ||\n\t\t\ta.pagination.hasMore !== b.pagination.hasMore\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t}\n\tif (a.conversations.length !== b.conversations.length) {\n\t\treturn false;\n\t}\n\tfor (let index = 0; index < a.conversations.length; index += 1) {\n\t\tif (a.conversations[index] !== b.conversations[index]) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n/**\n * Selector hook that exposes the normalized conversations list from the\n * internal store alongside pagination metadata.\n *\n * Returns empty selection when client is not available (configuration error).\n */\nexport function useConversationsStore(): ConversationSelection {\n\tconst { client } = useSupport();\n\n\treturn useStoreSelector(\n\t\tclient?.conversationsStore ?? null,\n\t\t(state: ConversationsState | null) => {\n\t\t\tif (!state) {\n\t\t\t\treturn EMPTY_SELECTION;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tconversations: state.ids\n\t\t\t\t\t.map((id) => state.byId[id])\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(conversation): conversation is Conversation =>\n\t\t\t\t\t\t\tconversation !== undefined\n\t\t\t\t\t),\n\t\t\t\tpagination: state.pagination,\n\t\t\t};\n\t\t},\n\t\tareSelectionsEqual\n\t);\n}\n\n/**\n * Picks a single conversation entity from the store by id. Returns `null` when\n * the identifier is missing or the entity has not been fetched yet.\n */\nexport function useConversationById(\n\tconversationId: string | null\n): Conversation | null {\n\tconst { client } = useSupport();\n\n\treturn useStoreSelector(\n\t\tclient?.conversationsStore ?? null,\n\t\t(state: ConversationsState | null) => {\n\t\t\tif (!(state && conversationId)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn state.byId[conversationId] ?? null;\n\t\t}\n\t);\n}\n"],"mappings":";;;;AAaA,MAAMA,kBAAyC;CAC9C,eAAe,EAAE;CACjB,YAAY;CACZ;AAED,SAAS,mBACR,GACA,GACU;AACV,KAAI,MAAM,EACT,QAAO;AAER,KAAI,EAAE,eAAe,EAAE,YAAY;AAClC,MAAI,EAAE,EAAE,cAAc,EAAE,YACvB,QAAO;AAER,MACC,EAAE,WAAW,SAAS,EAAE,WAAW,QACnC,EAAE,WAAW,UAAU,EAAE,WAAW,SACpC,EAAE,WAAW,UAAU,EAAE,WAAW,SACpC,EAAE,WAAW,eAAe,EAAE,WAAW,cACzC,EAAE,WAAW,YAAY,EAAE,WAAW,QAEtC,QAAO;;AAGT,KAAI,EAAE,cAAc,WAAW,EAAE,cAAc,OAC9C,QAAO;AAER,MAAK,IAAI,QAAQ,GAAG,QAAQ,EAAE,cAAc,QAAQ,SAAS,EAC5D,KAAI,EAAE,cAAc,WAAW,EAAE,cAAc,OAC9C,QAAO;AAGT,QAAO;;;;;;;;AASR,SAAgB,wBAA+C;CAC9D,MAAM,EAAE,WAAW,YAAY;AAE/B,QAAO,iBACN,QAAQ,sBAAsB,OAC7B,UAAqC;AACrC,MAAI,CAAC,MACJ,QAAO;AAER,SAAO;GACN,eAAe,MAAM,IACnB,KAAK,OAAO,MAAM,KAAK,IAAI,CAC3B,QACC,iBACA,iBAAiB,OAClB;GACF,YAAY,MAAM;GAClB;IAEF,mBACA;;;;;;AAOF,SAAgB,oBACf,gBACsB;CACtB,MAAM,EAAE,WAAW,YAAY;AAE/B,QAAO,iBACN,QAAQ,sBAAsB,OAC7B,UAAqC;AACrC,MAAI,EAAE,SAAS,gBACd,QAAO;AAER,SAAO,MAAM,KAAK,mBAAmB;GAEtC"}
|
|
@@ -7,8 +7,11 @@ type BasicStore<TState> = {
|
|
|
7
7
|
/**
|
|
8
8
|
* React hook that bridges Zustand-like stores with React components by
|
|
9
9
|
* memoizing selector results and resubscribing when dependencies change.
|
|
10
|
+
*
|
|
11
|
+
* Overloaded to support both nullable and non-nullable stores.
|
|
10
12
|
*/
|
|
11
13
|
declare function useStoreSelector<TState, TSelected>(store: BasicStore<TState>, selector: (state: TState) => TSelected, isEqual?: (previous: TSelected, next: TSelected) => boolean): TSelected;
|
|
14
|
+
declare function useStoreSelector<TState, TSelected>(store: BasicStore<TState> | null, selector: (state: TState | null) => TSelected, isEqual?: (previous: TSelected, next: TSelected) => boolean): TSelected;
|
|
12
15
|
//#endregion
|
|
13
16
|
export { useStoreSelector };
|
|
14
17
|
//# sourceMappingURL=use-store-selector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-store-selector.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-store-selector.ts"],"sourcesContent":[],"mappings":";KAEK,+BAA+B;KAE/B,UAFA,CAAA,MAAY,CAAA,GAAA;EAEZ,QAAA,EAAA,EACQ,MADE;EACF,SAAA,CAAA,QAAA,EACQ,YADR,CACqB,MADrB,CAAA,CAAA,EAAA,GAAA,GAAA,IAAA;CACqB;;;
|
|
1
|
+
{"version":3,"file":"use-store-selector.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-store-selector.ts"],"sourcesContent":[],"mappings":";KAEK,+BAA+B;KAE/B,UAFA,CAAA,MAAY,CAAA,GAAA;EAEZ,QAAA,EAAA,EACQ,MADE;EACF,SAAA,CAAA,QAAA,EACQ,YADR,CACqB,MADrB,CAAA,CAAA,EAAA,GAAA,GAAA,IAAA;CACqB;;;AAYlC;;;;AAE8B,iBAFd,gBAEc,CAAA,MAAA,EAAA,SAAA,CAAA,CAAA,KAAA,EADtB,UACsB,CADX,MACW,CAAA,EAAA,QAAA,EAAA,CAAA,KAAA,EAAX,MAAW,EAAA,GAAA,SAAA,EAAA,OAAA,CAAA,EAAA,CAAA,QAAA,EACR,SADQ,EAAA,IAAA,EACS,SADT,EAAA,GAAA,OAAA,CAAA,EAE3B,SAF2B;AACR,iBAGN,gBAHM,CAAA,MAAA,EAAA,SAAA,CAAA,CAAA,KAAA,EAId,UAJc,CAIH,MAJG,CAAA,GAAA,IAAA,EAAA,QAAA,EAAA,CAAA,KAAA,EAKH,MALG,GAAA,IAAA,EAAA,GAKe,SALf,EAAA,OAAA,CAAA,EAAA,CAAA,QAAA,EAMA,SANA,EAAA,IAAA,EAMiB,SANjB,EAAA,GAAA,OAAA,CAAA,EAOnB,SAPmB"}
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import { useRef, useSyncExternalStore } from "react";
|
|
2
2
|
|
|
3
3
|
//#region src/hooks/private/store/use-store-selector.ts
|
|
4
|
-
|
|
5
|
-
* React hook that bridges Zustand-like stores with React components by
|
|
6
|
-
* memoizing selector results and resubscribing when dependencies change.
|
|
7
|
-
*/
|
|
4
|
+
const noopSubscribe = () => () => {};
|
|
8
5
|
function useStoreSelector(store, selector, isEqual = Object.is) {
|
|
9
6
|
const selectionRef = useRef(void 0);
|
|
10
|
-
const subscribe = (onStoreChange) => store.subscribe(() =>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const selected = selector(useSyncExternalStore(subscribe, () => store.getState(), () => store.getState()));
|
|
7
|
+
const subscribe = store ? (onStoreChange) => store.subscribe(() => onStoreChange()) : noopSubscribe;
|
|
8
|
+
const getSnapshot = store ? () => store.getState() : () => null;
|
|
9
|
+
const selected = selector(useSyncExternalStore(subscribe, getSnapshot, getSnapshot));
|
|
14
10
|
if (selectionRef.current === void 0 || !isEqual(selectionRef.current, selected)) selectionRef.current = selected;
|
|
15
11
|
return selectionRef.current;
|
|
16
12
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-store-selector.js","names":[],"sources":["../../../../src/hooks/private/store/use-store-selector.ts"],"sourcesContent":["import { useRef, useSyncExternalStore } from \"react\";\n\ntype Subscription<TState> = (state: TState) => void;\n\ntype BasicStore<TState> = {\n\tgetState(): TState;\n\tsubscribe(listener: Subscription<TState>): () => void;\n};\n\n/**\n * React hook that bridges Zustand-like stores with React components by\n * memoizing selector results and resubscribing when dependencies change.\n */\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState>,\n\tselector: (state: TState) => TSelected,\n\tisEqual: (previous: TSelected, next: TSelected) => boolean = Object.is\n): TSelected {\n\tconst selectionRef = useRef<TSelected>(undefined);\n\n\tconst subscribe = (onStoreChange: () => void)
|
|
1
|
+
{"version":3,"file":"use-store-selector.js","names":[],"sources":["../../../../src/hooks/private/store/use-store-selector.ts"],"sourcesContent":["import { useRef, useSyncExternalStore } from \"react\";\n\ntype Subscription<TState> = (state: TState) => void;\n\ntype BasicStore<TState> = {\n\tgetState(): TState;\n\tsubscribe(listener: Subscription<TState>): () => void;\n};\n\n// No-op subscribe function for null store case\nconst noopSubscribe = () => () => {};\n\n/**\n * React hook that bridges Zustand-like stores with React components by\n * memoizing selector results and resubscribing when dependencies change.\n *\n * Overloaded to support both nullable and non-nullable stores.\n */\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState>,\n\tselector: (state: TState) => TSelected,\n\tisEqual?: (previous: TSelected, next: TSelected) => boolean\n): TSelected;\n\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState> | null,\n\tselector: (state: TState | null) => TSelected,\n\tisEqual?: (previous: TSelected, next: TSelected) => boolean\n): TSelected;\n\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState> | null,\n\tselector: (state: TState | null) => TSelected,\n\tisEqual: (previous: TSelected, next: TSelected) => boolean = Object.is\n): TSelected {\n\tconst selectionRef = useRef<TSelected>(undefined);\n\n\t// Create stable subscribe function\n\tconst subscribe = store\n\t\t? (onStoreChange: () => void) => store.subscribe(() => onStoreChange())\n\t\t: noopSubscribe;\n\n\t// Create stable getSnapshot function\n\tconst getSnapshot = store ? () => store.getState() : () => null;\n\n\t// Always call useSyncExternalStore unconditionally\n\tconst snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n\tconst selected = selector(snapshot);\n\n\tif (\n\t\tselectionRef.current === undefined ||\n\t\t!isEqual(selectionRef.current, selected)\n\t) {\n\t\tselectionRef.current = selected;\n\t}\n\n\treturn selectionRef.current as TSelected;\n}\n"],"mappings":";;;AAUA,MAAM,4BAA4B;AAoBlC,SAAgB,iBACf,OACA,UACA,UAA6D,OAAO,IACxD;CACZ,MAAM,eAAe,OAAkB,OAAU;CAGjD,MAAM,YAAY,SACd,kBAA8B,MAAM,gBAAgB,eAAe,CAAC,GACrE;CAGH,MAAM,cAAc,cAAc,MAAM,UAAU,SAAS;CAK3D,MAAM,WAAW,SAFA,qBAAqB,WAAW,aAAa,YAAY,CAEvC;AAEnC,KACC,aAAa,YAAY,UACzB,CAAC,QAAQ,aAAa,SAAS,SAAS,CAExC,cAAa,UAAU;AAGxB,QAAO,aAAa"}
|
|
@@ -15,8 +15,10 @@ type UseWebsiteStoreOptions = {
|
|
|
15
15
|
/**
|
|
16
16
|
* Subscribes to the shared website store on the SDK client and exposes
|
|
17
17
|
* convenient loading/error state plus a manual refresh helper.
|
|
18
|
+
*
|
|
19
|
+
* When client is null (e.g., due to configuration error), returns an idle state.
|
|
18
20
|
*/
|
|
19
|
-
declare function useWebsiteStore(client: CossistantClient, options?: UseWebsiteStoreOptions): UseWebsiteStoreResult;
|
|
21
|
+
declare function useWebsiteStore(client: CossistantClient | null, options?: UseWebsiteStoreOptions): UseWebsiteStoreResult;
|
|
20
22
|
//#endregion
|
|
21
23
|
export { UseWebsiteStoreOptions, UseWebsiteStoreResult, useWebsiteStore };
|
|
22
24
|
//# sourceMappingURL=use-website-store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-website-store.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-website-store.ts"],"sourcesContent":[],"mappings":";;;KAgBY,qBAAA;WACF;EADE,MAAA,EAEH,YAFG,CAAA,QAAqB,CAAA;EACvB,SAAA,EAAA,OAAA;EACD,KAAA,EAED,KAFC,GAAA,IAAA;EAED,OAAA,EAAA,GAAA,GACQ,OADR,CACgB,YADhB,CAAA,SAAA,CAAA,GAAA,IAAA,CAAA;CACgB;AAAR,KAGJ,sBAAA,GAHI;EAAO,eAAA,CAAA,EAAA,MAAA,GAAA,KAAA;EAGX,oBAAA,CAAA,EAAA,OAAsB;
|
|
1
|
+
{"version":3,"file":"use-website-store.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-website-store.ts"],"sourcesContent":[],"mappings":";;;KAgBY,qBAAA;WACF;EADE,MAAA,EAEH,YAFG,CAAA,QAAqB,CAAA;EACvB,SAAA,EAAA,OAAA;EACD,KAAA,EAED,KAFC,GAAA,IAAA;EAED,OAAA,EAAA,GAAA,GACQ,OADR,CACgB,YADhB,CAAA,SAAA,CAAA,GAAA,IAAA,CAAA;CACgB;AAAR,KAGJ,sBAAA,GAHI;EAAO,eAAA,CAAA,EAAA,MAAA,GAAA,KAAA;EAGX,oBAAA,CAAA,EAAA,OAAsB;AAuBlC,CAAA;;;;;;;iBAAgB,eAAA,SACP,mCACC,yBACP"}
|
|
@@ -3,6 +3,11 @@ import { useStoreSelector } from "./use-store-selector.js";
|
|
|
3
3
|
import { useMemo } from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/hooks/private/store/use-website-store.ts
|
|
6
|
+
const EMPTY_STATE = {
|
|
7
|
+
website: null,
|
|
8
|
+
status: "idle",
|
|
9
|
+
error: null
|
|
10
|
+
};
|
|
6
11
|
function toError(state, fallback) {
|
|
7
12
|
if (fallback) return fallback;
|
|
8
13
|
if (!state.error) return null;
|
|
@@ -11,24 +16,27 @@ function toError(state, fallback) {
|
|
|
11
16
|
/**
|
|
12
17
|
* Subscribes to the shared website store on the SDK client and exposes
|
|
13
18
|
* convenient loading/error state plus a manual refresh helper.
|
|
19
|
+
*
|
|
20
|
+
* When client is null (e.g., due to configuration error), returns an idle state.
|
|
14
21
|
*/
|
|
15
22
|
function useWebsiteStore(client, options = {}) {
|
|
16
|
-
const state = useStoreSelector(client
|
|
17
|
-
throw new Error("Website store is not available on the client instance");
|
|
18
|
-
})(), (current) => current);
|
|
23
|
+
const state = useStoreSelector(client?.websiteStore ?? null, (current) => current ? current : EMPTY_STATE);
|
|
19
24
|
const query = useClientQuery({
|
|
20
25
|
client,
|
|
21
26
|
queryKey: "website",
|
|
22
27
|
queryFn: (instance, params) => instance.fetchWebsite(params ?? {}),
|
|
23
|
-
enabled:
|
|
28
|
+
enabled: client !== null,
|
|
24
29
|
refetchInterval: options.refetchInterval ?? false,
|
|
25
30
|
refetchOnWindowFocus: options.refetchOnWindowFocus ?? false,
|
|
26
31
|
refetchOnMount: state.status === "idle",
|
|
27
32
|
initialData: state.website ?? void 0
|
|
28
33
|
});
|
|
29
34
|
const error = useMemo(() => toError(state, query.error), [state, query.error]);
|
|
30
|
-
const isLoading = query.isLoading || state.status === "loading" || state.status === "idle";
|
|
31
|
-
const refresh = () =>
|
|
35
|
+
const isLoading = client !== null && (query.isLoading || state.status === "loading" || state.status === "idle");
|
|
36
|
+
const refresh = () => {
|
|
37
|
+
if (!client) return Promise.resolve(null);
|
|
38
|
+
return query.refetch({ force: true }).then((result) => result ?? client.websiteStore.getState().website).catch(() => client.websiteStore.getState().website).then((website) => website ?? null);
|
|
39
|
+
};
|
|
32
40
|
return {
|
|
33
41
|
website: state.website,
|
|
34
42
|
status: state.status,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-website-store.js","names":[],"sources":["../../../../src/hooks/private/store/use-website-store.ts"],"sourcesContent":["import type {\n\tCossistantClient,\n\tWebsiteState,\n\tWebsiteStore,\n} from \"@cossistant/core\";\nimport type { PublicWebsiteResponse } from \"@cossistant/types\";\nimport { useMemo } from \"react\";\nimport { useClientQuery } from \"../use-client-query\";\nimport { useStoreSelector } from \"./use-store-selector\";\n\nconst EMPTY_STATE: WebsiteState = {\n\twebsite: null,\n\tstatus: \"idle\",\n\terror: null,\n};\n\nexport type UseWebsiteStoreResult = {\n\twebsite: WebsiteState[\"website\"];\n\tstatus: WebsiteState[\"status\"];\n\tisLoading: boolean;\n\terror: Error | null;\n\trefresh: () => Promise<WebsiteState[\"website\"] | null>;\n};\n\nexport type UseWebsiteStoreOptions = {\n\trefetchInterval?: number | false;\n\trefetchOnWindowFocus?: boolean;\n};\n\nfunction toError(state: WebsiteState, fallback: Error | null): Error | null {\n\tif (fallback) {\n\t\treturn fallback;\n\t}\n\n\tif (!state.error) {\n\t\treturn null;\n\t}\n\n\treturn new Error(state.error.message);\n}\n\n/**\n * Subscribes to the shared website store on the SDK client and exposes\n * convenient loading/error state plus a manual refresh helper.\n */\nexport function useWebsiteStore(\n\tclient: CossistantClient,\n\toptions: UseWebsiteStoreOptions = {}\n): UseWebsiteStoreResult {\n\
|
|
1
|
+
{"version":3,"file":"use-website-store.js","names":["EMPTY_STATE: WebsiteState"],"sources":["../../../../src/hooks/private/store/use-website-store.ts"],"sourcesContent":["import type {\n\tCossistantClient,\n\tWebsiteState,\n\tWebsiteStore,\n} from \"@cossistant/core\";\nimport type { PublicWebsiteResponse } from \"@cossistant/types\";\nimport { useMemo } from \"react\";\nimport { useClientQuery } from \"../use-client-query\";\nimport { useStoreSelector } from \"./use-store-selector\";\n\nconst EMPTY_STATE: WebsiteState = {\n\twebsite: null,\n\tstatus: \"idle\",\n\terror: null,\n};\n\nexport type UseWebsiteStoreResult = {\n\twebsite: WebsiteState[\"website\"];\n\tstatus: WebsiteState[\"status\"];\n\tisLoading: boolean;\n\terror: Error | null;\n\trefresh: () => Promise<WebsiteState[\"website\"] | null>;\n};\n\nexport type UseWebsiteStoreOptions = {\n\trefetchInterval?: number | false;\n\trefetchOnWindowFocus?: boolean;\n};\n\nfunction toError(state: WebsiteState, fallback: Error | null): Error | null {\n\tif (fallback) {\n\t\treturn fallback;\n\t}\n\n\tif (!state.error) {\n\t\treturn null;\n\t}\n\n\treturn new Error(state.error.message);\n}\n\n/**\n * Subscribes to the shared website store on the SDK client and exposes\n * convenient loading/error state plus a manual refresh helper.\n *\n * When client is null (e.g., due to configuration error), returns an idle state.\n */\nexport function useWebsiteStore(\n\tclient: CossistantClient | null,\n\toptions: UseWebsiteStoreOptions = {}\n): UseWebsiteStoreResult {\n\t// Handle null client (configuration error case)\n\tconst store = client?.websiteStore ?? null;\n\n\tconst state = useStoreSelector(store, (current) =>\n\t\tcurrent ? current : EMPTY_STATE\n\t);\n\n\tconst query = useClientQuery<PublicWebsiteResponse, { force?: boolean }>({\n\t\tclient,\n\t\tqueryKey: \"website\",\n\t\tqueryFn: (instance, params) => instance.fetchWebsite(params ?? {}),\n\t\tenabled: client !== null,\n\t\trefetchInterval: options.refetchInterval ?? false,\n\t\trefetchOnWindowFocus: options.refetchOnWindowFocus ?? false,\n\t\trefetchOnMount: state.status === \"idle\",\n\t\tinitialData: state.website ?? undefined,\n\t});\n\n\tconst error = useMemo(\n\t\t() => toError(state, query.error),\n\t\t[state, query.error]\n\t);\n\tconst isLoading =\n\t\tclient !== null &&\n\t\t(query.isLoading || state.status === \"loading\" || state.status === \"idle\");\n\n\tconst refresh = () => {\n\t\tif (!client) {\n\t\t\treturn Promise.resolve(null);\n\t\t}\n\t\treturn query\n\t\t\t.refetch({ force: true })\n\t\t\t.then((result) => result ?? client.websiteStore.getState().website)\n\t\t\t.catch(() => client.websiteStore.getState().website)\n\t\t\t.then((website) => website ?? null);\n\t};\n\n\treturn {\n\t\twebsite: state.website,\n\t\tstatus: state.status,\n\t\tisLoading,\n\t\terror,\n\t\trefresh,\n\t};\n}\n"],"mappings":";;;;;AAUA,MAAMA,cAA4B;CACjC,SAAS;CACT,QAAQ;CACR,OAAO;CACP;AAeD,SAAS,QAAQ,OAAqB,UAAsC;AAC3E,KAAI,SACH,QAAO;AAGR,KAAI,CAAC,MAAM,MACV,QAAO;AAGR,QAAO,IAAI,MAAM,MAAM,MAAM,QAAQ;;;;;;;;AAStC,SAAgB,gBACf,QACA,UAAkC,EAAE,EACZ;CAIxB,MAAM,QAAQ,iBAFA,QAAQ,gBAAgB,OAEC,YACtC,UAAU,UAAU,YACpB;CAED,MAAM,QAAQ,eAA2D;EACxE;EACA,UAAU;EACV,UAAU,UAAU,WAAW,SAAS,aAAa,UAAU,EAAE,CAAC;EAClE,SAAS,WAAW;EACpB,iBAAiB,QAAQ,mBAAmB;EAC5C,sBAAsB,QAAQ,wBAAwB;EACtD,gBAAgB,MAAM,WAAW;EACjC,aAAa,MAAM,WAAW;EAC9B,CAAC;CAEF,MAAM,QAAQ,cACP,QAAQ,OAAO,MAAM,MAAM,EACjC,CAAC,OAAO,MAAM,MAAM,CACpB;CACD,MAAM,YACL,WAAW,SACV,MAAM,aAAa,MAAM,WAAW,aAAa,MAAM,WAAW;CAEpE,MAAM,gBAAgB;AACrB,MAAI,CAAC,OACJ,QAAO,QAAQ,QAAQ,KAAK;AAE7B,SAAO,MACL,QAAQ,EAAE,OAAO,MAAM,CAAC,CACxB,MAAM,WAAW,UAAU,OAAO,aAAa,UAAU,CAAC,QAAQ,CAClE,YAAY,OAAO,aAAa,UAAU,CAAC,QAAQ,CACnD,MAAM,YAAY,WAAW,KAAK;;AAGrC,QAAO;EACN,SAAS,MAAM;EACf,QAAQ,MAAM;EACd;EACA;EACA;EACA"}
|
|
@@ -3,7 +3,7 @@ import { CossistantClient } from "@cossistant/core";
|
|
|
3
3
|
//#region src/hooks/private/use-client-query.d.ts
|
|
4
4
|
type QueryFn<TData, TArgs> = (client: CossistantClient, args?: TArgs | undefined) => Promise<TData>;
|
|
5
5
|
type UseClientQueryOptions<TData, TArgs> = {
|
|
6
|
-
client: CossistantClient;
|
|
6
|
+
client: CossistantClient | null;
|
|
7
7
|
queryFn: QueryFn<TData, TArgs>;
|
|
8
8
|
/**
|
|
9
9
|
* Unique key to identify this query for deduplication.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-client-query.d.ts","names":[],"sources":["../../../src/hooks/private/use-client-query.ts"],"sourcesContent":[],"mappings":";;;KAGK,iCACI,yBACD,sBACH,QAAQ;KAER;EALA,MAAA,EAMI,gBANG;EACH,OAAA,EAMC,OAND,CAMS,KANT,EAMgB,KANhB,CAAA;EACD;;;;AACI;EAGH,QAAA,CAAA,EAAA,MAAA;EACS,OAAA,CAAA,EAAA,OAAA;EAAO,eAAA,CAAA,EAAA,MAAA,GAAA,KAAA;EAAf,oBAAA,CAAA,EAAA,OAAA;EAWK,cAAA,CAAA,EAAA,OAAA;EACA,WAAA,CAAA,EADA,KACA;EAAK,WAAA,CAAA,EAAL,KAAK;EAIf,YAAA,CAAA,EAAA,SAAoB,OAAA,EAAA;CAClB;KADF,oBAEG,CAAA,KAAA,EAAA,KAAA,CAAA,GAAA;EAEU,IAAA,EAHX,KAGW,GAAA,SAAA;EAAkB,KAAA,EAF5B,KAE4B,GAAA,IAAA;EAAR,SAAA,EAAA,OAAA;EAAO,OAAA,EAAA,CAAA,IAAA,CAAA,EAAjB,KAAiB,EAAA,GAAP,OAAO,CAAC,KAAD,GAAA,SAAA,CAAA;AAqDnC,CAAA;;;;;;AAEG,iBAFa,cAEb,CAAA,KAAA,EAAA,QAAA,IAAA,CAAA,CAAA,OAAA,EADO,qBACP,CAD6B,KAC7B,EADoC,KACpC,CAAA,CAAA,EAAA,oBAAA,CAAqB,KAArB,EAA4B,KAA5B,CAAA"}
|
|
1
|
+
{"version":3,"file":"use-client-query.d.ts","names":[],"sources":["../../../src/hooks/private/use-client-query.ts"],"sourcesContent":[],"mappings":";;;KAGK,iCACI,yBACD,sBACH,QAAQ;KAER;EALA,MAAA,EAMI,gBANG,GAAA,IAAA;EACH,OAAA,EAMC,OAND,CAMS,KANT,EAMgB,KANhB,CAAA;EACD;;;;AACI;EAGH,QAAA,CAAA,EAAA,MAAA;EACS,OAAA,CAAA,EAAA,OAAA;EAAO,eAAA,CAAA,EAAA,MAAA,GAAA,KAAA;EAAf,oBAAA,CAAA,EAAA,OAAA;EAWK,cAAA,CAAA,EAAA,OAAA;EACA,WAAA,CAAA,EADA,KACA;EAAK,WAAA,CAAA,EAAL,KAAK;EAIf,YAAA,CAAA,EAAA,SAAoB,OAAA,EAAA;CAClB;KADF,oBAEG,CAAA,KAAA,EAAA,KAAA,CAAA,GAAA;EAEU,IAAA,EAHX,KAGW,GAAA,SAAA;EAAkB,KAAA,EAF5B,KAE4B,GAAA,IAAA;EAAR,SAAA,EAAA,OAAA;EAAO,OAAA,EAAA,CAAA,IAAA,CAAA,EAAjB,KAAiB,EAAA,GAAP,OAAO,CAAC,KAAD,GAAA,SAAA,CAAA;AAqDnC,CAAA;;;;;;AAEG,iBAFa,cAEb,CAAA,KAAA,EAAA,QAAA,IAAA,CAAA,CAAA,OAAA,EADO,qBACP,CAD6B,KAC7B,EADoC,KACpC,CAAA,CAAA,EAAA,oBAAA,CAAqB,KAArB,EAA4B,KAA5B,CAAA"}
|
|
@@ -51,6 +51,7 @@ function useClientQuery(options) {
|
|
|
51
51
|
argsRef.current = initialArgs;
|
|
52
52
|
}, [initialArgs]);
|
|
53
53
|
const execute = useCallback(async (args, ignoreEnabled = false) => {
|
|
54
|
+
if (!client) return dataRef.current;
|
|
54
55
|
if (!(enabled || ignoreEnabled)) return dataRef.current;
|
|
55
56
|
const nextArgs = args ?? argsRef.current;
|
|
56
57
|
argsRef.current = nextArgs;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-client-query.js","names":["EMPTY_DEPENDENCIES: readonly unknown[]","raw: unknown"],"sources":["../../../src/hooks/private/use-client-query.ts"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\ntype QueryFn<TData, TArgs> = (\n\tclient: CossistantClient,\n\targs?: TArgs | undefined\n) => Promise<TData>;\n\ntype UseClientQueryOptions<TData, TArgs> = {\n\tclient: CossistantClient;\n\tqueryFn: QueryFn<TData, TArgs>;\n\t/**\n\t * Unique key to identify this query for deduplication.\n\t * When provided, concurrent requests with the same key will share a single\n\t * in-flight promise instead of making duplicate API calls.\n\t */\n\tqueryKey?: string;\n\tenabled?: boolean;\n\trefetchInterval?: number | false;\n\trefetchOnWindowFocus?: boolean;\n\trefetchOnMount?: boolean;\n\tinitialData?: TData;\n\tinitialArgs?: TArgs;\n\tdependencies?: readonly unknown[];\n};\n\ntype UseClientQueryResult<TData, TArgs> = {\n\tdata: TData | undefined;\n\terror: Error | null;\n\tisLoading: boolean;\n\trefetch: (args?: TArgs) => Promise<TData | undefined>;\n};\n\nfunction toError(error: unknown): Error {\n\tif (error instanceof Error) {\n\t\treturn error;\n\t}\n\n\treturn new Error(typeof error === \"string\" ? error : \"Unknown error\");\n}\n\nconst EMPTY_DEPENDENCIES: readonly unknown[] = [];\n\n/**\n * Module-level cache for in-flight requests.\n * Maps query keys to their pending promises for deduplication.\n */\nconst inFlightRequests = new Map<string, Promise<unknown>>();\n\n/**\n * Execute a query with deduplication support.\n * If a query with the same key is already in flight, returns the existing promise.\n */\nfunction executeWithDeduplication<TData>(\n\tqueryKey: string | undefined,\n\tqueryFn: () => Promise<TData>\n): Promise<TData> {\n\t// No deduplication if no key provided\n\tif (!queryKey) {\n\t\treturn queryFn();\n\t}\n\n\t// Check for existing in-flight request\n\tconst existing = inFlightRequests.get(queryKey);\n\tif (existing) {\n\t\treturn existing as Promise<TData>;\n\t}\n\n\t// Create new request and track it\n\tconst promise = queryFn().finally(() => {\n\t\t// Clean up after request completes (success or error)\n\t\tinFlightRequests.delete(queryKey);\n\t});\n\n\tinFlightRequests.set(queryKey, promise);\n\treturn promise;\n}\n\n/**\n * Lightweight data-fetching abstraction that plugs into the SDK client instead\n * of React Query. It tracks loading/error state, supports polling, window\n * focus refetching and exposes a typed refetch helper.\n */\nexport function useClientQuery<TData, TArgs = void>(\n\toptions: UseClientQueryOptions<TData, TArgs>\n): UseClientQueryResult<TData, TArgs> {\n\tconst {\n\t\tclient,\n\t\tqueryFn,\n\t\tqueryKey,\n\t\tenabled = true,\n\t\trefetchInterval = false,\n\t\trefetchOnWindowFocus = false,\n\t\trefetchOnMount = true,\n\t\tinitialData,\n\t\tinitialArgs,\n\t\tdependencies = EMPTY_DEPENDENCIES,\n\t} = options;\n\n\tconst [data, setData] = useState<TData | undefined>(initialData);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [isLoading, setIsLoading] = useState(\n\t\tinitialData === undefined && Boolean(enabled)\n\t);\n\n\tconst dataRef = useRef(data);\n\tdataRef.current = data;\n\n\tconst argsRef = useRef<TArgs | undefined>(initialArgs);\n\tconst fetchIdRef = useRef(0);\n\tconst hasMountedRef = useRef(false);\n\tconst hasFetchedRef = useRef(initialData !== undefined);\n\tconst isMountedRef = useRef(true);\n\tconst queryFnRef = useRef(queryFn);\n\n\tqueryFnRef.current = queryFn;\n\n\tuseEffect(\n\t\t() => () => {\n\t\t\tisMountedRef.current = false;\n\t\t},\n\t\t[]\n\t);\n\n\tuseEffect(() => {\n\t\targsRef.current = initialArgs;\n\t}, [initialArgs]);\n\n\tconst execute = useCallback(\n\t\tasync (args?: TArgs, ignoreEnabled = false): Promise<TData | undefined> => {\n\t\t\tif (!(enabled || ignoreEnabled)) {\n\t\t\t\treturn dataRef.current;\n\t\t\t}\n\n\t\t\tconst nextArgs = args ?? argsRef.current;\n\t\t\targsRef.current = nextArgs;\n\n\t\t\tconst fetchId = fetchIdRef.current + 1;\n\t\t\tfetchIdRef.current = fetchId;\n\n\t\t\tsetIsLoading(true);\n\t\t\tsetError(null);\n\n\t\t\ttry {\n\t\t\t\t// Use deduplication to share in-flight requests with the same key\n\t\t\t\tconst result = await executeWithDeduplication(queryKey, () =>\n\t\t\t\t\tqueryFnRef.current(client, nextArgs)\n\t\t\t\t);\n\n\t\t\t\tif (!isMountedRef.current || fetchId !== fetchIdRef.current) {\n\t\t\t\t\treturn dataRef.current;\n\t\t\t\t}\n\n\t\t\t\tdataRef.current = result;\n\t\t\t\tsetData(result);\n\t\t\t\tsetError(null);\n\t\t\t\tsetIsLoading(false);\n\t\t\t\thasFetchedRef.current = true;\n\n\t\t\t\treturn result;\n\t\t\t} catch (raw: unknown) {\n\t\t\t\tif (!isMountedRef.current || fetchId !== fetchIdRef.current) {\n\t\t\t\t\treturn dataRef.current;\n\t\t\t\t}\n\n\t\t\t\tconst normalized = toError(raw);\n\t\t\t\tsetError(normalized);\n\t\t\t\tsetIsLoading(false);\n\n\t\t\t\tthrow normalized;\n\t\t\t}\n\t\t},\n\t\t[client, enabled, queryKey]\n\t);\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\tsetIsLoading(false);\n\t\t\treturn;\n\t\t}\n\n\t\tconst shouldFetchInitially = hasMountedRef.current\n\t\t\t? true\n\t\t\t: refetchOnMount || !hasFetchedRef.current;\n\n\t\thasMountedRef.current = true;\n\n\t\tif (!shouldFetchInitially) {\n\t\t\treturn;\n\t\t}\n\n\t\tvoid execute(argsRef.current);\n\t}, [enabled, execute, refetchOnMount, ...dependencies]);\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\trefetchInterval === false ||\n\t\t\trefetchInterval === null ||\n\t\t\trefetchInterval <= 0 ||\n\t\t\ttypeof window === \"undefined\"\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst timer = window.setInterval(() => {\n\t\t\tvoid execute(argsRef.current);\n\t\t}, refetchInterval);\n\n\t\treturn () => {\n\t\t\twindow.clearInterval(timer);\n\t\t};\n\t}, [enabled, execute, refetchInterval]);\n\n\tuseEffect(() => {\n\t\tif (\n\t\t\t!refetchOnWindowFocus ||\n\t\t\ttypeof window === \"undefined\" ||\n\t\t\ttypeof document === \"undefined\"\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst handleRefetch = () => {\n\t\t\tif (!enabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvoid execute(argsRef.current);\n\t\t};\n\n\t\tconst onFocus = () => {\n\t\t\tvoid handleRefetch();\n\t\t};\n\n\t\tconst onVisibilityChange = () => {\n\t\t\tif (document.visibilityState === \"visible\") {\n\t\t\t\tvoid handleRefetch();\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"focus\", onFocus);\n\t\tdocument.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"focus\", onFocus);\n\t\t\tdocument.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\t\t};\n\t}, [enabled, execute, refetchOnWindowFocus]);\n\n\tconst refetch = useCallback(\n\t\tasync (args?: TArgs) => execute(args, true),\n\t\t[execute]\n\t);\n\n\treturn useMemo(\n\t\t() => ({\n\t\t\tdata,\n\t\t\terror,\n\t\t\tisLoading,\n\t\t\trefetch,\n\t\t}),\n\t\t[data, error, isLoading, refetch]\n\t);\n}\n"],"mappings":";;;AAiCA,SAAS,QAAQ,OAAuB;AACvC,KAAI,iBAAiB,MACpB,QAAO;AAGR,QAAO,IAAI,MAAM,OAAO,UAAU,WAAW,QAAQ,gBAAgB;;AAGtE,MAAMA,qBAAyC,EAAE;;;;;AAMjD,MAAM,mCAAmB,IAAI,KAA+B;;;;;AAM5D,SAAS,yBACR,UACA,SACiB;AAEjB,KAAI,CAAC,SACJ,QAAO,SAAS;CAIjB,MAAM,WAAW,iBAAiB,IAAI,SAAS;AAC/C,KAAI,SACH,QAAO;CAIR,MAAM,UAAU,SAAS,CAAC,cAAc;AAEvC,mBAAiB,OAAO,SAAS;GAChC;AAEF,kBAAiB,IAAI,UAAU,QAAQ;AACvC,QAAO;;;;;;;AAQR,SAAgB,eACf,SACqC;CACrC,MAAM,EACL,QACA,SACA,UACA,UAAU,MACV,kBAAkB,OAClB,uBAAuB,OACvB,iBAAiB,MACjB,aACA,aACA,eAAe,uBACZ;CAEJ,MAAM,CAAC,MAAM,WAAW,SAA4B,YAAY;CAChE,MAAM,CAAC,OAAO,YAAY,SAAuB,KAAK;CACtD,MAAM,CAAC,WAAW,gBAAgB,SACjC,gBAAgB,UAAa,QAAQ,QAAQ,CAC7C;CAED,MAAM,UAAU,OAAO,KAAK;AAC5B,SAAQ,UAAU;CAElB,MAAM,UAAU,OAA0B,YAAY;CACtD,MAAM,aAAa,OAAO,EAAE;CAC5B,MAAM,gBAAgB,OAAO,MAAM;CACnC,MAAM,gBAAgB,OAAO,gBAAgB,OAAU;CACvD,MAAM,eAAe,OAAO,KAAK;CACjC,MAAM,aAAa,OAAO,QAAQ;AAElC,YAAW,UAAU;AAErB,uBACa;AACX,eAAa,UAAU;IAExB,EAAE,CACF;AAED,iBAAgB;AACf,UAAQ,UAAU;IAChB,CAAC,YAAY,CAAC;CAEjB,MAAM,UAAU,YACf,OAAO,MAAc,gBAAgB,UAAsC;
|
|
1
|
+
{"version":3,"file":"use-client-query.js","names":["EMPTY_DEPENDENCIES: readonly unknown[]","raw: unknown"],"sources":["../../../src/hooks/private/use-client-query.ts"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\ntype QueryFn<TData, TArgs> = (\n\tclient: CossistantClient,\n\targs?: TArgs | undefined\n) => Promise<TData>;\n\ntype UseClientQueryOptions<TData, TArgs> = {\n\tclient: CossistantClient | null;\n\tqueryFn: QueryFn<TData, TArgs>;\n\t/**\n\t * Unique key to identify this query for deduplication.\n\t * When provided, concurrent requests with the same key will share a single\n\t * in-flight promise instead of making duplicate API calls.\n\t */\n\tqueryKey?: string;\n\tenabled?: boolean;\n\trefetchInterval?: number | false;\n\trefetchOnWindowFocus?: boolean;\n\trefetchOnMount?: boolean;\n\tinitialData?: TData;\n\tinitialArgs?: TArgs;\n\tdependencies?: readonly unknown[];\n};\n\ntype UseClientQueryResult<TData, TArgs> = {\n\tdata: TData | undefined;\n\terror: Error | null;\n\tisLoading: boolean;\n\trefetch: (args?: TArgs) => Promise<TData | undefined>;\n};\n\nfunction toError(error: unknown): Error {\n\tif (error instanceof Error) {\n\t\treturn error;\n\t}\n\n\treturn new Error(typeof error === \"string\" ? error : \"Unknown error\");\n}\n\nconst EMPTY_DEPENDENCIES: readonly unknown[] = [];\n\n/**\n * Module-level cache for in-flight requests.\n * Maps query keys to their pending promises for deduplication.\n */\nconst inFlightRequests = new Map<string, Promise<unknown>>();\n\n/**\n * Execute a query with deduplication support.\n * If a query with the same key is already in flight, returns the existing promise.\n */\nfunction executeWithDeduplication<TData>(\n\tqueryKey: string | undefined,\n\tqueryFn: () => Promise<TData>\n): Promise<TData> {\n\t// No deduplication if no key provided\n\tif (!queryKey) {\n\t\treturn queryFn();\n\t}\n\n\t// Check for existing in-flight request\n\tconst existing = inFlightRequests.get(queryKey);\n\tif (existing) {\n\t\treturn existing as Promise<TData>;\n\t}\n\n\t// Create new request and track it\n\tconst promise = queryFn().finally(() => {\n\t\t// Clean up after request completes (success or error)\n\t\tinFlightRequests.delete(queryKey);\n\t});\n\n\tinFlightRequests.set(queryKey, promise);\n\treturn promise;\n}\n\n/**\n * Lightweight data-fetching abstraction that plugs into the SDK client instead\n * of React Query. It tracks loading/error state, supports polling, window\n * focus refetching and exposes a typed refetch helper.\n */\nexport function useClientQuery<TData, TArgs = void>(\n\toptions: UseClientQueryOptions<TData, TArgs>\n): UseClientQueryResult<TData, TArgs> {\n\tconst {\n\t\tclient,\n\t\tqueryFn,\n\t\tqueryKey,\n\t\tenabled = true,\n\t\trefetchInterval = false,\n\t\trefetchOnWindowFocus = false,\n\t\trefetchOnMount = true,\n\t\tinitialData,\n\t\tinitialArgs,\n\t\tdependencies = EMPTY_DEPENDENCIES,\n\t} = options;\n\n\tconst [data, setData] = useState<TData | undefined>(initialData);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [isLoading, setIsLoading] = useState(\n\t\tinitialData === undefined && Boolean(enabled)\n\t);\n\n\tconst dataRef = useRef(data);\n\tdataRef.current = data;\n\n\tconst argsRef = useRef<TArgs | undefined>(initialArgs);\n\tconst fetchIdRef = useRef(0);\n\tconst hasMountedRef = useRef(false);\n\tconst hasFetchedRef = useRef(initialData !== undefined);\n\tconst isMountedRef = useRef(true);\n\tconst queryFnRef = useRef(queryFn);\n\n\tqueryFnRef.current = queryFn;\n\n\tuseEffect(\n\t\t() => () => {\n\t\t\tisMountedRef.current = false;\n\t\t},\n\t\t[]\n\t);\n\n\tuseEffect(() => {\n\t\targsRef.current = initialArgs;\n\t}, [initialArgs]);\n\n\tconst execute = useCallback(\n\t\tasync (args?: TArgs, ignoreEnabled = false): Promise<TData | undefined> => {\n\t\t\t// Handle null client (configuration error case)\n\t\t\tif (!client) {\n\t\t\t\treturn dataRef.current;\n\t\t\t}\n\n\t\t\tif (!(enabled || ignoreEnabled)) {\n\t\t\t\treturn dataRef.current;\n\t\t\t}\n\n\t\t\tconst nextArgs = args ?? argsRef.current;\n\t\t\targsRef.current = nextArgs;\n\n\t\t\tconst fetchId = fetchIdRef.current + 1;\n\t\t\tfetchIdRef.current = fetchId;\n\n\t\t\tsetIsLoading(true);\n\t\t\tsetError(null);\n\n\t\t\ttry {\n\t\t\t\t// Use deduplication to share in-flight requests with the same key\n\t\t\t\tconst result = await executeWithDeduplication(queryKey, () =>\n\t\t\t\t\tqueryFnRef.current(client, nextArgs)\n\t\t\t\t);\n\n\t\t\t\tif (!isMountedRef.current || fetchId !== fetchIdRef.current) {\n\t\t\t\t\treturn dataRef.current;\n\t\t\t\t}\n\n\t\t\t\tdataRef.current = result;\n\t\t\t\tsetData(result);\n\t\t\t\tsetError(null);\n\t\t\t\tsetIsLoading(false);\n\t\t\t\thasFetchedRef.current = true;\n\n\t\t\t\treturn result;\n\t\t\t} catch (raw: unknown) {\n\t\t\t\tif (!isMountedRef.current || fetchId !== fetchIdRef.current) {\n\t\t\t\t\treturn dataRef.current;\n\t\t\t\t}\n\n\t\t\t\tconst normalized = toError(raw);\n\t\t\t\tsetError(normalized);\n\t\t\t\tsetIsLoading(false);\n\n\t\t\t\tthrow normalized;\n\t\t\t}\n\t\t},\n\t\t[client, enabled, queryKey]\n\t);\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\tsetIsLoading(false);\n\t\t\treturn;\n\t\t}\n\n\t\tconst shouldFetchInitially = hasMountedRef.current\n\t\t\t? true\n\t\t\t: refetchOnMount || !hasFetchedRef.current;\n\n\t\thasMountedRef.current = true;\n\n\t\tif (!shouldFetchInitially) {\n\t\t\treturn;\n\t\t}\n\n\t\tvoid execute(argsRef.current);\n\t}, [enabled, execute, refetchOnMount, ...dependencies]);\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\trefetchInterval === false ||\n\t\t\trefetchInterval === null ||\n\t\t\trefetchInterval <= 0 ||\n\t\t\ttypeof window === \"undefined\"\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst timer = window.setInterval(() => {\n\t\t\tvoid execute(argsRef.current);\n\t\t}, refetchInterval);\n\n\t\treturn () => {\n\t\t\twindow.clearInterval(timer);\n\t\t};\n\t}, [enabled, execute, refetchInterval]);\n\n\tuseEffect(() => {\n\t\tif (\n\t\t\t!refetchOnWindowFocus ||\n\t\t\ttypeof window === \"undefined\" ||\n\t\t\ttypeof document === \"undefined\"\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst handleRefetch = () => {\n\t\t\tif (!enabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvoid execute(argsRef.current);\n\t\t};\n\n\t\tconst onFocus = () => {\n\t\t\tvoid handleRefetch();\n\t\t};\n\n\t\tconst onVisibilityChange = () => {\n\t\t\tif (document.visibilityState === \"visible\") {\n\t\t\t\tvoid handleRefetch();\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"focus\", onFocus);\n\t\tdocument.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"focus\", onFocus);\n\t\t\tdocument.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\t\t};\n\t}, [enabled, execute, refetchOnWindowFocus]);\n\n\tconst refetch = useCallback(\n\t\tasync (args?: TArgs) => execute(args, true),\n\t\t[execute]\n\t);\n\n\treturn useMemo(\n\t\t() => ({\n\t\t\tdata,\n\t\t\terror,\n\t\t\tisLoading,\n\t\t\trefetch,\n\t\t}),\n\t\t[data, error, isLoading, refetch]\n\t);\n}\n"],"mappings":";;;AAiCA,SAAS,QAAQ,OAAuB;AACvC,KAAI,iBAAiB,MACpB,QAAO;AAGR,QAAO,IAAI,MAAM,OAAO,UAAU,WAAW,QAAQ,gBAAgB;;AAGtE,MAAMA,qBAAyC,EAAE;;;;;AAMjD,MAAM,mCAAmB,IAAI,KAA+B;;;;;AAM5D,SAAS,yBACR,UACA,SACiB;AAEjB,KAAI,CAAC,SACJ,QAAO,SAAS;CAIjB,MAAM,WAAW,iBAAiB,IAAI,SAAS;AAC/C,KAAI,SACH,QAAO;CAIR,MAAM,UAAU,SAAS,CAAC,cAAc;AAEvC,mBAAiB,OAAO,SAAS;GAChC;AAEF,kBAAiB,IAAI,UAAU,QAAQ;AACvC,QAAO;;;;;;;AAQR,SAAgB,eACf,SACqC;CACrC,MAAM,EACL,QACA,SACA,UACA,UAAU,MACV,kBAAkB,OAClB,uBAAuB,OACvB,iBAAiB,MACjB,aACA,aACA,eAAe,uBACZ;CAEJ,MAAM,CAAC,MAAM,WAAW,SAA4B,YAAY;CAChE,MAAM,CAAC,OAAO,YAAY,SAAuB,KAAK;CACtD,MAAM,CAAC,WAAW,gBAAgB,SACjC,gBAAgB,UAAa,QAAQ,QAAQ,CAC7C;CAED,MAAM,UAAU,OAAO,KAAK;AAC5B,SAAQ,UAAU;CAElB,MAAM,UAAU,OAA0B,YAAY;CACtD,MAAM,aAAa,OAAO,EAAE;CAC5B,MAAM,gBAAgB,OAAO,MAAM;CACnC,MAAM,gBAAgB,OAAO,gBAAgB,OAAU;CACvD,MAAM,eAAe,OAAO,KAAK;CACjC,MAAM,aAAa,OAAO,QAAQ;AAElC,YAAW,UAAU;AAErB,uBACa;AACX,eAAa,UAAU;IAExB,EAAE,CACF;AAED,iBAAgB;AACf,UAAQ,UAAU;IAChB,CAAC,YAAY,CAAC;CAEjB,MAAM,UAAU,YACf,OAAO,MAAc,gBAAgB,UAAsC;AAE1E,MAAI,CAAC,OACJ,QAAO,QAAQ;AAGhB,MAAI,EAAE,WAAW,eAChB,QAAO,QAAQ;EAGhB,MAAM,WAAW,QAAQ,QAAQ;AACjC,UAAQ,UAAU;EAElB,MAAM,UAAU,WAAW,UAAU;AACrC,aAAW,UAAU;AAErB,eAAa,KAAK;AAClB,WAAS,KAAK;AAEd,MAAI;GAEH,MAAM,SAAS,MAAM,yBAAyB,gBAC7C,WAAW,QAAQ,QAAQ,SAAS,CACpC;AAED,OAAI,CAAC,aAAa,WAAW,YAAY,WAAW,QACnD,QAAO,QAAQ;AAGhB,WAAQ,UAAU;AAClB,WAAQ,OAAO;AACf,YAAS,KAAK;AACd,gBAAa,MAAM;AACnB,iBAAc,UAAU;AAExB,UAAO;WACCC,KAAc;AACtB,OAAI,CAAC,aAAa,WAAW,YAAY,WAAW,QACnD,QAAO,QAAQ;GAGhB,MAAM,aAAa,QAAQ,IAAI;AAC/B,YAAS,WAAW;AACpB,gBAAa,MAAM;AAEnB,SAAM;;IAGR;EAAC;EAAQ;EAAS;EAAS,CAC3B;AAED,iBAAgB;AACf,MAAI,CAAC,SAAS;AACb,gBAAa,MAAM;AACnB;;EAGD,MAAM,uBAAuB,cAAc,UACxC,OACA,kBAAkB,CAAC,cAAc;AAEpC,gBAAc,UAAU;AAExB,MAAI,CAAC,qBACJ;AAGD,EAAK,QAAQ,QAAQ,QAAQ;IAC3B;EAAC;EAAS;EAAS;EAAgB,GAAG;EAAa,CAAC;AAEvD,iBAAgB;AACf,MAAI,CAAC,QACJ;AAGD,MACC,oBAAoB,SACpB,oBAAoB,QACpB,mBAAmB,KACnB,OAAO,WAAW,YAElB;EAGD,MAAM,QAAQ,OAAO,kBAAkB;AACtC,GAAK,QAAQ,QAAQ,QAAQ;KAC3B,gBAAgB;AAEnB,eAAa;AACZ,UAAO,cAAc,MAAM;;IAE1B;EAAC;EAAS;EAAS;EAAgB,CAAC;AAEvC,iBAAgB;AACf,MACC,CAAC,wBACD,OAAO,WAAW,eAClB,OAAO,aAAa,YAEpB;EAGD,MAAM,sBAAsB;AAC3B,OAAI,CAAC,QACJ;AAGD,GAAK,QAAQ,QAAQ,QAAQ;;EAG9B,MAAM,gBAAgB;AACrB,GAAK,eAAe;;EAGrB,MAAM,2BAA2B;AAChC,OAAI,SAAS,oBAAoB,UAChC,CAAK,eAAe;;AAItB,SAAO,iBAAiB,SAAS,QAAQ;AACzC,WAAS,iBAAiB,oBAAoB,mBAAmB;AAEjE,eAAa;AACZ,UAAO,oBAAoB,SAAS,QAAQ;AAC5C,YAAS,oBAAoB,oBAAoB,mBAAmB;;IAEnE;EAAC;EAAS;EAAS;EAAqB,CAAC;CAE5C,MAAM,UAAU,YACf,OAAO,SAAiB,QAAQ,MAAM,KAAK,EAC3C,CAAC,QAAQ,CACT;AAED,QAAO,eACC;EACN;EACA;EACA;EACA;EACA,GACD;EAAC;EAAM;EAAO;EAAW;EAAQ,CACjC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { TimelineItem } from "../../timeline-item.js";
|
|
2
|
-
import { ConversationSeen } from "../../
|
|
1
|
+
import { TimelineItem } from "../../packages/types/src/api/timeline-item.js";
|
|
2
|
+
import { ConversationSeen } from "../../packages/types/src/schemas.js";
|
|
3
3
|
import { SenderType } from "@cossistant/types";
|
|
4
4
|
|
|
5
5
|
//#region src/hooks/private/use-grouped-messages.d.ts
|
|
@@ -24,12 +24,16 @@ type TimelineToolItem = {
|
|
|
24
24
|
tool: string | null;
|
|
25
25
|
timestamp: Date;
|
|
26
26
|
};
|
|
27
|
-
type
|
|
27
|
+
type DaySeparatorItem = {
|
|
28
|
+
type: "day_separator";
|
|
29
|
+
date: Date;
|
|
30
|
+
dateString: string;
|
|
31
|
+
};
|
|
32
|
+
type ConversationItem = GroupedMessage | TimelineEventItem | TimelineToolItem | DaySeparatorItem;
|
|
28
33
|
type UseGroupedMessagesOptions = {
|
|
29
34
|
items: TimelineItem[];
|
|
30
35
|
seenData?: ConversationSeen[];
|
|
31
36
|
currentViewerId?: string;
|
|
32
|
-
viewerType?: SenderType;
|
|
33
37
|
};
|
|
34
38
|
type UseGroupedMessagesProps = UseGroupedMessagesOptions;
|
|
35
39
|
/**
|
|
@@ -41,8 +45,7 @@ type UseGroupedMessagesProps = UseGroupedMessagesOptions;
|
|
|
41
45
|
declare const useGroupedMessages: ({
|
|
42
46
|
items,
|
|
43
47
|
seenData,
|
|
44
|
-
currentViewerId
|
|
45
|
-
viewerType
|
|
48
|
+
currentViewerId
|
|
46
49
|
}: UseGroupedMessagesOptions) => {
|
|
47
50
|
items: ConversationItem[];
|
|
48
51
|
seenByMap: Map<string, Set<string>>;
|
|
@@ -56,5 +59,5 @@ declare const useGroupedMessages: ({
|
|
|
56
59
|
hasUnreadAfter: (messageId: string, userId: string) => boolean;
|
|
57
60
|
};
|
|
58
61
|
//#endregion
|
|
59
|
-
export { ConversationItem, GroupedMessage, TimelineEventItem, TimelineToolItem, UseGroupedMessagesOptions, UseGroupedMessagesProps, useGroupedMessages };
|
|
62
|
+
export { ConversationItem, DaySeparatorItem, GroupedMessage, TimelineEventItem, TimelineToolItem, UseGroupedMessagesOptions, UseGroupedMessagesProps, useGroupedMessages };
|
|
60
63
|
//# sourceMappingURL=use-grouped-messages.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-grouped-messages.d.ts","names":[],"sources":["../../../src/hooks/private/use-grouped-messages.ts"],"sourcesContent":[],"mappings":";;;;;KAKY,cAAA;;EAAA,QAAA,EAAA,MAAA;EAGC,UAAA,EAAA,UAAA;EACL,KAAA,EAAA,YAAA,EAAA;EAGW,cAAA,EAAA,MAAA;EACD,aAAA,EAAA,MAAA;EAAI,gBAAA,EADH,IACG;EAGV,eAAA,EAHM,IAGW;AAM7B,CAAA;AAOY,KAbA,iBAAA,GAagB;
|
|
1
|
+
{"version":3,"file":"use-grouped-messages.d.ts","names":[],"sources":["../../../src/hooks/private/use-grouped-messages.ts"],"sourcesContent":[],"mappings":";;;;;KAKY,cAAA;;EAAA,QAAA,EAAA,MAAA;EAGC,UAAA,EAAA,UAAA;EACL,KAAA,EAAA,YAAA,EAAA;EAGW,cAAA,EAAA,MAAA;EACD,aAAA,EAAA,MAAA;EAAI,gBAAA,EADH,IACG;EAGV,eAAA,EAHM,IAGW;AAM7B,CAAA;AAOY,KAbA,iBAAA,GAagB;EAMhB,IAAA,EAAA,gBAAgB;EACzB,IAAA,EAlBI,YAkBJ;EACA,SAAA,EAlBS,IAkBT;CACA;AACA,KAjBS,gBAAA,GAiBT;EAAgB,IAAA,EAAA,eAAA;EAEP,IAAA,EAjBL,YAiBK;EAMA,IAAA,EAAA,MAAA,GAAA,IAAA;EAsOC,SAAA,EA3PD,IA2PC;CAAsB;AAAA,KAxPvB,gBAAA,GAwPuB;EAAA,IAAA,EAAA,eAAA;EAIhC,IAAA,EA1PI,IA0PJ;;;KAtPS,gBAAA,GACT,iBACA,oBACA,mBACA;KAES,yBAAA;SACJ;aACI;;;KAIA,uBAAA,GAA0B;;;;;;;cAsOzB;;;;GAIV"}
|
|
@@ -12,6 +12,13 @@ const toDate = (date) => {
|
|
|
12
12
|
if (typeof date === "string") return new Date(date);
|
|
13
13
|
return date;
|
|
14
14
|
};
|
|
15
|
+
const getDateString = (date) => {
|
|
16
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
17
|
+
};
|
|
18
|
+
const createDayDate = (dateString) => {
|
|
19
|
+
const [year, month, day] = dateString.split("-").map(Number);
|
|
20
|
+
return new Date(year ?? 0, (month ?? 1) - 1, day ?? 1, 0, 0, 0, 0);
|
|
21
|
+
};
|
|
15
22
|
const getSenderIdAndTypeFromTimelineItem = (item) => {
|
|
16
23
|
if (item.visitorId) return {
|
|
17
24
|
senderId: item.visitorId,
|
|
@@ -34,7 +41,25 @@ const EMPTY_STRING_ARRAY = Object.freeze([]);
|
|
|
34
41
|
const groupTimelineItems = (items) => {
|
|
35
42
|
const result = [];
|
|
36
43
|
let currentGroup = null;
|
|
44
|
+
let currentDayString = null;
|
|
45
|
+
const maybeInsertDaySeparator = (itemDate) => {
|
|
46
|
+
const itemDayString = getDateString(itemDate);
|
|
47
|
+
if (currentDayString !== itemDayString) {
|
|
48
|
+
if (currentGroup) {
|
|
49
|
+
result.push(currentGroup);
|
|
50
|
+
currentGroup = null;
|
|
51
|
+
}
|
|
52
|
+
result.push({
|
|
53
|
+
type: "day_separator",
|
|
54
|
+
date: createDayDate(itemDayString),
|
|
55
|
+
dateString: itemDayString
|
|
56
|
+
});
|
|
57
|
+
currentDayString = itemDayString;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
37
60
|
for (const item of items) {
|
|
61
|
+
const itemDate = toDate(item.createdAt);
|
|
62
|
+
maybeInsertDaySeparator(itemDate);
|
|
38
63
|
if (item.type === "event") {
|
|
39
64
|
if (currentGroup) {
|
|
40
65
|
result.push(currentGroup);
|
|
@@ -43,7 +68,7 @@ const groupTimelineItems = (items) => {
|
|
|
43
68
|
result.push({
|
|
44
69
|
type: "timeline_event",
|
|
45
70
|
item,
|
|
46
|
-
timestamp:
|
|
71
|
+
timestamp: itemDate
|
|
47
72
|
});
|
|
48
73
|
continue;
|
|
49
74
|
}
|
|
@@ -56,7 +81,7 @@ const groupTimelineItems = (items) => {
|
|
|
56
81
|
type: "timeline_tool",
|
|
57
82
|
item,
|
|
58
83
|
tool: item.tool ?? null,
|
|
59
|
-
timestamp:
|
|
84
|
+
timestamp: itemDate
|
|
60
85
|
});
|
|
61
86
|
continue;
|
|
62
87
|
}
|
|
@@ -64,7 +89,7 @@ const groupTimelineItems = (items) => {
|
|
|
64
89
|
if (currentGroup && currentGroup.senderId === senderId) {
|
|
65
90
|
currentGroup.items.push(item);
|
|
66
91
|
currentGroup.lastMessageId = item.id || currentGroup.lastMessageId;
|
|
67
|
-
currentGroup.lastMessageTime =
|
|
92
|
+
currentGroup.lastMessageTime = itemDate;
|
|
68
93
|
} else {
|
|
69
94
|
if (currentGroup) result.push(currentGroup);
|
|
70
95
|
currentGroup = {
|
|
@@ -74,27 +99,26 @@ const groupTimelineItems = (items) => {
|
|
|
74
99
|
items: [item],
|
|
75
100
|
firstMessageId: item.id || "",
|
|
76
101
|
lastMessageId: item.id || "",
|
|
77
|
-
firstMessageTime:
|
|
78
|
-
lastMessageTime:
|
|
102
|
+
firstMessageTime: itemDate,
|
|
103
|
+
lastMessageTime: itemDate
|
|
79
104
|
};
|
|
80
105
|
}
|
|
81
106
|
}
|
|
82
107
|
if (currentGroup) result.push(currentGroup);
|
|
83
108
|
return result;
|
|
84
109
|
};
|
|
85
|
-
const buildTimelineReadReceiptData = (seenData, items) => {
|
|
110
|
+
const buildTimelineReadReceiptData = (seenData, items, sortedMessageItems) => {
|
|
86
111
|
const seenByMap = /* @__PURE__ */ new Map();
|
|
87
112
|
const lastReadMessageMap = /* @__PURE__ */ new Map();
|
|
88
113
|
const unreadCountMap = /* @__PURE__ */ new Map();
|
|
89
114
|
for (const item of items) if (item.type === "message" && item.id) seenByMap.set(item.id, /* @__PURE__ */ new Set());
|
|
90
|
-
const sortedItems = [...items].filter((item) => item.type === "message").sort((a, b) => getTimestamp(a.createdAt) - getTimestamp(b.createdAt));
|
|
91
115
|
for (const seen of seenData) {
|
|
92
116
|
const seenTime = getTimestamp(seen.lastSeenAt);
|
|
93
117
|
const viewerId = seen.userId || seen.visitorId || seen.aiAgentId;
|
|
94
118
|
if (!viewerId) continue;
|
|
95
119
|
let lastReadItem = null;
|
|
96
120
|
let unreadCount = 0;
|
|
97
|
-
for (const item of
|
|
121
|
+
for (const item of sortedMessageItems) if (getTimestamp(item.createdAt) <= seenTime) {
|
|
98
122
|
if (item.id) {
|
|
99
123
|
const seenBy = seenByMap.get(item.id);
|
|
100
124
|
if (seenBy) seenBy.add(viewerId);
|
|
@@ -116,10 +140,16 @@ const buildTimelineReadReceiptData = (seenData, items) => {
|
|
|
116
140
|
* minimal effort. Seen data is normalised into quick lookup maps for unread
|
|
117
141
|
* indicators.
|
|
118
142
|
*/
|
|
119
|
-
const useGroupedMessages = ({ items, seenData = [], currentViewerId
|
|
143
|
+
const useGroupedMessages = ({ items, seenData = [], currentViewerId }) => {
|
|
120
144
|
return useMemo(() => {
|
|
121
145
|
const groupedItems = groupTimelineItems(items);
|
|
122
|
-
const
|
|
146
|
+
const sortedMessageItems = items.filter((item) => item.type === "message").sort((a, b) => getTimestamp(a.createdAt) - getTimestamp(b.createdAt));
|
|
147
|
+
const messageIndexMap = /* @__PURE__ */ new Map();
|
|
148
|
+
for (let i = 0; i < sortedMessageItems.length; i++) {
|
|
149
|
+
const item = sortedMessageItems[i];
|
|
150
|
+
if (item?.id) messageIndexMap.set(item.id, i);
|
|
151
|
+
}
|
|
152
|
+
const { seenByMap, lastReadMessageMap, unreadCountMap } = buildTimelineReadReceiptData(seenData, items, sortedMessageItems);
|
|
123
153
|
const seenByArrayCache = /* @__PURE__ */ new Map();
|
|
124
154
|
return {
|
|
125
155
|
items: groupedItems,
|
|
@@ -148,7 +178,10 @@ const useGroupedMessages = ({ items, seenData = [], currentViewerId, viewerType
|
|
|
148
178
|
hasUnreadAfter: (messageId, userId) => {
|
|
149
179
|
const lastRead = lastReadMessageMap.get(userId);
|
|
150
180
|
if (!lastRead) return true;
|
|
151
|
-
|
|
181
|
+
const messageIndex = messageIndexMap.get(messageId);
|
|
182
|
+
const lastReadIndex = messageIndexMap.get(lastRead);
|
|
183
|
+
if (messageIndex === void 0 || lastReadIndex === void 0) return true;
|
|
184
|
+
return messageIndex < lastReadIndex;
|
|
152
185
|
}
|
|
153
186
|
};
|
|
154
187
|
}, [
|