@athoscommerce/snap-store-mobx 1.5.0 → 1.5.1-beta.112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Autocomplete/Stores/AutocompleteFacetStore.js +2 -1
- package/dist/cjs/Chat/ChatStore.d.ts +87 -0
- package/dist/cjs/Chat/ChatStore.d.ts.map +1 -0
- package/dist/cjs/Chat/ChatStore.js +545 -0
- package/dist/cjs/Chat/Stores/ChatAttachmentStore.d.ts +98 -0
- package/dist/cjs/Chat/Stores/ChatAttachmentStore.d.ts.map +1 -0
- package/dist/cjs/Chat/Stores/ChatAttachmentStore.js +314 -0
- package/dist/cjs/Chat/Stores/ChatCompareStore.d.ts +16 -0
- package/dist/cjs/Chat/Stores/ChatCompareStore.d.ts.map +1 -0
- package/dist/cjs/Chat/Stores/ChatCompareStore.js +65 -0
- package/dist/cjs/Chat/Stores/ChatSessionStore.d.ts +107 -0
- package/dist/cjs/Chat/Stores/ChatSessionStore.d.ts.map +1 -0
- package/dist/cjs/Chat/Stores/ChatSessionStore.js +635 -0
- package/dist/cjs/Search/Stores/SearchFacetStore.d.ts.map +1 -1
- package/dist/cjs/Search/Stores/SearchFacetStore.js +13 -10
- package/dist/cjs/Search/Stores/index.d.ts +1 -1
- package/dist/cjs/Search/Stores/index.d.ts.map +1 -1
- package/dist/cjs/Search/Stores/index.js +2 -1
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/types.d.ts +21 -2
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/esm/Autocomplete/Stores/AutocompleteFacetStore.js +1 -1
- package/dist/esm/Chat/ChatStore.d.ts +87 -0
- package/dist/esm/Chat/ChatStore.d.ts.map +1 -0
- package/dist/esm/Chat/ChatStore.js +484 -0
- package/dist/esm/Chat/Stores/ChatAttachmentStore.d.ts +98 -0
- package/dist/esm/Chat/Stores/ChatAttachmentStore.d.ts.map +1 -0
- package/dist/esm/Chat/Stores/ChatAttachmentStore.js +222 -0
- package/dist/esm/Chat/Stores/ChatCompareStore.d.ts +16 -0
- package/dist/esm/Chat/Stores/ChatCompareStore.d.ts.map +1 -0
- package/dist/esm/Chat/Stores/ChatCompareStore.js +51 -0
- package/dist/esm/Chat/Stores/ChatSessionStore.d.ts +107 -0
- package/dist/esm/Chat/Stores/ChatSessionStore.d.ts.map +1 -0
- package/dist/esm/Chat/Stores/ChatSessionStore.js +601 -0
- package/dist/esm/Search/Stores/SearchFacetStore.d.ts.map +1 -1
- package/dist/esm/Search/Stores/SearchFacetStore.js +13 -10
- package/dist/esm/Search/Stores/index.d.ts +1 -1
- package/dist/esm/Search/Stores/index.d.ts.map +1 -1
- package/dist/esm/Search/Stores/index.js +1 -1
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/types.d.ts +21 -2
- package/dist/esm/types.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -39,6 +39,7 @@ var AutocompleteFacetStore = /** @class */ (function (_super) {
|
|
|
39
39
|
var _a;
|
|
40
40
|
(_a = facet.values) === null || _a === void 0 ? void 0 : _a.forEach(function (value) {
|
|
41
41
|
value.preview = function () {
|
|
42
|
+
var _a;
|
|
42
43
|
facets.map(function (facet) {
|
|
43
44
|
var _a;
|
|
44
45
|
facet.filtered = false;
|
|
@@ -49,7 +50,7 @@ var AutocompleteFacetStore = /** @class */ (function (_super) {
|
|
|
49
50
|
facet.filtered = true;
|
|
50
51
|
value.filtered = true;
|
|
51
52
|
state === null || state === void 0 ? void 0 : state.autocomplete.locks.facets.lock();
|
|
52
|
-
value.url.go();
|
|
53
|
+
(_a = value.url) === null || _a === void 0 ? void 0 : _a.go();
|
|
53
54
|
};
|
|
54
55
|
});
|
|
55
56
|
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { ChatStoreConfig, StoreServices } from '../types';
|
|
2
|
+
import { MetaStore } from '../Meta/MetaStore';
|
|
3
|
+
import type { MetaResponseModel, SearchResponseModelFacet } from '@athoscommerce/snapi-types';
|
|
4
|
+
import { AbstractStore } from '../Abstract/AbstractStore';
|
|
5
|
+
import type { ChatResponseModel, ChatRequestModel, ProductsResponseModel } from '@athoscommerce/snap-client';
|
|
6
|
+
import type { UrlManager } from '@athoscommerce/snap-url-manager';
|
|
7
|
+
import { StorageStore } from '@athoscommerce/snap-toolbox';
|
|
8
|
+
import { ChatSessionStore } from './Stores/ChatSessionStore';
|
|
9
|
+
import { ChatStatusResponse } from '@athoscommerce/snap-client';
|
|
10
|
+
import { Product } from '../Search/Stores/SearchResultStore';
|
|
11
|
+
import { SearchFacetStore } from '../Search/Stores/SearchFacetStore';
|
|
12
|
+
export declare class ChatStore extends AbstractStore<ChatStoreConfig> {
|
|
13
|
+
meta?: MetaStore;
|
|
14
|
+
inputValue: string;
|
|
15
|
+
open: boolean;
|
|
16
|
+
storage: StorageStore;
|
|
17
|
+
services: StoreServices;
|
|
18
|
+
/** Detached UrlManager — holds the in-progress facet selection for the active facets display.
|
|
19
|
+
* Detached so .go() updates state without navigating the page. */
|
|
20
|
+
urlManager: UrlManager;
|
|
21
|
+
/** The single facet store rendered in chat — backed by the active message's facets. */
|
|
22
|
+
facets: SearchFacetStore;
|
|
23
|
+
chats: ChatSessionStore[];
|
|
24
|
+
currentChatId: string;
|
|
25
|
+
chatEnabled: boolean | null;
|
|
26
|
+
initChatLoading: boolean;
|
|
27
|
+
suggestedQuestions: string[];
|
|
28
|
+
welcomeMessage: string;
|
|
29
|
+
features: ChatStatusResponse['features'];
|
|
30
|
+
productQuickview: Product | null;
|
|
31
|
+
productQuickviewError: string | null;
|
|
32
|
+
/** Raw meta kept for lazy hydration of inactive chat sessions. */
|
|
33
|
+
private storedMetaData;
|
|
34
|
+
/** Tracks which message currently owns the displayed facets — guards against redundant rebuilds. */
|
|
35
|
+
private activeFacetsMessageId;
|
|
36
|
+
/** Bumps on every detached-urlManager change so observers re-evaluate isFacetSelected. */
|
|
37
|
+
urlVersion: number;
|
|
38
|
+
/** Snapshot of the applied filter state captured the last time the active message was seeded.
|
|
39
|
+
* Compared against the live urlManager state to decide whether there are pending changes.
|
|
40
|
+
* `null` means no facets have been seeded yet — there are no pending changes in that case. */
|
|
41
|
+
private appliedFilterSnapshot;
|
|
42
|
+
constructor(config: ChatStoreConfig, services: StoreServices);
|
|
43
|
+
/** Build a SearchFacetStore from raw facet data using the current detached urlManager.
|
|
44
|
+
* Synthesizes meta entries for each facet so SearchFacetStore's display/meta filter
|
|
45
|
+
* doesn't drop chat facets (the chat backend already decided what to send). */
|
|
46
|
+
private buildFacetStore;
|
|
47
|
+
/** Reset the detached urlManager and seed it with this message's filtered facet values,
|
|
48
|
+
* then rebuild the root SearchFacetStore so the UI shows the matching display. */
|
|
49
|
+
setActiveFacets(facets: SearchResponseModelFacet[], messageId: string | null): void;
|
|
50
|
+
/** True when the in-progress facet selection differs from the filters that were applied
|
|
51
|
+
* by the active productSearchResult — i.e. the user has selected/removed something that
|
|
52
|
+
* hasn't been sent yet. Used to decide whether to show the Apply button. */
|
|
53
|
+
get hasPendingFacetChanges(): boolean;
|
|
54
|
+
get currentChat(): ChatSessionStore | undefined;
|
|
55
|
+
get chatsIds(): string[];
|
|
56
|
+
get blocked(): boolean;
|
|
57
|
+
private applyChatStatusResponse;
|
|
58
|
+
handleChatStatusResponse(response: ChatStatusResponse): boolean;
|
|
59
|
+
reset(): void;
|
|
60
|
+
setProductQuickview(product: Product): void;
|
|
61
|
+
updateProductQuickview(response: ProductsResponseModel): void;
|
|
62
|
+
setProductQuickviewError(message: string): void;
|
|
63
|
+
clearProductQuickview(): void;
|
|
64
|
+
clearHistory(): void;
|
|
65
|
+
createChat(data?: {
|
|
66
|
+
sessionId: string;
|
|
67
|
+
sessionEndTime?: Date;
|
|
68
|
+
}): ChatSessionStore;
|
|
69
|
+
switchChat(id: string): void;
|
|
70
|
+
sendProductQuery(result: any, options: {
|
|
71
|
+
requestType: 'productQuery' | 'productSimilar' | 'productComparison';
|
|
72
|
+
}): void;
|
|
73
|
+
compareProduct(result: any): void;
|
|
74
|
+
addFacet(facet: {
|
|
75
|
+
key: string;
|
|
76
|
+
value: string;
|
|
77
|
+
}): void;
|
|
78
|
+
removeFacet(key: string, value: string): void;
|
|
79
|
+
clearPendingFacets(): void;
|
|
80
|
+
isFacetSelected(key: string, value: string): boolean;
|
|
81
|
+
request(request: ChatRequestModel): void;
|
|
82
|
+
update(data: {
|
|
83
|
+
chat: ChatResponseModel;
|
|
84
|
+
meta: MetaResponseModel;
|
|
85
|
+
}): void;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=ChatStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatStore.d.ts","sourceRoot":"","sources":["../../../src/Chat/ChatStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __extends = (this && this.__extends) || (function () {
|
|
3
|
+
var extendStatics = function (d, b) {
|
|
4
|
+
extendStatics = Object.setPrototypeOf ||
|
|
5
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
+
return extendStatics(d, b);
|
|
8
|
+
};
|
|
9
|
+
return function (d, b) {
|
|
10
|
+
if (typeof b !== "function" && b !== null)
|
|
11
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
12
|
+
extendStatics(d, b);
|
|
13
|
+
function __() { this.constructor = d; }
|
|
14
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
15
|
+
};
|
|
16
|
+
})();
|
|
17
|
+
var __assign = (this && this.__assign) || function () {
|
|
18
|
+
__assign = Object.assign || function(t) {
|
|
19
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
20
|
+
s = arguments[i];
|
|
21
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
22
|
+
t[p] = s[p];
|
|
23
|
+
}
|
|
24
|
+
return t;
|
|
25
|
+
};
|
|
26
|
+
return __assign.apply(this, arguments);
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.ChatStore = void 0;
|
|
30
|
+
var mobx_1 = require("mobx");
|
|
31
|
+
var MetaStore_1 = require("../Meta/MetaStore");
|
|
32
|
+
var AbstractStore_1 = require("../Abstract/AbstractStore");
|
|
33
|
+
var snap_toolbox_1 = require("@athoscommerce/snap-toolbox");
|
|
34
|
+
var ChatSessionStore_1 = require("./Stores/ChatSessionStore");
|
|
35
|
+
var SearchResultStore_1 = require("../Search/Stores/SearchResultStore");
|
|
36
|
+
var SearchFacetStore_1 = require("../Search/Stores/SearchFacetStore");
|
|
37
|
+
var CHAT_STATUS_EXPIRATION_TIME = 1000 * 60 * 60 * 12; // 12 hours
|
|
38
|
+
var ChatStore = /** @class */ (function (_super) {
|
|
39
|
+
__extends(ChatStore, _super);
|
|
40
|
+
function ChatStore(config, services) {
|
|
41
|
+
var _this = _super.call(this, config) || this;
|
|
42
|
+
_this.meta = undefined;
|
|
43
|
+
_this.inputValue = '';
|
|
44
|
+
_this.open = false;
|
|
45
|
+
_this.chats = [];
|
|
46
|
+
_this.chatEnabled = null;
|
|
47
|
+
_this.initChatLoading = false;
|
|
48
|
+
_this.suggestedQuestions = [];
|
|
49
|
+
_this.welcomeMessage = '';
|
|
50
|
+
_this.features = { imageSearch: { enabled: false }, similarProducts: { enabled: false } };
|
|
51
|
+
_this.productQuickview = null;
|
|
52
|
+
_this.productQuickviewError = null;
|
|
53
|
+
/** Raw meta kept for lazy hydration of inactive chat sessions. */
|
|
54
|
+
_this.storedMetaData = null;
|
|
55
|
+
/** Tracks which message currently owns the displayed facets — guards against redundant rebuilds. */
|
|
56
|
+
_this.activeFacetsMessageId = null;
|
|
57
|
+
/** Bumps on every detached-urlManager change so observers re-evaluate isFacetSelected. */
|
|
58
|
+
_this.urlVersion = 0;
|
|
59
|
+
/** Snapshot of the applied filter state captured the last time the active message was seeded.
|
|
60
|
+
* Compared against the live urlManager state to decide whether there are pending changes.
|
|
61
|
+
* `null` means no facets have been seeded yet — there are no pending changes in that case. */
|
|
62
|
+
_this.appliedFilterSnapshot = null;
|
|
63
|
+
_this.services = services;
|
|
64
|
+
_this.urlManager = services.urlManager.detach(true);
|
|
65
|
+
_this.urlManager.subscribe(function () {
|
|
66
|
+
_this.urlVersion++;
|
|
67
|
+
});
|
|
68
|
+
var legacyKey = "ss-".concat(_this.config.id);
|
|
69
|
+
var storageKey = _this.config.siteId ? "ss-".concat(_this.config.siteId, "-").concat(_this.config.id) : legacyKey;
|
|
70
|
+
_this.storage = new snap_toolbox_1.StorageStore({
|
|
71
|
+
type: 'local',
|
|
72
|
+
key: storageKey,
|
|
73
|
+
});
|
|
74
|
+
_this.facets = _this.buildFacetStore();
|
|
75
|
+
var storedChatStatusResponse = _this.storage.get('chatStatusResponse');
|
|
76
|
+
if (storedChatStatusResponse) {
|
|
77
|
+
try {
|
|
78
|
+
var storedChatStatus = JSON.parse(storedChatStatusResponse);
|
|
79
|
+
if (storedChatStatus.checkTime && Date.now() - storedChatStatus.checkTime > CHAT_STATUS_EXPIRATION_TIME) {
|
|
80
|
+
// chat status is expired, remove from storage to trigger a new check
|
|
81
|
+
_this.storage.set('chatStatusResponse', null);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Apply the stored response without persisting, so the 12-hour
|
|
85
|
+
// checkTime keeps counting from the last real API call rather
|
|
86
|
+
// than resetting on every page load.
|
|
87
|
+
_this.applyChatStatusResponse(storedChatStatus.response);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (_a) {
|
|
91
|
+
_this.storage.set('chatStatusResponse', null);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// check for entries in storage
|
|
95
|
+
var latestChatId = '';
|
|
96
|
+
var storedChats = _this.storage.get('chats');
|
|
97
|
+
storedChats &&
|
|
98
|
+
Object.keys(storedChats || {}).forEach(function (chatId) {
|
|
99
|
+
var chatData = storedChats[chatId];
|
|
100
|
+
if (chatData) {
|
|
101
|
+
var restoredChat = new ChatSessionStore_1.ChatSessionStore({
|
|
102
|
+
data: __assign(__assign({}, chatData), { id: chatId }),
|
|
103
|
+
stores: {
|
|
104
|
+
storage: _this.storage,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
// Mark as unhydrated — results are still raw JSON from storage.
|
|
108
|
+
// Only the active session will be hydrated below.
|
|
109
|
+
restoredChat.hydrated = false;
|
|
110
|
+
_this.chats.push(restoredChat);
|
|
111
|
+
latestChatId = chatId;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
// Prefer the persisted active chat ID so switching chats survives page reloads.
|
|
115
|
+
// Fall back to the most recently created chat if the stored ID is missing or stale.
|
|
116
|
+
var storedCurrentChatId = _this.storage.get('currentChatId');
|
|
117
|
+
var activeChatId = storedCurrentChatId && _this.chats.some(function (chat) { return chat.id === storedCurrentChatId; }) ? storedCurrentChatId : latestChatId;
|
|
118
|
+
var storedMeta = _this.storage.get('meta');
|
|
119
|
+
if (storedMeta) {
|
|
120
|
+
try {
|
|
121
|
+
var metaData = JSON.parse(storedMeta);
|
|
122
|
+
_this.meta = new MetaStore_1.MetaStore({
|
|
123
|
+
data: {
|
|
124
|
+
meta: metaData,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
// Keep raw meta for lazy hydration of inactive sessions
|
|
128
|
+
_this.storedMetaData = metaData;
|
|
129
|
+
// Only hydrate the active chat session — inactive sessions will be
|
|
130
|
+
// hydrated lazily when switched to via switchChat()
|
|
131
|
+
var activeChat = _this.chats.find(function (chat) { return chat.id === activeChatId; });
|
|
132
|
+
if (activeChat) {
|
|
133
|
+
activeChat.hydrateResults(metaData);
|
|
134
|
+
activeChat.hydrated = true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (_b) {
|
|
138
|
+
_this.storage.set('meta', null);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
_this.currentChatId = activeChatId;
|
|
142
|
+
(0, mobx_1.makeObservable)(_this, {
|
|
143
|
+
meta: mobx_1.observable,
|
|
144
|
+
inputValue: mobx_1.observable,
|
|
145
|
+
open: mobx_1.observable,
|
|
146
|
+
chats: mobx_1.observable,
|
|
147
|
+
currentChatId: mobx_1.observable,
|
|
148
|
+
chatEnabled: mobx_1.observable,
|
|
149
|
+
initChatLoading: mobx_1.observable,
|
|
150
|
+
blocked: mobx_1.computed,
|
|
151
|
+
currentChat: mobx_1.computed,
|
|
152
|
+
chatsIds: mobx_1.computed,
|
|
153
|
+
features: mobx_1.observable,
|
|
154
|
+
suggestedQuestions: mobx_1.observable,
|
|
155
|
+
welcomeMessage: mobx_1.observable,
|
|
156
|
+
productQuickview: mobx_1.observable,
|
|
157
|
+
productQuickviewError: mobx_1.observable,
|
|
158
|
+
facets: mobx_1.observable,
|
|
159
|
+
urlVersion: mobx_1.observable,
|
|
160
|
+
hasPendingFacetChanges: mobx_1.computed,
|
|
161
|
+
});
|
|
162
|
+
// Sync the root facets display with the active message. Only productSearchResult
|
|
163
|
+
// messages carry facets — for any other active message, clear the display so a
|
|
164
|
+
// stale facet bar from a previous response doesn't linger.
|
|
165
|
+
(0, mobx_1.reaction)(function () {
|
|
166
|
+
var _a;
|
|
167
|
+
var active = (_a = _this.currentChat) === null || _a === void 0 ? void 0 : _a.activeMessage;
|
|
168
|
+
return active && active.messageType === 'productSearchResult' ? active : null;
|
|
169
|
+
}, function (active) {
|
|
170
|
+
if (active && active.id !== _this.activeFacetsMessageId) {
|
|
171
|
+
var facets = active.facets;
|
|
172
|
+
_this.setActiveFacets(facets || [], active.id);
|
|
173
|
+
}
|
|
174
|
+
else if (!active && _this.activeFacetsMessageId !== null) {
|
|
175
|
+
_this.setActiveFacets([], null);
|
|
176
|
+
}
|
|
177
|
+
}, { fireImmediately: true });
|
|
178
|
+
return _this;
|
|
179
|
+
}
|
|
180
|
+
/** Build a SearchFacetStore from raw facet data using the current detached urlManager.
|
|
181
|
+
* Synthesizes meta entries for each facet so SearchFacetStore's display/meta filter
|
|
182
|
+
* doesn't drop chat facets (the chat backend already decided what to send). */
|
|
183
|
+
ChatStore.prototype.buildFacetStore = function (facets) {
|
|
184
|
+
var _a;
|
|
185
|
+
if (facets === void 0) { facets = []; }
|
|
186
|
+
var baseMeta = ((_a = this.meta) === null || _a === void 0 ? void 0 : _a.data) || {};
|
|
187
|
+
var baseFacetMeta = baseMeta.facets || {};
|
|
188
|
+
var facetMeta = __assign({}, baseFacetMeta);
|
|
189
|
+
facets.forEach(function (facet) {
|
|
190
|
+
if (!facet.field)
|
|
191
|
+
return;
|
|
192
|
+
var expectedDisplay = facet.type === 'range' ? 'slider' : 'list';
|
|
193
|
+
var existing = facetMeta[facet.field];
|
|
194
|
+
if (!existing || existing.display !== expectedDisplay) {
|
|
195
|
+
facetMeta[facet.field] = __assign(__assign({}, (existing || {})), { field: facet.field, label: facet.label, display: expectedDisplay });
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
return new SearchFacetStore_1.SearchFacetStore({
|
|
199
|
+
config: {},
|
|
200
|
+
services: __assign(__assign({}, this.services), { urlManager: this.urlManager }),
|
|
201
|
+
stores: { storage: this.storage },
|
|
202
|
+
data: {
|
|
203
|
+
search: { facets: facets },
|
|
204
|
+
meta: __assign(__assign({}, baseMeta), { facets: facetMeta }),
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
};
|
|
208
|
+
/** Reset the detached urlManager and seed it with this message's filtered facet values,
|
|
209
|
+
* then rebuild the root SearchFacetStore so the UI shows the matching display. */
|
|
210
|
+
ChatStore.prototype.setActiveFacets = function (facets, messageId) {
|
|
211
|
+
var _this = this;
|
|
212
|
+
var _a;
|
|
213
|
+
// reset url state, then seed with the API-reported filtered values
|
|
214
|
+
this.urlManager.reset().go();
|
|
215
|
+
facets.forEach(function (facet) {
|
|
216
|
+
// Slider/range facets carry the active selection on the facet itself rather
|
|
217
|
+
// than on individual bucket values.
|
|
218
|
+
if (facet.type === 'range') {
|
|
219
|
+
var active = facet.active;
|
|
220
|
+
var range = facet.range;
|
|
221
|
+
if (active && range && (active.low !== range.low || active.high !== range.high)) {
|
|
222
|
+
_this.urlManager.set("filter.".concat(facet.field), { low: active.low, high: active.high }).go();
|
|
223
|
+
}
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
var filteredValues = (facet.values || []).filter(function (v) { return v === null || v === void 0 ? void 0 : v.filtered; });
|
|
227
|
+
filteredValues.forEach(function (v) {
|
|
228
|
+
if (typeof v.low !== 'undefined' || typeof v.high !== 'undefined') {
|
|
229
|
+
_this.urlManager.merge("filter.".concat(facet.field), [{ low: v.low, high: v.high }]).go();
|
|
230
|
+
}
|
|
231
|
+
else if (typeof v.value !== 'undefined') {
|
|
232
|
+
_this.urlManager.merge("filter.".concat(facet.field), v.value).go();
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
this.facets = this.buildFacetStore(facets);
|
|
237
|
+
this.activeFacetsMessageId = messageId;
|
|
238
|
+
this.appliedFilterSnapshot = stableStringify((_a = this.urlManager.state) === null || _a === void 0 ? void 0 : _a.filter);
|
|
239
|
+
};
|
|
240
|
+
Object.defineProperty(ChatStore.prototype, "hasPendingFacetChanges", {
|
|
241
|
+
/** True when the in-progress facet selection differs from the filters that were applied
|
|
242
|
+
* by the active productSearchResult — i.e. the user has selected/removed something that
|
|
243
|
+
* hasn't been sent yet. Used to decide whether to show the Apply button. */
|
|
244
|
+
get: function () {
|
|
245
|
+
var _a;
|
|
246
|
+
// touch the version so mobx re-runs this when urlManager state changes
|
|
247
|
+
void this.urlVersion;
|
|
248
|
+
var filterState = (_a = this.urlManager.state) === null || _a === void 0 ? void 0 : _a.filter;
|
|
249
|
+
if (this.appliedFilterSnapshot === null) {
|
|
250
|
+
// No facets have been seeded — pending changes exist only if
|
|
251
|
+
// the user has manually added filters via addFacet.
|
|
252
|
+
return filterState !== undefined && Object.keys(filterState).length > 0;
|
|
253
|
+
}
|
|
254
|
+
return stableStringify(filterState) !== this.appliedFilterSnapshot;
|
|
255
|
+
},
|
|
256
|
+
enumerable: false,
|
|
257
|
+
configurable: true
|
|
258
|
+
});
|
|
259
|
+
Object.defineProperty(ChatStore.prototype, "currentChat", {
|
|
260
|
+
get: function () {
|
|
261
|
+
var _this = this;
|
|
262
|
+
return this.chats.find(function (chat) { return chat.id === _this.currentChatId; });
|
|
263
|
+
},
|
|
264
|
+
enumerable: false,
|
|
265
|
+
configurable: true
|
|
266
|
+
});
|
|
267
|
+
Object.defineProperty(ChatStore.prototype, "chatsIds", {
|
|
268
|
+
get: function () {
|
|
269
|
+
return this.chats.map(function (chat) { return chat.id; });
|
|
270
|
+
},
|
|
271
|
+
enumerable: false,
|
|
272
|
+
configurable: true
|
|
273
|
+
});
|
|
274
|
+
Object.defineProperty(ChatStore.prototype, "blocked", {
|
|
275
|
+
get: function () {
|
|
276
|
+
var _a;
|
|
277
|
+
var isBlocked = false;
|
|
278
|
+
var blockedAttachments = (_a = this.currentChat) === null || _a === void 0 ? void 0 : _a.attachments.items.some(function (item) { return item.type === 'image' && item.state === 'loading'; });
|
|
279
|
+
if (this.loading || blockedAttachments) {
|
|
280
|
+
isBlocked = true;
|
|
281
|
+
}
|
|
282
|
+
return isBlocked;
|
|
283
|
+
},
|
|
284
|
+
enumerable: false,
|
|
285
|
+
configurable: true
|
|
286
|
+
});
|
|
287
|
+
ChatStore.prototype.applyChatStatusResponse = function (response) {
|
|
288
|
+
var chatbot = response.chatbot, features = response.features;
|
|
289
|
+
var status = chatbot.status, suggestedQuestions = chatbot.suggestedQuestions, welcomeMessage = chatbot.welcomeMessage;
|
|
290
|
+
this.chatEnabled = status.enabled === true;
|
|
291
|
+
this.features = features;
|
|
292
|
+
this.suggestedQuestions = suggestedQuestions || [];
|
|
293
|
+
this.welcomeMessage = welcomeMessage || '';
|
|
294
|
+
return this.chatEnabled;
|
|
295
|
+
};
|
|
296
|
+
ChatStore.prototype.handleChatStatusResponse = function (response) {
|
|
297
|
+
var enabled = this.applyChatStatusResponse(response);
|
|
298
|
+
// Always persist on a real API response — restarts the 12-hour expiration
|
|
299
|
+
// so each new chat session refreshes the cache.
|
|
300
|
+
this.storage.set('chatStatusResponse', JSON.stringify({ response: response, checkTime: Date.now() }));
|
|
301
|
+
return enabled;
|
|
302
|
+
};
|
|
303
|
+
ChatStore.prototype.reset = function () {
|
|
304
|
+
this.chats = [];
|
|
305
|
+
this.clearProductQuickview();
|
|
306
|
+
this.storage.clear();
|
|
307
|
+
this.createChat();
|
|
308
|
+
};
|
|
309
|
+
ChatStore.prototype.setProductQuickview = function (product) {
|
|
310
|
+
var _a;
|
|
311
|
+
// Clone the product so variant selections (and the parent-mappings merge done
|
|
312
|
+
// in updateProductQuickview) only affect the quickview — the carousel and any
|
|
313
|
+
// other surfaces still hold the original instance.
|
|
314
|
+
this.productQuickview = cloneProductForQuickview(product, (_a = this.meta) === null || _a === void 0 ? void 0 : _a.data);
|
|
315
|
+
this.productQuickviewError = null;
|
|
316
|
+
};
|
|
317
|
+
ChatStore.prototype.updateProductQuickview = function (response) {
|
|
318
|
+
var _a;
|
|
319
|
+
if (!this.productQuickview) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
// merge parent-level mappings from the API response into the Product
|
|
323
|
+
this.productQuickview.mappings = __assign(__assign({}, this.productQuickview.mappings), { core: __assign(__assign({}, this.productQuickview.mappings.core), response.mappings.core) });
|
|
324
|
+
var meta = ((_a = this.meta) === null || _a === void 0 ? void 0 : _a.data) || {};
|
|
325
|
+
if (this.productQuickview.variants) {
|
|
326
|
+
// update existing Variants with new data from the Products API
|
|
327
|
+
this.productQuickview.variants.optionConfig = response.variants.optionConfig;
|
|
328
|
+
this.productQuickview.variants.update(response.variants.data);
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
// create Variants for the first time using the Products API data
|
|
332
|
+
this.productQuickview.variants = new SearchResultStore_1.Variants({
|
|
333
|
+
data: {
|
|
334
|
+
mask: this.productQuickview.mask,
|
|
335
|
+
variants: response.variants.data,
|
|
336
|
+
optionConfig: response.variants.optionConfig,
|
|
337
|
+
meta: meta,
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
ChatStore.prototype.setProductQuickviewError = function (message) {
|
|
343
|
+
this.productQuickviewError = message;
|
|
344
|
+
};
|
|
345
|
+
ChatStore.prototype.clearProductQuickview = function () {
|
|
346
|
+
this.productQuickview = null;
|
|
347
|
+
this.productQuickviewError = null;
|
|
348
|
+
};
|
|
349
|
+
ChatStore.prototype.clearHistory = function () {
|
|
350
|
+
// drop previous chats while keeping the conversation the user is currently in;
|
|
351
|
+
// if there is no current chat, fall back to a full reset
|
|
352
|
+
var currentChat = this.currentChat;
|
|
353
|
+
if (!currentChat) {
|
|
354
|
+
this.reset();
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
var storedChats = this.storage.get('chats') || {};
|
|
358
|
+
var filtered = {};
|
|
359
|
+
if (storedChats[currentChat.id]) {
|
|
360
|
+
filtered[currentChat.id] = storedChats[currentChat.id];
|
|
361
|
+
}
|
|
362
|
+
this.storage.set('chats', filtered);
|
|
363
|
+
this.chats = [currentChat];
|
|
364
|
+
};
|
|
365
|
+
ChatStore.prototype.createChat = function (data) {
|
|
366
|
+
// abandon any in-flight request state from the previous chat — its
|
|
367
|
+
// response will be discarded by the controller, so the UI shouldn't
|
|
368
|
+
// keep showing its loading spinner or stale error
|
|
369
|
+
this.loading = false;
|
|
370
|
+
this.error = undefined;
|
|
371
|
+
// Prune old sessions before creating a new one to keep storage bounded
|
|
372
|
+
ChatSessionStore_1.ChatSessionStore.pruneStoredSessions(this.storage);
|
|
373
|
+
var newChat = new ChatSessionStore_1.ChatSessionStore({
|
|
374
|
+
data: {
|
|
375
|
+
sessionId: data === null || data === void 0 ? void 0 : data.sessionId,
|
|
376
|
+
sessionEndTime: data === null || data === void 0 ? void 0 : data.sessionEndTime,
|
|
377
|
+
},
|
|
378
|
+
stores: {
|
|
379
|
+
storage: this.storage,
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
this.currentChatId = newChat.id;
|
|
383
|
+
this.storage.set('currentChatId', newChat.id);
|
|
384
|
+
this.chats.push(newChat);
|
|
385
|
+
return newChat;
|
|
386
|
+
};
|
|
387
|
+
ChatStore.prototype.switchChat = function (id) {
|
|
388
|
+
var chatExists = this.chats.find(function (chat) { return chat.id === id; });
|
|
389
|
+
if (chatExists) {
|
|
390
|
+
// Lazily hydrate results when switching to a session for the first time
|
|
391
|
+
if (!chatExists.hydrated && this.storedMetaData) {
|
|
392
|
+
chatExists.hydrateResults(this.storedMetaData);
|
|
393
|
+
chatExists.hydrated = true;
|
|
394
|
+
}
|
|
395
|
+
this.currentChatId = id;
|
|
396
|
+
this.storage.set('currentChatId', id);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
ChatStore.prototype.sendProductQuery = function (result, options) {
|
|
400
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
401
|
+
var display = (result === null || result === void 0 ? void 0 : result.display) || result;
|
|
402
|
+
(_a = this.currentChat) === null || _a === void 0 ? void 0 : _a.attachments.add({
|
|
403
|
+
type: 'product',
|
|
404
|
+
requestType: options.requestType,
|
|
405
|
+
productId: result.id,
|
|
406
|
+
name: (_c = (_b = display.mappings) === null || _b === void 0 ? void 0 : _b.core) === null || _c === void 0 ? void 0 : _c.name,
|
|
407
|
+
thumbnailUrl: ((_e = (_d = display.mappings) === null || _d === void 0 ? void 0 : _d.core) === null || _e === void 0 ? void 0 : _e.thumbnailImageUrl) || ((_g = (_f = display.mappings) === null || _f === void 0 ? void 0 : _f.core) === null || _g === void 0 ? void 0 : _g.imageUrl) || ((_j = (_h = display.mappings) === null || _h === void 0 ? void 0 : _h.core) === null || _j === void 0 ? void 0 : _j.parentImageUrl),
|
|
408
|
+
});
|
|
409
|
+
// for the productQuery flow we want the secondary chat to immediately
|
|
410
|
+
// show the product details panel — push a productQuery message so it
|
|
411
|
+
// becomes the active side-chat message until a productAnswer arrives
|
|
412
|
+
if (options.requestType === 'productQuery' && this.currentChat) {
|
|
413
|
+
this.currentChat.pushProductQueryMessage(result);
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
ChatStore.prototype.compareProduct = function (result) {
|
|
417
|
+
var _a;
|
|
418
|
+
(_a = this.currentChat) === null || _a === void 0 ? void 0 : _a.comparisons.add({
|
|
419
|
+
result: result,
|
|
420
|
+
});
|
|
421
|
+
};
|
|
422
|
+
ChatStore.prototype.addFacet = function (facet) {
|
|
423
|
+
var value = parseFacetValue(facet.value);
|
|
424
|
+
this.urlManager.merge("filter.".concat(facet.key), Array.isArray(value) ? value : value).go();
|
|
425
|
+
};
|
|
426
|
+
ChatStore.prototype.removeFacet = function (key, value) {
|
|
427
|
+
var parsed = parseFacetValue(value);
|
|
428
|
+
this.urlManager.remove("filter.".concat(key), parsed).go();
|
|
429
|
+
};
|
|
430
|
+
ChatStore.prototype.clearPendingFacets = function () {
|
|
431
|
+
this.urlManager.remove('filter').go();
|
|
432
|
+
};
|
|
433
|
+
ChatStore.prototype.isFacetSelected = function (key, value) {
|
|
434
|
+
var _a;
|
|
435
|
+
// touch the version so mobx re-runs this when urlManager state changes (the
|
|
436
|
+
// underlying UrlManager isn't a mobx observable, so we mirror its updates here)
|
|
437
|
+
void this.urlVersion;
|
|
438
|
+
var filterState = (_a = this.urlManager.state) === null || _a === void 0 ? void 0 : _a.filter;
|
|
439
|
+
if (!filterState || !filterState[key])
|
|
440
|
+
return false;
|
|
441
|
+
var stored = Array.isArray(filterState[key]) ? filterState[key] : [filterState[key]];
|
|
442
|
+
var parsed = parseFacetValue(value);
|
|
443
|
+
return stored.some(function (entry) { return facetValueEquals(entry, parsed); });
|
|
444
|
+
};
|
|
445
|
+
ChatStore.prototype.request = function (request) {
|
|
446
|
+
var _this = this;
|
|
447
|
+
var _a, _b;
|
|
448
|
+
// snapshot facet labels (field + per-value) from the active facets display so the
|
|
449
|
+
// outgoing user message can render "Filter by Color Red" rather than raw keys
|
|
450
|
+
var filterLabels = {};
|
|
451
|
+
var _loop_1 = function (facet) {
|
|
452
|
+
var valueFacet = facet;
|
|
453
|
+
var values = {};
|
|
454
|
+
(valueFacet.values || []).forEach(function (v) {
|
|
455
|
+
var _a, _b;
|
|
456
|
+
if (typeof v.low !== 'undefined' || typeof v.high !== 'undefined') {
|
|
457
|
+
values["".concat((_a = v.low) !== null && _a !== void 0 ? _a : '*', ":").concat((_b = v.high) !== null && _b !== void 0 ? _b : '*')] = v.label;
|
|
458
|
+
}
|
|
459
|
+
else if (typeof v.value !== 'undefined') {
|
|
460
|
+
values[v.value] = v.label;
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
filterLabels[valueFacet.field] = { facetLabel: valueFacet.label || valueFacet.field, values: values };
|
|
464
|
+
};
|
|
465
|
+
for (var _i = 0, _c = this.facets; _i < _c.length; _i++) {
|
|
466
|
+
var facet = _c[_i];
|
|
467
|
+
_loop_1(facet);
|
|
468
|
+
}
|
|
469
|
+
(_a = this.currentChat) === null || _a === void 0 ? void 0 : _a.request(request, filterLabels);
|
|
470
|
+
// remove any productSimilar attachments after request
|
|
471
|
+
var productSimilarAttachments = ((_b = this.currentChat) === null || _b === void 0 ? void 0 : _b.attachments.attached.filter(function (item) { return item.type === 'product' && item.requestType === 'productSimilar'; })) || [];
|
|
472
|
+
productSimilarAttachments.forEach(function (item) {
|
|
473
|
+
var _a;
|
|
474
|
+
(_a = _this.currentChat) === null || _a === void 0 ? void 0 : _a.attachments.remove(item.id);
|
|
475
|
+
});
|
|
476
|
+
};
|
|
477
|
+
ChatStore.prototype.update = function (data) {
|
|
478
|
+
// TODO: handle error
|
|
479
|
+
// if(err?.responseBody?.errorMessage || err?.responseBody?.errorCode) {
|
|
480
|
+
// const errorMessage = err?.responseBody?.errorMessage || 'An unknown error has occurred.';
|
|
481
|
+
// }
|
|
482
|
+
var _a;
|
|
483
|
+
(_a = this.currentChat) === null || _a === void 0 ? void 0 : _a.update(data);
|
|
484
|
+
if (!this.meta) {
|
|
485
|
+
this.storage.set('meta', JSON.stringify(data.meta));
|
|
486
|
+
}
|
|
487
|
+
this.meta = new MetaStore_1.MetaStore({
|
|
488
|
+
data: {
|
|
489
|
+
meta: data.meta,
|
|
490
|
+
},
|
|
491
|
+
});
|
|
492
|
+
// Keep raw meta in sync for lazy hydration of inactive sessions
|
|
493
|
+
this.storedMetaData = data.meta;
|
|
494
|
+
};
|
|
495
|
+
return ChatStore;
|
|
496
|
+
}(AbstractStore_1.AbstractStore));
|
|
497
|
+
exports.ChatStore = ChatStore;
|
|
498
|
+
/** Deterministic JSON.stringify — sorts object keys recursively so equality comparisons
|
|
499
|
+
* aren't sensitive to insertion order. */
|
|
500
|
+
function stableStringify(value) {
|
|
501
|
+
if (value === null || value === undefined)
|
|
502
|
+
return 'null';
|
|
503
|
+
if (typeof value !== 'object')
|
|
504
|
+
return JSON.stringify(value);
|
|
505
|
+
if (Array.isArray(value)) {
|
|
506
|
+
return "[".concat(value.map(function (v) { return stableStringify(v); }).join(','), "]");
|
|
507
|
+
}
|
|
508
|
+
var keys = Object.keys(value).sort();
|
|
509
|
+
return "{".concat(keys.map(function (k) { return "".concat(JSON.stringify(k), ":").concat(stableStringify(value[k])); }).join(','), "}");
|
|
510
|
+
}
|
|
511
|
+
/** Convert a chat-facet UI value into the URL-state shape — range buckets arrive as "low:high". */
|
|
512
|
+
function parseFacetValue(value) {
|
|
513
|
+
if (typeof value === 'string' && value.includes(':')) {
|
|
514
|
+
var _a = value.split(':'), low = _a[0], high = _a[1];
|
|
515
|
+
return {
|
|
516
|
+
low: low === '*' ? undefined : Number(low),
|
|
517
|
+
high: high === '*' ? undefined : Number(high),
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
return value;
|
|
521
|
+
}
|
|
522
|
+
function facetValueEquals(a, b) {
|
|
523
|
+
if (typeof a === 'object' && a !== null && typeof b === 'object' && b !== null) {
|
|
524
|
+
return a.low === b.low && a.high === b.high;
|
|
525
|
+
}
|
|
526
|
+
return a === b;
|
|
527
|
+
}
|
|
528
|
+
/** Build a fresh observable Product from an existing one. Variants are intentionally
|
|
529
|
+
* omitted — `updateProductQuickview` populates them from the products API response. */
|
|
530
|
+
function cloneProductForQuickview(product, meta) {
|
|
531
|
+
var _a, _b, _c;
|
|
532
|
+
var result = {
|
|
533
|
+
id: product.id,
|
|
534
|
+
responseId: product.responseId,
|
|
535
|
+
mappings: JSON.parse(JSON.stringify(product.mappings || {})),
|
|
536
|
+
attributes: JSON.parse(JSON.stringify(product.attributes || {})),
|
|
537
|
+
badges: ((_b = (_a = product.badges) === null || _a === void 0 ? void 0 : _a.all) === null || _b === void 0 ? void 0 : _b.map(function (b) { return ({ tag: b.tag }); })) || [],
|
|
538
|
+
};
|
|
539
|
+
return new SearchResultStore_1.Product({
|
|
540
|
+
config: {},
|
|
541
|
+
data: { result: result, meta: meta || {} },
|
|
542
|
+
position: (_c = product.position) !== null && _c !== void 0 ? _c : 0,
|
|
543
|
+
responseId: product.responseId,
|
|
544
|
+
});
|
|
545
|
+
}
|