@cimplify/sdk 0.92.0 → 0.94.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced.d.mts +3 -3
- package/dist/advanced.d.ts +3 -3
- package/dist/advanced.js +20 -20
- package/dist/advanced.mjs +1 -1
- package/dist/{chunk-OAHSF542.js → chunk-6Y7KC5YZ.js} +11 -0
- package/dist/{chunk-3MBMIEWT.js → chunk-AWNWP5J3.js} +4 -4
- package/dist/{chunk-3XD2FVP4.mjs → chunk-FKARGZK3.mjs} +195 -60
- package/dist/{chunk-DCRN67NO.js → chunk-QLGZIBRO.js} +237 -102
- package/dist/{chunk-54H2YYIW.mjs → chunk-QXWX7CD2.mjs} +2 -2
- package/dist/{chunk-IVJEUGWF.js → chunk-VEOAMBEX.js} +1 -3
- package/dist/{chunk-T7LDBZZZ.mjs → chunk-VUSHG3HA.mjs} +1 -3
- package/dist/{chunk-PYBFTMHI.mjs → chunk-X36FZZT6.mjs} +11 -0
- package/dist/{client-CeW3acve.d.ts → client-BTL9d_nN.d.mts} +29 -2
- package/dist/{client-BQbSKlHS.d.mts → client-BX30FAWZ.d.ts} +29 -2
- package/dist/{client-Bw4HtLP1.d.mts → client-Bhe2TnFW.d.mts} +2 -2
- package/dist/{client-Otzv_rgP.d.ts → client-DrkuEK_I.d.ts} +2 -2
- package/dist/{index-CmIboQjl.d.mts → index-BuNjyfaV.d.mts} +3 -3
- package/dist/{index-DgoRwz8l.d.ts → index-D-w5h82t.d.ts} +3 -3
- package/dist/index.d.mts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +69 -69
- package/dist/index.mjs +3 -3
- package/dist/mock/cli.mjs +11 -0
- package/dist/mock/library.js +11 -0
- package/dist/mock/library.mjs +11 -0
- package/dist/mock/msw.js +11 -0
- package/dist/mock/msw.mjs +11 -0
- package/dist/{payment-Cq8FT1B6.d.ts → payment-By0JidTe.d.ts} +1 -1
- package/dist/{payment-BFtLqiG2.d.mts → payment-pKPpaORI.d.mts} +1 -1
- package/dist/{price-DaFXT-Lr.d.ts → price-23RNXftz.d.ts} +1 -1
- package/dist/{price-DleDWk_s.d.mts → price-BvRtp6ak.d.mts} +1 -1
- package/dist/{product-bZ8G5Z8D.d.mts → product-O8-wcHSe.d.mts} +11 -0
- package/dist/{product-bZ8G5Z8D.d.ts → product-O8-wcHSe.d.ts} +11 -0
- package/dist/react.d.mts +20 -6
- package/dist/react.d.ts +20 -6
- package/dist/react.js +208 -76
- package/dist/react.mjs +190 -59
- package/dist/{server-D-iK-bNt.d.mts → server-BN9ZCAKT.d.mts} +5 -1
- package/dist/{server-DwsY9hlX.d.ts → server-DDGbfW_S.d.ts} +5 -1
- package/dist/server.d.mts +4 -4
- package/dist/server.d.ts +4 -4
- package/dist/server.js +7 -7
- package/dist/server.mjs +2 -2
- package/dist/testing/msw.d.mts +2 -2
- package/dist/testing/msw.d.ts +2 -2
- package/dist/testing/msw.js +2 -2
- package/dist/testing/msw.mjs +1 -1
- package/dist/testing/suite.d.mts +6 -6
- package/dist/testing/suite.d.ts +6 -6
- package/dist/testing/suite.js +23 -23
- package/dist/testing/suite.mjs +4 -4
- package/dist/testing.d.mts +5 -5
- package/dist/testing.d.ts +5 -5
- package/dist/testing.js +79 -79
- package/dist/testing.mjs +5 -5
- package/dist/utils.d.mts +4 -4
- package/dist/utils.d.ts +4 -4
- package/package.json +1 -1
- package/registry/chat-widget.json +1 -1
package/dist/react.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
var chunkHKN3YG76_js = require('./chunk-HKN3YG76.js');
|
|
5
5
|
var chunkXREIZ56K_js = require('./chunk-XREIZ56K.js');
|
|
6
|
-
var
|
|
7
|
-
var
|
|
6
|
+
var chunkQLGZIBRO_js = require('./chunk-QLGZIBRO.js');
|
|
7
|
+
var chunkVEOAMBEX_js = require('./chunk-VEOAMBEX.js');
|
|
8
8
|
var chunkPTPOQVXS_js = require('./chunk-PTPOQVXS.js');
|
|
9
9
|
var chunkAETVRBKI_js = require('./chunk-AETVRBKI.js');
|
|
10
10
|
require('./chunk-OWW5GUSB.js');
|
|
@@ -806,7 +806,7 @@ async function syncFromServer(entry) {
|
|
|
806
806
|
return next;
|
|
807
807
|
}
|
|
808
808
|
function emitMutationError(entry, operation, opId, idempotencyKey, error) {
|
|
809
|
-
entry.client.events.emit(
|
|
809
|
+
entry.client.events.emit(chunkQLGZIBRO_js.MUTATION_ERROR, {
|
|
810
810
|
service: "cart",
|
|
811
811
|
operation,
|
|
812
812
|
opId,
|
|
@@ -846,10 +846,10 @@ function getOrCreateStore(params) {
|
|
|
846
846
|
syncTail: Promise.resolve(),
|
|
847
847
|
failedOpRemovers: /* @__PURE__ */ new Set()
|
|
848
848
|
};
|
|
849
|
-
client.events.on(
|
|
849
|
+
client.events.on(chunkQLGZIBRO_js.SESSION_CLAIMED, () => {
|
|
850
850
|
if (entry.initialized) void syncFromServer(entry);
|
|
851
851
|
});
|
|
852
|
-
client.events.on(
|
|
852
|
+
client.events.on(chunkQLGZIBRO_js.SESSION_CLEARED, () => {
|
|
853
853
|
if (entry.initialized) void syncFromServer(entry);
|
|
854
854
|
});
|
|
855
855
|
cartStores.set(storeKey, entry);
|
|
@@ -1137,6 +1137,37 @@ function preconnectOrigin(url, crossOrigin) {
|
|
|
1137
1137
|
if (crossOrigin) link.crossOrigin = crossOrigin;
|
|
1138
1138
|
document.head.appendChild(link);
|
|
1139
1139
|
}
|
|
1140
|
+
function usePageViewTracking(client, enabled) {
|
|
1141
|
+
React14.useEffect(() => {
|
|
1142
|
+
if (!enabled || typeof window === "undefined") return;
|
|
1143
|
+
let lastPath = null;
|
|
1144
|
+
const record = () => {
|
|
1145
|
+
const path = window.location.pathname;
|
|
1146
|
+
if (path === lastPath) return;
|
|
1147
|
+
lastPath = path;
|
|
1148
|
+
try {
|
|
1149
|
+
client.activity?.trackPageView({ pagePath: path });
|
|
1150
|
+
} catch {
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
record();
|
|
1154
|
+
const { pushState, replaceState } = window.history;
|
|
1155
|
+
window.history.pushState = function(...args) {
|
|
1156
|
+
pushState.apply(this, args);
|
|
1157
|
+
record();
|
|
1158
|
+
};
|
|
1159
|
+
window.history.replaceState = function(...args) {
|
|
1160
|
+
replaceState.apply(this, args);
|
|
1161
|
+
record();
|
|
1162
|
+
};
|
|
1163
|
+
window.addEventListener("popstate", record);
|
|
1164
|
+
return () => {
|
|
1165
|
+
window.history.pushState = pushState;
|
|
1166
|
+
window.history.replaceState = replaceState;
|
|
1167
|
+
window.removeEventListener("popstate", record);
|
|
1168
|
+
};
|
|
1169
|
+
}, [client, enabled]);
|
|
1170
|
+
}
|
|
1140
1171
|
var LOCATION_STORAGE_KEY = "cimplify_location_id";
|
|
1141
1172
|
var DISPLAY_CURRENCY_STORAGE_KEY = "cimplify_display_currency";
|
|
1142
1173
|
var FX_REFRESH_INTERVAL = 12e4;
|
|
@@ -1145,7 +1176,7 @@ var DEFAULT_COUNTRY = "US";
|
|
|
1145
1176
|
function createDefaultClient() {
|
|
1146
1177
|
const processRef = globalThis.process;
|
|
1147
1178
|
const envPublicKey = processRef?.env?.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY || "";
|
|
1148
|
-
return
|
|
1179
|
+
return chunkQLGZIBRO_js.createCimplifyClient({ publicKey: envPublicKey });
|
|
1149
1180
|
}
|
|
1150
1181
|
function getStoredLocationId() {
|
|
1151
1182
|
if (typeof window === "undefined" || !window.localStorage) {
|
|
@@ -1217,9 +1248,11 @@ function CimplifyProvider({
|
|
|
1217
1248
|
initialLocations,
|
|
1218
1249
|
optimistic = true,
|
|
1219
1250
|
prefetch,
|
|
1220
|
-
linkComponent
|
|
1251
|
+
linkComponent,
|
|
1252
|
+
trackPageViews = true
|
|
1221
1253
|
}) {
|
|
1222
1254
|
const resolvedClient = React14.useMemo(() => client ?? createDefaultClient(), [client]);
|
|
1255
|
+
usePageViewTracking(resolvedClient, trackPageViews);
|
|
1223
1256
|
const resolvedPrefetch = React14.useMemo(
|
|
1224
1257
|
() => ({
|
|
1225
1258
|
mode: prefetch?.mode ?? DEFAULT_PREFETCH_OPTIONS.mode,
|
|
@@ -3457,7 +3490,7 @@ function CimplifySignInButton({
|
|
|
3457
3490
|
const onClick = async () => {
|
|
3458
3491
|
if (busy || disabled || !signInOpts.clientId) return;
|
|
3459
3492
|
setBusy(true);
|
|
3460
|
-
await
|
|
3493
|
+
await chunkVEOAMBEX_js.startSignIn({
|
|
3461
3494
|
...signInOpts,
|
|
3462
3495
|
onSuccess: () => {
|
|
3463
3496
|
setBusy(false);
|
|
@@ -5913,6 +5946,20 @@ var IDLE_TIMEOUT_MS = 6e4;
|
|
|
5913
5946
|
var WS_MAX_RECONNECT_ATTEMPTS = 10;
|
|
5914
5947
|
var WS_PATH = "/api/v1/support/conversation/ws";
|
|
5915
5948
|
var AWAIT_REPLY_TIMEOUT_MS = 3e4;
|
|
5949
|
+
function mergeMessages(prev, incoming) {
|
|
5950
|
+
const ids = new Set(prev.map((m) => m.id));
|
|
5951
|
+
const fresh = incoming.filter((m) => !ids.has(m.id));
|
|
5952
|
+
if (fresh.length === 0) return prev;
|
|
5953
|
+
const merged = [...prev, ...fresh].sort(
|
|
5954
|
+
(a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
|
|
5955
|
+
);
|
|
5956
|
+
const confirmed = new Set(
|
|
5957
|
+
merged.filter((m) => !m.id.startsWith("opt_") && m.client_id).map((m) => m.client_id)
|
|
5958
|
+
);
|
|
5959
|
+
return merged.filter(
|
|
5960
|
+
(m) => !(m.id.startsWith("opt_") && m.client_id && confirmed.has(m.client_id))
|
|
5961
|
+
);
|
|
5962
|
+
}
|
|
5916
5963
|
function useChat(options = {}) {
|
|
5917
5964
|
const context = useOptionalCimplifyClient();
|
|
5918
5965
|
const client = options.client ?? context?.client;
|
|
@@ -5965,18 +6012,22 @@ function useChat(options = {}) {
|
|
|
5965
6012
|
if (incoming.some((m) => m.sender_type === "bot" || m.sender_type === "agent")) {
|
|
5966
6013
|
stopAwaitingReply();
|
|
5967
6014
|
}
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
const fresh = incoming.filter((m) => !ids.has(m.id));
|
|
5971
|
-
return fresh.length > 0 ? [...prev, ...fresh] : prev;
|
|
5972
|
-
});
|
|
6015
|
+
if (incoming.length > 0) lastActivityRef.current = Date.now();
|
|
6016
|
+
setMessages((prev) => mergeMessages(prev, incoming));
|
|
5973
6017
|
},
|
|
5974
6018
|
[stopAwaitingReply]
|
|
5975
6019
|
);
|
|
6020
|
+
const lastConfirmedMessage = React14.useCallback(() => {
|
|
6021
|
+
const list = messagesRef.current;
|
|
6022
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
6023
|
+
if (!list[i].id.startsWith("opt_")) return list[i];
|
|
6024
|
+
}
|
|
6025
|
+
return void 0;
|
|
6026
|
+
}, []);
|
|
5976
6027
|
const fetchNewMessages = React14.useCallback(async () => {
|
|
5977
6028
|
const c = clientRef.current;
|
|
5978
6029
|
if (!c) return;
|
|
5979
|
-
const last =
|
|
6030
|
+
const last = lastConfirmedMessage();
|
|
5980
6031
|
const result = await c.support.getMessages({
|
|
5981
6032
|
after: last?.created_at,
|
|
5982
6033
|
limit: 50
|
|
@@ -5984,16 +6035,23 @@ function useChat(options = {}) {
|
|
|
5984
6035
|
if (result.ok && result.value.length > 0) {
|
|
5985
6036
|
appendMessages(result.value);
|
|
5986
6037
|
}
|
|
5987
|
-
}, [appendMessages]);
|
|
5988
|
-
const pollMessages = React14.useCallback(async () => {
|
|
5989
|
-
if (Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS) return;
|
|
5990
|
-
await fetchNewMessages();
|
|
5991
|
-
}, [fetchNewMessages]);
|
|
6038
|
+
}, [appendMessages, lastConfirmedMessage]);
|
|
5992
6039
|
const startPolling = React14.useCallback(() => {
|
|
5993
6040
|
if (pollRef.current) clearInterval(pollRef.current);
|
|
5994
|
-
const
|
|
5995
|
-
|
|
5996
|
-
|
|
6041
|
+
const idle = () => Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS;
|
|
6042
|
+
let current = idle() ? pollIdleInterval : pollInterval;
|
|
6043
|
+
const tick = () => {
|
|
6044
|
+
void fetchNewMessages().then(() => {
|
|
6045
|
+
const desired = idle() ? pollIdleInterval : pollInterval;
|
|
6046
|
+
if (desired !== current && pollRef.current) {
|
|
6047
|
+
current = desired;
|
|
6048
|
+
clearInterval(pollRef.current);
|
|
6049
|
+
pollRef.current = setInterval(tick, current);
|
|
6050
|
+
}
|
|
6051
|
+
});
|
|
6052
|
+
};
|
|
6053
|
+
pollRef.current = setInterval(tick, current);
|
|
6054
|
+
}, [fetchNewMessages, pollInterval, pollIdleInterval]);
|
|
5997
6055
|
const stopPolling = React14.useCallback(() => {
|
|
5998
6056
|
if (pollRef.current) {
|
|
5999
6057
|
clearInterval(pollRef.current);
|
|
@@ -6005,7 +6063,15 @@ function useChat(options = {}) {
|
|
|
6005
6063
|
if (!c || !openedRef.current) return;
|
|
6006
6064
|
const result = await c.support.openConversation();
|
|
6007
6065
|
if (!result.ok) return;
|
|
6008
|
-
|
|
6066
|
+
const snapshot = result.value.messages;
|
|
6067
|
+
setMessages((prev) => {
|
|
6068
|
+
const snapshotIds = new Set(snapshot.map((m) => m.id));
|
|
6069
|
+
const tail = snapshot.length > 0 ? new Date(snapshot[snapshot.length - 1].created_at).getTime() : 0;
|
|
6070
|
+
const extras = prev.filter(
|
|
6071
|
+
(m) => !snapshotIds.has(m.id) && (m.id.startsWith("opt_") || new Date(m.created_at).getTime() > tail)
|
|
6072
|
+
);
|
|
6073
|
+
return mergeMessages(snapshot, extras);
|
|
6074
|
+
});
|
|
6009
6075
|
aiEnabledRef.current = result.value.conversation.ai_enabled;
|
|
6010
6076
|
setHumanRequested(result.value.conversation.needs_human_review ?? false);
|
|
6011
6077
|
setSeenAt(result.value.conversation.team_read_at ?? null);
|
|
@@ -6017,7 +6083,13 @@ function useChat(options = {}) {
|
|
|
6017
6083
|
startPolling();
|
|
6018
6084
|
return;
|
|
6019
6085
|
}
|
|
6086
|
+
if (wsReconnectTimerRef.current) {
|
|
6087
|
+
clearTimeout(wsReconnectTimerRef.current);
|
|
6088
|
+
wsReconnectTimerRef.current = null;
|
|
6089
|
+
}
|
|
6020
6090
|
if (wsRef.current) {
|
|
6091
|
+
wsRef.current.onclose = null;
|
|
6092
|
+
wsRef.current.onmessage = null;
|
|
6021
6093
|
wsRef.current.close();
|
|
6022
6094
|
wsRef.current = null;
|
|
6023
6095
|
}
|
|
@@ -6041,7 +6113,7 @@ function useChat(options = {}) {
|
|
|
6041
6113
|
else stopAwaitingReply();
|
|
6042
6114
|
} else if (envelope.type === "resync") {
|
|
6043
6115
|
const latest = envelope.latest_message_at;
|
|
6044
|
-
const last =
|
|
6116
|
+
const last = lastConfirmedMessage();
|
|
6045
6117
|
if (latest && (!last || new Date(last.created_at).getTime() < new Date(latest).getTime())) {
|
|
6046
6118
|
fetchNewMessages();
|
|
6047
6119
|
}
|
|
@@ -6058,6 +6130,7 @@ function useChat(options = {}) {
|
|
|
6058
6130
|
if (wsReconnectAttemptRef.current < WS_MAX_RECONNECT_ATTEMPTS) {
|
|
6059
6131
|
const attempt = wsReconnectAttemptRef.current++;
|
|
6060
6132
|
const delay = Math.min(1e3 * Math.pow(2, attempt) + Math.random() * 500, 3e4);
|
|
6133
|
+
if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
|
|
6061
6134
|
wsReconnectTimerRef.current = setTimeout(connectWebSocket, delay);
|
|
6062
6135
|
} else {
|
|
6063
6136
|
startPolling();
|
|
@@ -6066,7 +6139,7 @@ function useChat(options = {}) {
|
|
|
6066
6139
|
ws.onerror = () => {
|
|
6067
6140
|
};
|
|
6068
6141
|
wsRef.current = ws;
|
|
6069
|
-
}, [appendMessages, startPolling, stopPolling, fetchNewMessages, resyncConversation]);
|
|
6142
|
+
}, [appendMessages, startPolling, stopPolling, fetchNewMessages, resyncConversation, lastConfirmedMessage]);
|
|
6070
6143
|
const resetConversation = React14.useCallback(() => {
|
|
6071
6144
|
stopPolling();
|
|
6072
6145
|
if (wsReconnectTimerRef.current) {
|
|
@@ -6089,21 +6162,51 @@ function useChat(options = {}) {
|
|
|
6089
6162
|
setError(null);
|
|
6090
6163
|
setClaimedAs(null);
|
|
6091
6164
|
}, [stopPolling, stopAwaitingReply]);
|
|
6165
|
+
const hydrateConversation = React14.useCallback(
|
|
6166
|
+
(value) => {
|
|
6167
|
+
const c = clientRef.current;
|
|
6168
|
+
setMessages(value.messages);
|
|
6169
|
+
setConversationOpened(true);
|
|
6170
|
+
aiEnabledRef.current = value.conversation.ai_enabled;
|
|
6171
|
+
setHumanRequested(value.conversation.needs_human_review ?? false);
|
|
6172
|
+
setSeenAt(value.conversation.team_read_at ?? null);
|
|
6173
|
+
setMayHaveOlder(value.messages.length >= 50);
|
|
6174
|
+
setClaimedAs(c?.support.claimedCustomerName ?? null);
|
|
6175
|
+
connectWebSocket();
|
|
6176
|
+
},
|
|
6177
|
+
[connectWebSocket]
|
|
6178
|
+
);
|
|
6179
|
+
const openIfExisting = React14.useCallback(async () => {
|
|
6180
|
+
const c = clientRef.current;
|
|
6181
|
+
if (!c || openedRef.current) return;
|
|
6182
|
+
openedRef.current = true;
|
|
6183
|
+
setIsLoading(true);
|
|
6184
|
+
const result = await c.support.peekConversation();
|
|
6185
|
+
if (result.ok && result.value) {
|
|
6186
|
+
hydrateConversation(result.value);
|
|
6187
|
+
} else {
|
|
6188
|
+
openedRef.current = false;
|
|
6189
|
+
}
|
|
6190
|
+
setIsLoading(false);
|
|
6191
|
+
}, [hydrateConversation]);
|
|
6092
6192
|
const watchSessionClaims = React14.useCallback(() => {
|
|
6093
6193
|
const c = clientRef.current;
|
|
6094
6194
|
if (!c || claimUnsubRef.current) return;
|
|
6095
|
-
const unsubClaimed = c.events.on(
|
|
6096
|
-
setClaimedAs(event.customerName || null);
|
|
6097
|
-
if (!openedRef.current)
|
|
6195
|
+
const unsubClaimed = c.events.on(chunkQLGZIBRO_js.SESSION_CLAIMED, (event) => {
|
|
6196
|
+
setClaimedAs(event.customerName || c.support.claimedCustomerName || null);
|
|
6197
|
+
if (!openedRef.current) {
|
|
6198
|
+
void openIfExisting();
|
|
6199
|
+
return;
|
|
6200
|
+
}
|
|
6098
6201
|
connectWebSocket();
|
|
6099
6202
|
void resyncConversation();
|
|
6100
6203
|
});
|
|
6101
|
-
const unsubCleared = c.events.on(
|
|
6204
|
+
const unsubCleared = c.events.on(chunkQLGZIBRO_js.SESSION_CLEARED, resetConversation);
|
|
6102
6205
|
claimUnsubRef.current = () => {
|
|
6103
6206
|
unsubClaimed();
|
|
6104
6207
|
unsubCleared();
|
|
6105
6208
|
};
|
|
6106
|
-
}, [connectWebSocket, resyncConversation, resetConversation]);
|
|
6209
|
+
}, [connectWebSocket, resyncConversation, resetConversation, openIfExisting]);
|
|
6107
6210
|
const signOut = React14.useCallback(async () => {
|
|
6108
6211
|
const c = clientRef.current;
|
|
6109
6212
|
if (!c) return false;
|
|
@@ -6111,6 +6214,7 @@ function useChat(options = {}) {
|
|
|
6111
6214
|
return result.ok;
|
|
6112
6215
|
}, []);
|
|
6113
6216
|
React14.useEffect(() => {
|
|
6217
|
+
watchSessionClaims();
|
|
6114
6218
|
return () => {
|
|
6115
6219
|
if (pollRef.current) clearInterval(pollRef.current);
|
|
6116
6220
|
if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
|
|
@@ -6119,6 +6223,7 @@ function useChat(options = {}) {
|
|
|
6119
6223
|
claimUnsubRef.current = null;
|
|
6120
6224
|
if (wsRef.current) {
|
|
6121
6225
|
wsRef.current.onclose = null;
|
|
6226
|
+
wsRef.current.onmessage = null;
|
|
6122
6227
|
wsRef.current.close();
|
|
6123
6228
|
}
|
|
6124
6229
|
};
|
|
@@ -6130,21 +6235,13 @@ function useChat(options = {}) {
|
|
|
6130
6235
|
setError(null);
|
|
6131
6236
|
const result = await client.support.openConversation();
|
|
6132
6237
|
if (result.ok) {
|
|
6133
|
-
|
|
6134
|
-
setConversationOpened(true);
|
|
6135
|
-
aiEnabledRef.current = result.value.conversation.ai_enabled;
|
|
6136
|
-
setHumanRequested(result.value.conversation.needs_human_review ?? false);
|
|
6137
|
-
setSeenAt(result.value.conversation.team_read_at ?? null);
|
|
6138
|
-
setMayHaveOlder(result.value.messages.length >= 50);
|
|
6139
|
-
setClaimedAs(client.support.claimedCustomerName);
|
|
6140
|
-
connectWebSocket();
|
|
6141
|
-
watchSessionClaims();
|
|
6238
|
+
hydrateConversation(result.value);
|
|
6142
6239
|
} else {
|
|
6143
6240
|
openedRef.current = false;
|
|
6144
6241
|
setError(result.error);
|
|
6145
6242
|
}
|
|
6146
6243
|
setIsLoading(false);
|
|
6147
|
-
}, [client,
|
|
6244
|
+
}, [client, hydrateConversation]);
|
|
6148
6245
|
const send = React14.useCallback(
|
|
6149
6246
|
async (content, opts) => {
|
|
6150
6247
|
const attachments = opts?.attachments ?? [];
|
|
@@ -6156,7 +6253,8 @@ function useChat(options = {}) {
|
|
|
6156
6253
|
if (!openedRef.current) return false;
|
|
6157
6254
|
}
|
|
6158
6255
|
const replyTo = opts?.replyTo;
|
|
6159
|
-
const
|
|
6256
|
+
const clientId = chunkVEOAMBEX_js.generateIdempotencyKey();
|
|
6257
|
+
const optimisticId = `opt_${clientId}`;
|
|
6160
6258
|
const optimistic = {
|
|
6161
6259
|
id: optimisticId,
|
|
6162
6260
|
sender_type: "customer",
|
|
@@ -6164,6 +6262,7 @@ function useChat(options = {}) {
|
|
|
6164
6262
|
content_type: attachments.length > 0 ? "image" : "text",
|
|
6165
6263
|
attachments,
|
|
6166
6264
|
metadata: {},
|
|
6265
|
+
client_id: clientId,
|
|
6167
6266
|
// Same shape the server echoes back, so the bubble doesn't reflow
|
|
6168
6267
|
// when the confirmed message replaces it.
|
|
6169
6268
|
reply_to: replyTo ? {
|
|
@@ -6176,6 +6275,7 @@ function useChat(options = {}) {
|
|
|
6176
6275
|
setMessages((prev) => [...prev, optimistic]);
|
|
6177
6276
|
setIsSending(true);
|
|
6178
6277
|
const result = await client.support.sendMessage(content.trim(), {
|
|
6278
|
+
idempotencyKey: clientId,
|
|
6179
6279
|
replyToMessageId: replyTo?.id,
|
|
6180
6280
|
attachments: attachments.length > 0 ? attachments : void 0
|
|
6181
6281
|
});
|
|
@@ -6246,11 +6346,7 @@ function useChat(options = {}) {
|
|
|
6246
6346
|
const older = result.value;
|
|
6247
6347
|
setMayHaveOlder(older.length >= 50);
|
|
6248
6348
|
if (older.length === 0) return false;
|
|
6249
|
-
setMessages((prev) =>
|
|
6250
|
-
const ids = new Set(prev.map((m) => m.id));
|
|
6251
|
-
const fresh = older.filter((m) => !ids.has(m.id));
|
|
6252
|
-
return fresh.length > 0 ? [...fresh, ...prev] : prev;
|
|
6253
|
-
});
|
|
6349
|
+
setMessages((prev) => mergeMessages(prev, older));
|
|
6254
6350
|
return true;
|
|
6255
6351
|
}, []);
|
|
6256
6352
|
const agentArrived = messages.some((m) => m.sender_type === "agent");
|
|
@@ -6270,7 +6366,8 @@ function useChat(options = {}) {
|
|
|
6270
6366
|
startConversation,
|
|
6271
6367
|
react,
|
|
6272
6368
|
requestHuman,
|
|
6273
|
-
loadOlder
|
|
6369
|
+
loadOlder,
|
|
6370
|
+
openIfExisting
|
|
6274
6371
|
};
|
|
6275
6372
|
}
|
|
6276
6373
|
var ElementsContext = React14.createContext({
|
|
@@ -6345,15 +6442,15 @@ function AuthElement({
|
|
|
6345
6442
|
}, [onReady, onAuthenticated, onError]);
|
|
6346
6443
|
React14.useEffect(() => {
|
|
6347
6444
|
if (!elements || !containerRef.current) return;
|
|
6348
|
-
const element = elements.create(
|
|
6445
|
+
const element = elements.create(chunkVEOAMBEX_js.ELEMENT_TYPES.AUTH, { prefillEmail });
|
|
6349
6446
|
elementRef.current = element;
|
|
6350
|
-
element.on(
|
|
6447
|
+
element.on(chunkVEOAMBEX_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
6351
6448
|
element.on(
|
|
6352
|
-
|
|
6449
|
+
chunkVEOAMBEX_js.EVENT_TYPES.AUTHENTICATED,
|
|
6353
6450
|
(data) => onAuthenticatedRef.current?.(data)
|
|
6354
6451
|
);
|
|
6355
6452
|
element.on(
|
|
6356
|
-
|
|
6453
|
+
chunkVEOAMBEX_js.EVENT_TYPES.ERROR,
|
|
6357
6454
|
(data) => onErrorRef.current?.(data)
|
|
6358
6455
|
);
|
|
6359
6456
|
element.mount(containerRef.current);
|
|
@@ -6382,15 +6479,15 @@ function AddressElement({
|
|
|
6382
6479
|
}, [onReady, onChange, onError]);
|
|
6383
6480
|
React14.useEffect(() => {
|
|
6384
6481
|
if (!elements || !containerRef.current) return;
|
|
6385
|
-
const element = elements.create(
|
|
6482
|
+
const element = elements.create(chunkVEOAMBEX_js.ELEMENT_TYPES.ADDRESS, { mode });
|
|
6386
6483
|
elementRef.current = element;
|
|
6387
|
-
element.on(
|
|
6484
|
+
element.on(chunkVEOAMBEX_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
6388
6485
|
element.on(
|
|
6389
|
-
|
|
6486
|
+
chunkVEOAMBEX_js.EVENT_TYPES.CHANGE,
|
|
6390
6487
|
(data) => onChangeRef.current?.(data)
|
|
6391
6488
|
);
|
|
6392
6489
|
element.on(
|
|
6393
|
-
|
|
6490
|
+
chunkVEOAMBEX_js.EVENT_TYPES.ERROR,
|
|
6394
6491
|
(data) => onErrorRef.current?.(data)
|
|
6395
6492
|
);
|
|
6396
6493
|
element.mount(containerRef.current);
|
|
@@ -6420,15 +6517,15 @@ function PaymentElement({
|
|
|
6420
6517
|
}, [onReady, onChange, onError]);
|
|
6421
6518
|
React14.useEffect(() => {
|
|
6422
6519
|
if (!elements || !containerRef.current) return;
|
|
6423
|
-
const element = elements.create(
|
|
6520
|
+
const element = elements.create(chunkVEOAMBEX_js.ELEMENT_TYPES.PAYMENT, { amount, currency });
|
|
6424
6521
|
elementRef.current = element;
|
|
6425
|
-
element.on(
|
|
6522
|
+
element.on(chunkVEOAMBEX_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
6426
6523
|
element.on(
|
|
6427
|
-
|
|
6524
|
+
chunkVEOAMBEX_js.EVENT_TYPES.CHANGE,
|
|
6428
6525
|
(data) => onChangeRef.current?.(data)
|
|
6429
6526
|
);
|
|
6430
6527
|
element.on(
|
|
6431
|
-
|
|
6528
|
+
chunkVEOAMBEX_js.EVENT_TYPES.ERROR,
|
|
6432
6529
|
(data) => onErrorRef.current?.(data)
|
|
6433
6530
|
);
|
|
6434
6531
|
element.mount(containerRef.current);
|
|
@@ -12438,6 +12535,9 @@ function ProductGrid({
|
|
|
12438
12535
|
|
|
12439
12536
|
// src/react/utils/attachment.ts
|
|
12440
12537
|
var IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".svg"];
|
|
12538
|
+
function safeAttachmentUrl(url) {
|
|
12539
|
+
return /^https?:\/\//i.test(url) ? url : null;
|
|
12540
|
+
}
|
|
12441
12541
|
function isImageAttachment(attachment) {
|
|
12442
12542
|
if (attachment.mime_type?.startsWith("image/")) return true;
|
|
12443
12543
|
if (attachment.mime_type) return false;
|
|
@@ -17084,6 +17184,7 @@ function partitionMessages(messages) {
|
|
|
17084
17184
|
}
|
|
17085
17185
|
return { bubbles, reactionsByTarget };
|
|
17086
17186
|
}
|
|
17187
|
+
var MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;
|
|
17087
17188
|
function ChatWidget({
|
|
17088
17189
|
client: clientProp,
|
|
17089
17190
|
businessName = "Support",
|
|
@@ -17100,7 +17201,8 @@ function ChatWidget({
|
|
|
17100
17201
|
const context = useOptionalCimplifyClient();
|
|
17101
17202
|
const client = clientProp ?? context?.client;
|
|
17102
17203
|
const [open, setOpen] = React14.useState(defaultOpen);
|
|
17103
|
-
const [
|
|
17204
|
+
const [readAt, setReadAt] = React14.useState(null);
|
|
17205
|
+
const [attachError, setAttachError] = React14.useState(null);
|
|
17104
17206
|
const {
|
|
17105
17207
|
messages,
|
|
17106
17208
|
isLoading,
|
|
@@ -17113,14 +17215,27 @@ function ChatWidget({
|
|
|
17113
17215
|
signOut,
|
|
17114
17216
|
send,
|
|
17115
17217
|
react,
|
|
17116
|
-
startConversation
|
|
17218
|
+
startConversation,
|
|
17219
|
+
openIfExisting
|
|
17117
17220
|
} = useChat({ client, pollInterval });
|
|
17118
17221
|
const showTyping = isSending || awaitingReply;
|
|
17119
17222
|
const attachPhoto = React14.useCallback(
|
|
17120
17223
|
async (file) => {
|
|
17121
|
-
if (!client
|
|
17224
|
+
if (!client) return;
|
|
17225
|
+
setAttachError(null);
|
|
17226
|
+
if (!file.type.startsWith("image/")) {
|
|
17227
|
+
setAttachError(new chunkAETVRBKI_js.CimplifyError("invalid_attachment", "Only photos can be attached."));
|
|
17228
|
+
return;
|
|
17229
|
+
}
|
|
17230
|
+
if (file.size > MAX_ATTACHMENT_BYTES) {
|
|
17231
|
+
setAttachError(new chunkAETVRBKI_js.CimplifyError("attachment_too_large", "Photos must be under 5MB."));
|
|
17232
|
+
return;
|
|
17233
|
+
}
|
|
17122
17234
|
const uploaded = await client.uploads.upload(file);
|
|
17123
|
-
if (!uploaded.ok)
|
|
17235
|
+
if (!uploaded.ok) {
|
|
17236
|
+
setAttachError(uploaded.error);
|
|
17237
|
+
return;
|
|
17238
|
+
}
|
|
17124
17239
|
const attachment = {
|
|
17125
17240
|
upload_id: uploaded.value.id,
|
|
17126
17241
|
url: uploaded.value.url,
|
|
@@ -17132,14 +17247,28 @@ function ChatWidget({
|
|
|
17132
17247
|
},
|
|
17133
17248
|
[client, send]
|
|
17134
17249
|
);
|
|
17135
|
-
const
|
|
17250
|
+
const handleSend = React14.useCallback(
|
|
17251
|
+
(content) => {
|
|
17252
|
+
setAttachError(null);
|
|
17253
|
+
return send(content);
|
|
17254
|
+
},
|
|
17255
|
+
[send]
|
|
17256
|
+
);
|
|
17257
|
+
const unread = open ? 0 : messages.filter(
|
|
17258
|
+
(m) => m.sender_type !== "customer" && (!readAt || m.created_at > readAt)
|
|
17259
|
+
).length;
|
|
17260
|
+
React14.useState(() => {
|
|
17261
|
+
if (defaultOpen) void openIfExisting();
|
|
17262
|
+
});
|
|
17136
17263
|
const handleOpen = React14.useCallback(() => {
|
|
17137
17264
|
setOpen(true);
|
|
17138
|
-
|
|
17265
|
+
void openIfExisting();
|
|
17266
|
+
}, [openIfExisting]);
|
|
17139
17267
|
const handleClose = React14.useCallback(() => {
|
|
17140
|
-
|
|
17268
|
+
const newest = messages[messages.length - 1];
|
|
17269
|
+
setReadAt(newest?.created_at ?? null);
|
|
17141
17270
|
setOpen(false);
|
|
17142
|
-
}, [messages
|
|
17271
|
+
}, [messages]);
|
|
17143
17272
|
const isLeft = position === "bottom-left";
|
|
17144
17273
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17145
17274
|
"div",
|
|
@@ -17172,13 +17301,13 @@ function ChatWidget({
|
|
|
17172
17301
|
isSending,
|
|
17173
17302
|
isTyping: showTyping,
|
|
17174
17303
|
seenAt,
|
|
17175
|
-
error,
|
|
17304
|
+
error: error ?? attachError,
|
|
17176
17305
|
isActive,
|
|
17177
17306
|
isLeft,
|
|
17178
17307
|
claimedAs,
|
|
17179
17308
|
onSignOut: () => void signOut(),
|
|
17180
17309
|
onClose: handleClose,
|
|
17181
|
-
onSend:
|
|
17310
|
+
onSend: handleSend,
|
|
17182
17311
|
onReact: react,
|
|
17183
17312
|
onAttach: attachPhoto,
|
|
17184
17313
|
onStartConversation: startConversation,
|
|
@@ -17289,7 +17418,7 @@ function Panel({
|
|
|
17289
17418
|
className: classNames?.messages
|
|
17290
17419
|
}
|
|
17291
17420
|
),
|
|
17292
|
-
error != null && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-red-200 bg-red-50 px-4 py-2 text-xs text-red-600", children: "Something went wrong. Please try again." }),
|
|
17421
|
+
error != null && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-red-200 bg-red-50 px-4 py-2 text-xs text-red-600", children: error.userMessage || "Something went wrong. Please try again." }),
|
|
17293
17422
|
(isActive || isLoading) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
17294
17423
|
ChatInput,
|
|
17295
17424
|
{
|
|
@@ -17499,11 +17628,13 @@ function MessageList({
|
|
|
17499
17628
|
]
|
|
17500
17629
|
}
|
|
17501
17630
|
),
|
|
17502
|
-
msg.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-wrap gap-1.5", msg.content && "mb-1.5"), children: msg.attachments.map(
|
|
17503
|
-
|
|
17631
|
+
msg.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-wrap gap-1.5", msg.content && "mb-1.5"), children: msg.attachments.map((attachment, index) => {
|
|
17632
|
+
const url = safeAttachmentUrl(attachment.url);
|
|
17633
|
+
if (!url) return null;
|
|
17634
|
+
return attachment.mime_type?.startsWith("image/") ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: url, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
17504
17635
|
"img",
|
|
17505
17636
|
{
|
|
17506
|
-
src:
|
|
17637
|
+
src: url,
|
|
17507
17638
|
alt: attachment.filename ?? "Photo",
|
|
17508
17639
|
loading: "lazy",
|
|
17509
17640
|
className: "max-h-48 max-w-full rounded-lg object-cover"
|
|
@@ -17511,15 +17642,15 @@ function MessageList({
|
|
|
17511
17642
|
) }, index) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
17512
17643
|
"a",
|
|
17513
17644
|
{
|
|
17514
|
-
href:
|
|
17645
|
+
href: url,
|
|
17515
17646
|
target: "_blank",
|
|
17516
17647
|
rel: "noreferrer",
|
|
17517
17648
|
className: "truncate text-xs underline",
|
|
17518
17649
|
children: attachment.filename ?? "Attachment"
|
|
17519
17650
|
},
|
|
17520
17651
|
index
|
|
17521
|
-
)
|
|
17522
|
-
) }),
|
|
17652
|
+
);
|
|
17653
|
+
}) }),
|
|
17523
17654
|
isCustomer ? msg.content && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap", children: msg.content }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
17524
17655
|
ChatMarkdown,
|
|
17525
17656
|
{
|
|
@@ -19810,6 +19941,7 @@ exports.prefetchProduct = prefetchProduct;
|
|
|
19810
19941
|
exports.prefetchProducts = prefetchProducts;
|
|
19811
19942
|
exports.resolveBoundaryKind = resolveBoundaryKind;
|
|
19812
19943
|
exports.roomToResource = roomToResource;
|
|
19944
|
+
exports.safeAttachmentUrl = safeAttachmentUrl;
|
|
19813
19945
|
exports.sanitizeRichTextHtml = sanitizeRichTextHtml;
|
|
19814
19946
|
exports.stripHtmlToText = stripHtmlToText;
|
|
19815
19947
|
exports.toAccountLineItem = toAccountLineItem;
|