@cimplify/sdk 0.92.0 → 0.93.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 +13 -5
- package/dist/react.d.ts +13 -5
- package/dist/react.js +174 -75
- package/dist/react.mjs +156 -58
- 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);
|
|
@@ -1145,7 +1145,7 @@ var DEFAULT_COUNTRY = "US";
|
|
|
1145
1145
|
function createDefaultClient() {
|
|
1146
1146
|
const processRef = globalThis.process;
|
|
1147
1147
|
const envPublicKey = processRef?.env?.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY || "";
|
|
1148
|
-
return
|
|
1148
|
+
return chunkQLGZIBRO_js.createCimplifyClient({ publicKey: envPublicKey });
|
|
1149
1149
|
}
|
|
1150
1150
|
function getStoredLocationId() {
|
|
1151
1151
|
if (typeof window === "undefined" || !window.localStorage) {
|
|
@@ -3457,7 +3457,7 @@ function CimplifySignInButton({
|
|
|
3457
3457
|
const onClick = async () => {
|
|
3458
3458
|
if (busy || disabled || !signInOpts.clientId) return;
|
|
3459
3459
|
setBusy(true);
|
|
3460
|
-
await
|
|
3460
|
+
await chunkVEOAMBEX_js.startSignIn({
|
|
3461
3461
|
...signInOpts,
|
|
3462
3462
|
onSuccess: () => {
|
|
3463
3463
|
setBusy(false);
|
|
@@ -5913,6 +5913,20 @@ var IDLE_TIMEOUT_MS = 6e4;
|
|
|
5913
5913
|
var WS_MAX_RECONNECT_ATTEMPTS = 10;
|
|
5914
5914
|
var WS_PATH = "/api/v1/support/conversation/ws";
|
|
5915
5915
|
var AWAIT_REPLY_TIMEOUT_MS = 3e4;
|
|
5916
|
+
function mergeMessages(prev, incoming) {
|
|
5917
|
+
const ids = new Set(prev.map((m) => m.id));
|
|
5918
|
+
const fresh = incoming.filter((m) => !ids.has(m.id));
|
|
5919
|
+
if (fresh.length === 0) return prev;
|
|
5920
|
+
const merged = [...prev, ...fresh].sort(
|
|
5921
|
+
(a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
|
|
5922
|
+
);
|
|
5923
|
+
const confirmed = new Set(
|
|
5924
|
+
merged.filter((m) => !m.id.startsWith("opt_") && m.client_id).map((m) => m.client_id)
|
|
5925
|
+
);
|
|
5926
|
+
return merged.filter(
|
|
5927
|
+
(m) => !(m.id.startsWith("opt_") && m.client_id && confirmed.has(m.client_id))
|
|
5928
|
+
);
|
|
5929
|
+
}
|
|
5916
5930
|
function useChat(options = {}) {
|
|
5917
5931
|
const context = useOptionalCimplifyClient();
|
|
5918
5932
|
const client = options.client ?? context?.client;
|
|
@@ -5965,18 +5979,22 @@ function useChat(options = {}) {
|
|
|
5965
5979
|
if (incoming.some((m) => m.sender_type === "bot" || m.sender_type === "agent")) {
|
|
5966
5980
|
stopAwaitingReply();
|
|
5967
5981
|
}
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
const fresh = incoming.filter((m) => !ids.has(m.id));
|
|
5971
|
-
return fresh.length > 0 ? [...prev, ...fresh] : prev;
|
|
5972
|
-
});
|
|
5982
|
+
if (incoming.length > 0) lastActivityRef.current = Date.now();
|
|
5983
|
+
setMessages((prev) => mergeMessages(prev, incoming));
|
|
5973
5984
|
},
|
|
5974
5985
|
[stopAwaitingReply]
|
|
5975
5986
|
);
|
|
5987
|
+
const lastConfirmedMessage = React14.useCallback(() => {
|
|
5988
|
+
const list = messagesRef.current;
|
|
5989
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
5990
|
+
if (!list[i].id.startsWith("opt_")) return list[i];
|
|
5991
|
+
}
|
|
5992
|
+
return void 0;
|
|
5993
|
+
}, []);
|
|
5976
5994
|
const fetchNewMessages = React14.useCallback(async () => {
|
|
5977
5995
|
const c = clientRef.current;
|
|
5978
5996
|
if (!c) return;
|
|
5979
|
-
const last =
|
|
5997
|
+
const last = lastConfirmedMessage();
|
|
5980
5998
|
const result = await c.support.getMessages({
|
|
5981
5999
|
after: last?.created_at,
|
|
5982
6000
|
limit: 50
|
|
@@ -5984,16 +6002,23 @@ function useChat(options = {}) {
|
|
|
5984
6002
|
if (result.ok && result.value.length > 0) {
|
|
5985
6003
|
appendMessages(result.value);
|
|
5986
6004
|
}
|
|
5987
|
-
}, [appendMessages]);
|
|
5988
|
-
const pollMessages = React14.useCallback(async () => {
|
|
5989
|
-
if (Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS) return;
|
|
5990
|
-
await fetchNewMessages();
|
|
5991
|
-
}, [fetchNewMessages]);
|
|
6005
|
+
}, [appendMessages, lastConfirmedMessage]);
|
|
5992
6006
|
const startPolling = React14.useCallback(() => {
|
|
5993
6007
|
if (pollRef.current) clearInterval(pollRef.current);
|
|
5994
|
-
const
|
|
5995
|
-
|
|
5996
|
-
|
|
6008
|
+
const idle = () => Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS;
|
|
6009
|
+
let current = idle() ? pollIdleInterval : pollInterval;
|
|
6010
|
+
const tick = () => {
|
|
6011
|
+
void fetchNewMessages().then(() => {
|
|
6012
|
+
const desired = idle() ? pollIdleInterval : pollInterval;
|
|
6013
|
+
if (desired !== current && pollRef.current) {
|
|
6014
|
+
current = desired;
|
|
6015
|
+
clearInterval(pollRef.current);
|
|
6016
|
+
pollRef.current = setInterval(tick, current);
|
|
6017
|
+
}
|
|
6018
|
+
});
|
|
6019
|
+
};
|
|
6020
|
+
pollRef.current = setInterval(tick, current);
|
|
6021
|
+
}, [fetchNewMessages, pollInterval, pollIdleInterval]);
|
|
5997
6022
|
const stopPolling = React14.useCallback(() => {
|
|
5998
6023
|
if (pollRef.current) {
|
|
5999
6024
|
clearInterval(pollRef.current);
|
|
@@ -6005,7 +6030,15 @@ function useChat(options = {}) {
|
|
|
6005
6030
|
if (!c || !openedRef.current) return;
|
|
6006
6031
|
const result = await c.support.openConversation();
|
|
6007
6032
|
if (!result.ok) return;
|
|
6008
|
-
|
|
6033
|
+
const snapshot = result.value.messages;
|
|
6034
|
+
setMessages((prev) => {
|
|
6035
|
+
const snapshotIds = new Set(snapshot.map((m) => m.id));
|
|
6036
|
+
const tail = snapshot.length > 0 ? new Date(snapshot[snapshot.length - 1].created_at).getTime() : 0;
|
|
6037
|
+
const extras = prev.filter(
|
|
6038
|
+
(m) => !snapshotIds.has(m.id) && (m.id.startsWith("opt_") || new Date(m.created_at).getTime() > tail)
|
|
6039
|
+
);
|
|
6040
|
+
return mergeMessages(snapshot, extras);
|
|
6041
|
+
});
|
|
6009
6042
|
aiEnabledRef.current = result.value.conversation.ai_enabled;
|
|
6010
6043
|
setHumanRequested(result.value.conversation.needs_human_review ?? false);
|
|
6011
6044
|
setSeenAt(result.value.conversation.team_read_at ?? null);
|
|
@@ -6017,7 +6050,13 @@ function useChat(options = {}) {
|
|
|
6017
6050
|
startPolling();
|
|
6018
6051
|
return;
|
|
6019
6052
|
}
|
|
6053
|
+
if (wsReconnectTimerRef.current) {
|
|
6054
|
+
clearTimeout(wsReconnectTimerRef.current);
|
|
6055
|
+
wsReconnectTimerRef.current = null;
|
|
6056
|
+
}
|
|
6020
6057
|
if (wsRef.current) {
|
|
6058
|
+
wsRef.current.onclose = null;
|
|
6059
|
+
wsRef.current.onmessage = null;
|
|
6021
6060
|
wsRef.current.close();
|
|
6022
6061
|
wsRef.current = null;
|
|
6023
6062
|
}
|
|
@@ -6041,7 +6080,7 @@ function useChat(options = {}) {
|
|
|
6041
6080
|
else stopAwaitingReply();
|
|
6042
6081
|
} else if (envelope.type === "resync") {
|
|
6043
6082
|
const latest = envelope.latest_message_at;
|
|
6044
|
-
const last =
|
|
6083
|
+
const last = lastConfirmedMessage();
|
|
6045
6084
|
if (latest && (!last || new Date(last.created_at).getTime() < new Date(latest).getTime())) {
|
|
6046
6085
|
fetchNewMessages();
|
|
6047
6086
|
}
|
|
@@ -6058,6 +6097,7 @@ function useChat(options = {}) {
|
|
|
6058
6097
|
if (wsReconnectAttemptRef.current < WS_MAX_RECONNECT_ATTEMPTS) {
|
|
6059
6098
|
const attempt = wsReconnectAttemptRef.current++;
|
|
6060
6099
|
const delay = Math.min(1e3 * Math.pow(2, attempt) + Math.random() * 500, 3e4);
|
|
6100
|
+
if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
|
|
6061
6101
|
wsReconnectTimerRef.current = setTimeout(connectWebSocket, delay);
|
|
6062
6102
|
} else {
|
|
6063
6103
|
startPolling();
|
|
@@ -6066,7 +6106,7 @@ function useChat(options = {}) {
|
|
|
6066
6106
|
ws.onerror = () => {
|
|
6067
6107
|
};
|
|
6068
6108
|
wsRef.current = ws;
|
|
6069
|
-
}, [appendMessages, startPolling, stopPolling, fetchNewMessages, resyncConversation]);
|
|
6109
|
+
}, [appendMessages, startPolling, stopPolling, fetchNewMessages, resyncConversation, lastConfirmedMessage]);
|
|
6070
6110
|
const resetConversation = React14.useCallback(() => {
|
|
6071
6111
|
stopPolling();
|
|
6072
6112
|
if (wsReconnectTimerRef.current) {
|
|
@@ -6089,21 +6129,51 @@ function useChat(options = {}) {
|
|
|
6089
6129
|
setError(null);
|
|
6090
6130
|
setClaimedAs(null);
|
|
6091
6131
|
}, [stopPolling, stopAwaitingReply]);
|
|
6132
|
+
const hydrateConversation = React14.useCallback(
|
|
6133
|
+
(value) => {
|
|
6134
|
+
const c = clientRef.current;
|
|
6135
|
+
setMessages(value.messages);
|
|
6136
|
+
setConversationOpened(true);
|
|
6137
|
+
aiEnabledRef.current = value.conversation.ai_enabled;
|
|
6138
|
+
setHumanRequested(value.conversation.needs_human_review ?? false);
|
|
6139
|
+
setSeenAt(value.conversation.team_read_at ?? null);
|
|
6140
|
+
setMayHaveOlder(value.messages.length >= 50);
|
|
6141
|
+
setClaimedAs(c?.support.claimedCustomerName ?? null);
|
|
6142
|
+
connectWebSocket();
|
|
6143
|
+
},
|
|
6144
|
+
[connectWebSocket]
|
|
6145
|
+
);
|
|
6146
|
+
const openIfExisting = React14.useCallback(async () => {
|
|
6147
|
+
const c = clientRef.current;
|
|
6148
|
+
if (!c || openedRef.current) return;
|
|
6149
|
+
openedRef.current = true;
|
|
6150
|
+
setIsLoading(true);
|
|
6151
|
+
const result = await c.support.peekConversation();
|
|
6152
|
+
if (result.ok && result.value) {
|
|
6153
|
+
hydrateConversation(result.value);
|
|
6154
|
+
} else {
|
|
6155
|
+
openedRef.current = false;
|
|
6156
|
+
}
|
|
6157
|
+
setIsLoading(false);
|
|
6158
|
+
}, [hydrateConversation]);
|
|
6092
6159
|
const watchSessionClaims = React14.useCallback(() => {
|
|
6093
6160
|
const c = clientRef.current;
|
|
6094
6161
|
if (!c || claimUnsubRef.current) return;
|
|
6095
|
-
const unsubClaimed = c.events.on(
|
|
6096
|
-
setClaimedAs(event.customerName || null);
|
|
6097
|
-
if (!openedRef.current)
|
|
6162
|
+
const unsubClaimed = c.events.on(chunkQLGZIBRO_js.SESSION_CLAIMED, (event) => {
|
|
6163
|
+
setClaimedAs(event.customerName || c.support.claimedCustomerName || null);
|
|
6164
|
+
if (!openedRef.current) {
|
|
6165
|
+
void openIfExisting();
|
|
6166
|
+
return;
|
|
6167
|
+
}
|
|
6098
6168
|
connectWebSocket();
|
|
6099
6169
|
void resyncConversation();
|
|
6100
6170
|
});
|
|
6101
|
-
const unsubCleared = c.events.on(
|
|
6171
|
+
const unsubCleared = c.events.on(chunkQLGZIBRO_js.SESSION_CLEARED, resetConversation);
|
|
6102
6172
|
claimUnsubRef.current = () => {
|
|
6103
6173
|
unsubClaimed();
|
|
6104
6174
|
unsubCleared();
|
|
6105
6175
|
};
|
|
6106
|
-
}, [connectWebSocket, resyncConversation, resetConversation]);
|
|
6176
|
+
}, [connectWebSocket, resyncConversation, resetConversation, openIfExisting]);
|
|
6107
6177
|
const signOut = React14.useCallback(async () => {
|
|
6108
6178
|
const c = clientRef.current;
|
|
6109
6179
|
if (!c) return false;
|
|
@@ -6111,6 +6181,7 @@ function useChat(options = {}) {
|
|
|
6111
6181
|
return result.ok;
|
|
6112
6182
|
}, []);
|
|
6113
6183
|
React14.useEffect(() => {
|
|
6184
|
+
watchSessionClaims();
|
|
6114
6185
|
return () => {
|
|
6115
6186
|
if (pollRef.current) clearInterval(pollRef.current);
|
|
6116
6187
|
if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
|
|
@@ -6119,6 +6190,7 @@ function useChat(options = {}) {
|
|
|
6119
6190
|
claimUnsubRef.current = null;
|
|
6120
6191
|
if (wsRef.current) {
|
|
6121
6192
|
wsRef.current.onclose = null;
|
|
6193
|
+
wsRef.current.onmessage = null;
|
|
6122
6194
|
wsRef.current.close();
|
|
6123
6195
|
}
|
|
6124
6196
|
};
|
|
@@ -6130,21 +6202,13 @@ function useChat(options = {}) {
|
|
|
6130
6202
|
setError(null);
|
|
6131
6203
|
const result = await client.support.openConversation();
|
|
6132
6204
|
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();
|
|
6205
|
+
hydrateConversation(result.value);
|
|
6142
6206
|
} else {
|
|
6143
6207
|
openedRef.current = false;
|
|
6144
6208
|
setError(result.error);
|
|
6145
6209
|
}
|
|
6146
6210
|
setIsLoading(false);
|
|
6147
|
-
}, [client,
|
|
6211
|
+
}, [client, hydrateConversation]);
|
|
6148
6212
|
const send = React14.useCallback(
|
|
6149
6213
|
async (content, opts) => {
|
|
6150
6214
|
const attachments = opts?.attachments ?? [];
|
|
@@ -6156,7 +6220,8 @@ function useChat(options = {}) {
|
|
|
6156
6220
|
if (!openedRef.current) return false;
|
|
6157
6221
|
}
|
|
6158
6222
|
const replyTo = opts?.replyTo;
|
|
6159
|
-
const
|
|
6223
|
+
const clientId = chunkVEOAMBEX_js.generateIdempotencyKey();
|
|
6224
|
+
const optimisticId = `opt_${clientId}`;
|
|
6160
6225
|
const optimistic = {
|
|
6161
6226
|
id: optimisticId,
|
|
6162
6227
|
sender_type: "customer",
|
|
@@ -6164,6 +6229,7 @@ function useChat(options = {}) {
|
|
|
6164
6229
|
content_type: attachments.length > 0 ? "image" : "text",
|
|
6165
6230
|
attachments,
|
|
6166
6231
|
metadata: {},
|
|
6232
|
+
client_id: clientId,
|
|
6167
6233
|
// Same shape the server echoes back, so the bubble doesn't reflow
|
|
6168
6234
|
// when the confirmed message replaces it.
|
|
6169
6235
|
reply_to: replyTo ? {
|
|
@@ -6176,6 +6242,7 @@ function useChat(options = {}) {
|
|
|
6176
6242
|
setMessages((prev) => [...prev, optimistic]);
|
|
6177
6243
|
setIsSending(true);
|
|
6178
6244
|
const result = await client.support.sendMessage(content.trim(), {
|
|
6245
|
+
idempotencyKey: clientId,
|
|
6179
6246
|
replyToMessageId: replyTo?.id,
|
|
6180
6247
|
attachments: attachments.length > 0 ? attachments : void 0
|
|
6181
6248
|
});
|
|
@@ -6246,11 +6313,7 @@ function useChat(options = {}) {
|
|
|
6246
6313
|
const older = result.value;
|
|
6247
6314
|
setMayHaveOlder(older.length >= 50);
|
|
6248
6315
|
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
|
-
});
|
|
6316
|
+
setMessages((prev) => mergeMessages(prev, older));
|
|
6254
6317
|
return true;
|
|
6255
6318
|
}, []);
|
|
6256
6319
|
const agentArrived = messages.some((m) => m.sender_type === "agent");
|
|
@@ -6270,7 +6333,8 @@ function useChat(options = {}) {
|
|
|
6270
6333
|
startConversation,
|
|
6271
6334
|
react,
|
|
6272
6335
|
requestHuman,
|
|
6273
|
-
loadOlder
|
|
6336
|
+
loadOlder,
|
|
6337
|
+
openIfExisting
|
|
6274
6338
|
};
|
|
6275
6339
|
}
|
|
6276
6340
|
var ElementsContext = React14.createContext({
|
|
@@ -6345,15 +6409,15 @@ function AuthElement({
|
|
|
6345
6409
|
}, [onReady, onAuthenticated, onError]);
|
|
6346
6410
|
React14.useEffect(() => {
|
|
6347
6411
|
if (!elements || !containerRef.current) return;
|
|
6348
|
-
const element = elements.create(
|
|
6412
|
+
const element = elements.create(chunkVEOAMBEX_js.ELEMENT_TYPES.AUTH, { prefillEmail });
|
|
6349
6413
|
elementRef.current = element;
|
|
6350
|
-
element.on(
|
|
6414
|
+
element.on(chunkVEOAMBEX_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
6351
6415
|
element.on(
|
|
6352
|
-
|
|
6416
|
+
chunkVEOAMBEX_js.EVENT_TYPES.AUTHENTICATED,
|
|
6353
6417
|
(data) => onAuthenticatedRef.current?.(data)
|
|
6354
6418
|
);
|
|
6355
6419
|
element.on(
|
|
6356
|
-
|
|
6420
|
+
chunkVEOAMBEX_js.EVENT_TYPES.ERROR,
|
|
6357
6421
|
(data) => onErrorRef.current?.(data)
|
|
6358
6422
|
);
|
|
6359
6423
|
element.mount(containerRef.current);
|
|
@@ -6382,15 +6446,15 @@ function AddressElement({
|
|
|
6382
6446
|
}, [onReady, onChange, onError]);
|
|
6383
6447
|
React14.useEffect(() => {
|
|
6384
6448
|
if (!elements || !containerRef.current) return;
|
|
6385
|
-
const element = elements.create(
|
|
6449
|
+
const element = elements.create(chunkVEOAMBEX_js.ELEMENT_TYPES.ADDRESS, { mode });
|
|
6386
6450
|
elementRef.current = element;
|
|
6387
|
-
element.on(
|
|
6451
|
+
element.on(chunkVEOAMBEX_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
6388
6452
|
element.on(
|
|
6389
|
-
|
|
6453
|
+
chunkVEOAMBEX_js.EVENT_TYPES.CHANGE,
|
|
6390
6454
|
(data) => onChangeRef.current?.(data)
|
|
6391
6455
|
);
|
|
6392
6456
|
element.on(
|
|
6393
|
-
|
|
6457
|
+
chunkVEOAMBEX_js.EVENT_TYPES.ERROR,
|
|
6394
6458
|
(data) => onErrorRef.current?.(data)
|
|
6395
6459
|
);
|
|
6396
6460
|
element.mount(containerRef.current);
|
|
@@ -6420,15 +6484,15 @@ function PaymentElement({
|
|
|
6420
6484
|
}, [onReady, onChange, onError]);
|
|
6421
6485
|
React14.useEffect(() => {
|
|
6422
6486
|
if (!elements || !containerRef.current) return;
|
|
6423
|
-
const element = elements.create(
|
|
6487
|
+
const element = elements.create(chunkVEOAMBEX_js.ELEMENT_TYPES.PAYMENT, { amount, currency });
|
|
6424
6488
|
elementRef.current = element;
|
|
6425
|
-
element.on(
|
|
6489
|
+
element.on(chunkVEOAMBEX_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
6426
6490
|
element.on(
|
|
6427
|
-
|
|
6491
|
+
chunkVEOAMBEX_js.EVENT_TYPES.CHANGE,
|
|
6428
6492
|
(data) => onChangeRef.current?.(data)
|
|
6429
6493
|
);
|
|
6430
6494
|
element.on(
|
|
6431
|
-
|
|
6495
|
+
chunkVEOAMBEX_js.EVENT_TYPES.ERROR,
|
|
6432
6496
|
(data) => onErrorRef.current?.(data)
|
|
6433
6497
|
);
|
|
6434
6498
|
element.mount(containerRef.current);
|
|
@@ -12438,6 +12502,9 @@ function ProductGrid({
|
|
|
12438
12502
|
|
|
12439
12503
|
// src/react/utils/attachment.ts
|
|
12440
12504
|
var IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".svg"];
|
|
12505
|
+
function safeAttachmentUrl(url) {
|
|
12506
|
+
return /^https?:\/\//i.test(url) ? url : null;
|
|
12507
|
+
}
|
|
12441
12508
|
function isImageAttachment(attachment) {
|
|
12442
12509
|
if (attachment.mime_type?.startsWith("image/")) return true;
|
|
12443
12510
|
if (attachment.mime_type) return false;
|
|
@@ -17084,6 +17151,7 @@ function partitionMessages(messages) {
|
|
|
17084
17151
|
}
|
|
17085
17152
|
return { bubbles, reactionsByTarget };
|
|
17086
17153
|
}
|
|
17154
|
+
var MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;
|
|
17087
17155
|
function ChatWidget({
|
|
17088
17156
|
client: clientProp,
|
|
17089
17157
|
businessName = "Support",
|
|
@@ -17100,7 +17168,8 @@ function ChatWidget({
|
|
|
17100
17168
|
const context = useOptionalCimplifyClient();
|
|
17101
17169
|
const client = clientProp ?? context?.client;
|
|
17102
17170
|
const [open, setOpen] = React14.useState(defaultOpen);
|
|
17103
|
-
const [
|
|
17171
|
+
const [readAt, setReadAt] = React14.useState(null);
|
|
17172
|
+
const [attachError, setAttachError] = React14.useState(null);
|
|
17104
17173
|
const {
|
|
17105
17174
|
messages,
|
|
17106
17175
|
isLoading,
|
|
@@ -17113,14 +17182,27 @@ function ChatWidget({
|
|
|
17113
17182
|
signOut,
|
|
17114
17183
|
send,
|
|
17115
17184
|
react,
|
|
17116
|
-
startConversation
|
|
17185
|
+
startConversation,
|
|
17186
|
+
openIfExisting
|
|
17117
17187
|
} = useChat({ client, pollInterval });
|
|
17118
17188
|
const showTyping = isSending || awaitingReply;
|
|
17119
17189
|
const attachPhoto = React14.useCallback(
|
|
17120
17190
|
async (file) => {
|
|
17121
|
-
if (!client
|
|
17191
|
+
if (!client) return;
|
|
17192
|
+
setAttachError(null);
|
|
17193
|
+
if (!file.type.startsWith("image/")) {
|
|
17194
|
+
setAttachError(new chunkAETVRBKI_js.CimplifyError("invalid_attachment", "Only photos can be attached."));
|
|
17195
|
+
return;
|
|
17196
|
+
}
|
|
17197
|
+
if (file.size > MAX_ATTACHMENT_BYTES) {
|
|
17198
|
+
setAttachError(new chunkAETVRBKI_js.CimplifyError("attachment_too_large", "Photos must be under 5MB."));
|
|
17199
|
+
return;
|
|
17200
|
+
}
|
|
17122
17201
|
const uploaded = await client.uploads.upload(file);
|
|
17123
|
-
if (!uploaded.ok)
|
|
17202
|
+
if (!uploaded.ok) {
|
|
17203
|
+
setAttachError(uploaded.error);
|
|
17204
|
+
return;
|
|
17205
|
+
}
|
|
17124
17206
|
const attachment = {
|
|
17125
17207
|
upload_id: uploaded.value.id,
|
|
17126
17208
|
url: uploaded.value.url,
|
|
@@ -17132,14 +17214,28 @@ function ChatWidget({
|
|
|
17132
17214
|
},
|
|
17133
17215
|
[client, send]
|
|
17134
17216
|
);
|
|
17135
|
-
const
|
|
17217
|
+
const handleSend = React14.useCallback(
|
|
17218
|
+
(content) => {
|
|
17219
|
+
setAttachError(null);
|
|
17220
|
+
return send(content);
|
|
17221
|
+
},
|
|
17222
|
+
[send]
|
|
17223
|
+
);
|
|
17224
|
+
const unread = open ? 0 : messages.filter(
|
|
17225
|
+
(m) => m.sender_type !== "customer" && (!readAt || m.created_at > readAt)
|
|
17226
|
+
).length;
|
|
17227
|
+
React14.useState(() => {
|
|
17228
|
+
if (defaultOpen) void openIfExisting();
|
|
17229
|
+
});
|
|
17136
17230
|
const handleOpen = React14.useCallback(() => {
|
|
17137
17231
|
setOpen(true);
|
|
17138
|
-
|
|
17232
|
+
void openIfExisting();
|
|
17233
|
+
}, [openIfExisting]);
|
|
17139
17234
|
const handleClose = React14.useCallback(() => {
|
|
17140
|
-
|
|
17235
|
+
const newest = messages[messages.length - 1];
|
|
17236
|
+
setReadAt(newest?.created_at ?? null);
|
|
17141
17237
|
setOpen(false);
|
|
17142
|
-
}, [messages
|
|
17238
|
+
}, [messages]);
|
|
17143
17239
|
const isLeft = position === "bottom-left";
|
|
17144
17240
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17145
17241
|
"div",
|
|
@@ -17172,13 +17268,13 @@ function ChatWidget({
|
|
|
17172
17268
|
isSending,
|
|
17173
17269
|
isTyping: showTyping,
|
|
17174
17270
|
seenAt,
|
|
17175
|
-
error,
|
|
17271
|
+
error: error ?? attachError,
|
|
17176
17272
|
isActive,
|
|
17177
17273
|
isLeft,
|
|
17178
17274
|
claimedAs,
|
|
17179
17275
|
onSignOut: () => void signOut(),
|
|
17180
17276
|
onClose: handleClose,
|
|
17181
|
-
onSend:
|
|
17277
|
+
onSend: handleSend,
|
|
17182
17278
|
onReact: react,
|
|
17183
17279
|
onAttach: attachPhoto,
|
|
17184
17280
|
onStartConversation: startConversation,
|
|
@@ -17289,7 +17385,7 @@ function Panel({
|
|
|
17289
17385
|
className: classNames?.messages
|
|
17290
17386
|
}
|
|
17291
17387
|
),
|
|
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." }),
|
|
17388
|
+
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
17389
|
(isActive || isLoading) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
17294
17390
|
ChatInput,
|
|
17295
17391
|
{
|
|
@@ -17499,11 +17595,13 @@ function MessageList({
|
|
|
17499
17595
|
]
|
|
17500
17596
|
}
|
|
17501
17597
|
),
|
|
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
|
-
|
|
17598
|
+
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) => {
|
|
17599
|
+
const url = safeAttachmentUrl(attachment.url);
|
|
17600
|
+
if (!url) return null;
|
|
17601
|
+
return attachment.mime_type?.startsWith("image/") ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: url, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
17504
17602
|
"img",
|
|
17505
17603
|
{
|
|
17506
|
-
src:
|
|
17604
|
+
src: url,
|
|
17507
17605
|
alt: attachment.filename ?? "Photo",
|
|
17508
17606
|
loading: "lazy",
|
|
17509
17607
|
className: "max-h-48 max-w-full rounded-lg object-cover"
|
|
@@ -17511,15 +17609,15 @@ function MessageList({
|
|
|
17511
17609
|
) }, index) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
17512
17610
|
"a",
|
|
17513
17611
|
{
|
|
17514
|
-
href:
|
|
17612
|
+
href: url,
|
|
17515
17613
|
target: "_blank",
|
|
17516
17614
|
rel: "noreferrer",
|
|
17517
17615
|
className: "truncate text-xs underline",
|
|
17518
17616
|
children: attachment.filename ?? "Attachment"
|
|
17519
17617
|
},
|
|
17520
17618
|
index
|
|
17521
|
-
)
|
|
17522
|
-
) }),
|
|
17619
|
+
);
|
|
17620
|
+
}) }),
|
|
17523
17621
|
isCustomer ? msg.content && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap", children: msg.content }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
17524
17622
|
ChatMarkdown,
|
|
17525
17623
|
{
|
|
@@ -19810,6 +19908,7 @@ exports.prefetchProduct = prefetchProduct;
|
|
|
19810
19908
|
exports.prefetchProducts = prefetchProducts;
|
|
19811
19909
|
exports.resolveBoundaryKind = resolveBoundaryKind;
|
|
19812
19910
|
exports.roomToResource = roomToResource;
|
|
19911
|
+
exports.safeAttachmentUrl = safeAttachmentUrl;
|
|
19813
19912
|
exports.sanitizeRichTextHtml = sanitizeRichTextHtml;
|
|
19814
19913
|
exports.stripHtmlToText = stripHtmlToText;
|
|
19815
19914
|
exports.toAccountLineItem = toAccountLineItem;
|