@bootdesk/js-web-adapter-react 0.3.4 → 0.3.5
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/index.cjs +166 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +166 -35
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -599,29 +599,101 @@ function useAttachmentUpload(uploadConfig) {
|
|
|
599
599
|
|
|
600
600
|
// src/hooks/useBridge.ts
|
|
601
601
|
var import_react7 = require("react");
|
|
602
|
+
function getBridge() {
|
|
603
|
+
return typeof window !== "undefined" ? window.__chatBridge : null;
|
|
604
|
+
}
|
|
605
|
+
function hasNativeBridge() {
|
|
606
|
+
if (typeof window === "undefined") return false;
|
|
607
|
+
return !!getBridge() || !!window.webkit?.messageHandlers?.chatBridge || !!window.ReactNativeWebView || !!window.AndroidBridge;
|
|
608
|
+
}
|
|
609
|
+
function bridgeSend(msg) {
|
|
610
|
+
const bridge = getBridge();
|
|
611
|
+
if (bridge?.send) {
|
|
612
|
+
bridge.send(msg);
|
|
613
|
+
} else if (typeof window !== "undefined") {
|
|
614
|
+
window.parent.postMessage(msg, "*");
|
|
615
|
+
}
|
|
616
|
+
}
|
|
602
617
|
function useBridge() {
|
|
603
618
|
const notificationCbRef = (0, import_react7.useRef)(null);
|
|
604
619
|
const [config, setConfig] = (0, import_react7.useState)(null);
|
|
620
|
+
const [pushState, setPushState] = (0, import_react7.useState)(null);
|
|
605
621
|
const isInIframe = typeof window !== "undefined" && window !== window.parent;
|
|
622
|
+
const isInWebView = !isInIframe && hasNativeBridge();
|
|
606
623
|
const notifyMessage = (0, import_react7.useCallback)(
|
|
607
624
|
(text) => {
|
|
608
|
-
if (!isInIframe) return;
|
|
609
|
-
|
|
625
|
+
if (!isInIframe && !isInWebView) return;
|
|
626
|
+
if (isInWebView) {
|
|
627
|
+
bridgeSend({ type: "chat-message", text });
|
|
628
|
+
} else {
|
|
629
|
+
window.parent.postMessage({ type: "chat-message", text }, "*");
|
|
630
|
+
}
|
|
610
631
|
},
|
|
611
|
-
[isInIframe]
|
|
632
|
+
[isInIframe, isInWebView]
|
|
612
633
|
);
|
|
613
634
|
const notifyViewportConfig = (0, import_react7.useCallback)(
|
|
614
635
|
(viewportContent) => {
|
|
615
|
-
if (!isInIframe) return;
|
|
616
|
-
|
|
636
|
+
if (!isInIframe && !isInWebView) return;
|
|
637
|
+
if (isInWebView) {
|
|
638
|
+
bridgeSend({ type: "chat-viewport-config", content: viewportContent });
|
|
639
|
+
} else {
|
|
640
|
+
window.parent.postMessage({ type: "chat-viewport-config", content: viewportContent }, "*");
|
|
641
|
+
}
|
|
617
642
|
},
|
|
618
|
-
[isInIframe]
|
|
643
|
+
[isInIframe, isInWebView]
|
|
619
644
|
);
|
|
620
645
|
const onNotificationClicked = (0, import_react7.useCallback)((cb) => {
|
|
621
646
|
notificationCbRef.current = cb;
|
|
622
647
|
}, []);
|
|
648
|
+
const requestPushSubscribe = (0, import_react7.useCallback)(() => {
|
|
649
|
+
if (!isInIframe && !isInWebView) return;
|
|
650
|
+
if (isInWebView) {
|
|
651
|
+
bridgeSend({ type: "chat-push-subscribe" });
|
|
652
|
+
} else {
|
|
653
|
+
window.parent.postMessage({ type: "chat-push-subscribe" }, "*");
|
|
654
|
+
}
|
|
655
|
+
}, [isInIframe, isInWebView]);
|
|
656
|
+
const requestPushUnsubscribe = (0, import_react7.useCallback)(() => {
|
|
657
|
+
if (!isInIframe && !isInWebView) return;
|
|
658
|
+
if (isInWebView) {
|
|
659
|
+
bridgeSend({ type: "chat-push-unsubscribe" });
|
|
660
|
+
} else {
|
|
661
|
+
window.parent.postMessage({ type: "chat-push-unsubscribe" }, "*");
|
|
662
|
+
}
|
|
663
|
+
}, [isInIframe, isInWebView]);
|
|
664
|
+
const bridgeInScope = typeof window !== "undefined" ? getBridge() : null;
|
|
665
|
+
(0, import_react7.useEffect)(() => {
|
|
666
|
+
if (bridgeInScope?._pushState) {
|
|
667
|
+
setPushState(bridgeInScope._pushState);
|
|
668
|
+
}
|
|
669
|
+
}, [bridgeInScope?._pushState]);
|
|
670
|
+
(0, import_react7.useEffect)(() => {
|
|
671
|
+
if (!isInIframe && !isInWebView) return;
|
|
672
|
+
if (!getBridge()?._ready) {
|
|
673
|
+
const bridge = getBridge();
|
|
674
|
+
if (bridge) {
|
|
675
|
+
bridge._ready = true;
|
|
676
|
+
bridgeSend({ type: "chat-ready" });
|
|
677
|
+
} else {
|
|
678
|
+
if (!window.__chatBridge) {
|
|
679
|
+
window.__chatBridge = {};
|
|
680
|
+
}
|
|
681
|
+
window.__chatBridge._ready = true;
|
|
682
|
+
if (!window.__chatBridge.send) {
|
|
683
|
+
window.__chatBridge.send = function(msg) {
|
|
684
|
+
if (window.ReactNativeWebView?.postMessage) {
|
|
685
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(msg));
|
|
686
|
+
} else {
|
|
687
|
+
window.parent.postMessage(msg, "*");
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
window.__chatBridge.send({ type: "chat-ready" });
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}, [isInIframe, isInWebView]);
|
|
623
695
|
(0, import_react7.useEffect)(() => {
|
|
624
|
-
if (!isInIframe) return;
|
|
696
|
+
if (!isInIframe && !isInWebView) return;
|
|
625
697
|
function handleMessage(event) {
|
|
626
698
|
const data = event.data;
|
|
627
699
|
if (!data || typeof data !== "object" || !data.type) return;
|
|
@@ -633,11 +705,43 @@ function useBridge() {
|
|
|
633
705
|
if (data.type === "chat-notification-clicked") {
|
|
634
706
|
notificationCbRef.current?.();
|
|
635
707
|
}
|
|
708
|
+
if (data.type === "chat-push-state" && typeof data.status === "string") {
|
|
709
|
+
setPushState(data.status);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function handleCustomEvent(event) {
|
|
713
|
+
const detail = event.detail;
|
|
714
|
+
if (!detail || typeof detail !== "object") return;
|
|
715
|
+
if (detail.type === "chat-config") {
|
|
716
|
+
const configData = { ...detail };
|
|
717
|
+
delete configData.type;
|
|
718
|
+
setConfig(configData);
|
|
719
|
+
}
|
|
720
|
+
if (detail.type === "chat-notification-clicked") {
|
|
721
|
+
notificationCbRef.current?.();
|
|
722
|
+
}
|
|
723
|
+
if (detail.type === "chat-push-state" && typeof detail.status === "string") {
|
|
724
|
+
setPushState(detail.status);
|
|
725
|
+
}
|
|
636
726
|
}
|
|
637
727
|
window.addEventListener("message", handleMessage);
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
728
|
+
window.addEventListener("chat-bridge", handleCustomEvent);
|
|
729
|
+
return () => {
|
|
730
|
+
window.removeEventListener("message", handleMessage);
|
|
731
|
+
window.removeEventListener("chat-bridge", handleCustomEvent);
|
|
732
|
+
};
|
|
733
|
+
}, [isInIframe, isInWebView]);
|
|
734
|
+
return {
|
|
735
|
+
config,
|
|
736
|
+
isInIframe,
|
|
737
|
+
isInWebView,
|
|
738
|
+
notifyMessage,
|
|
739
|
+
notifyViewportConfig,
|
|
740
|
+
onNotificationClicked,
|
|
741
|
+
pushState,
|
|
742
|
+
requestPushSubscribe,
|
|
743
|
+
requestPushUnsubscribe
|
|
744
|
+
};
|
|
641
745
|
}
|
|
642
746
|
|
|
643
747
|
// src/i18n/LocaleProvider.tsx
|
|
@@ -4037,17 +4141,23 @@ function ChatWidget({
|
|
|
4037
4141
|
const {
|
|
4038
4142
|
config: iframeConfig,
|
|
4039
4143
|
isInIframe,
|
|
4144
|
+
isInWebView,
|
|
4040
4145
|
notifyMessage,
|
|
4041
4146
|
notifyViewportConfig,
|
|
4042
|
-
onNotificationClicked
|
|
4147
|
+
onNotificationClicked,
|
|
4148
|
+
pushState: bridgePushState,
|
|
4149
|
+
requestPushSubscribe,
|
|
4150
|
+
requestPushUnsubscribe
|
|
4043
4151
|
} = useBridge();
|
|
4044
|
-
const
|
|
4152
|
+
const hasBridgePush = (isInIframe || isInWebView) && bridgePushState !== null;
|
|
4153
|
+
const inBridge = isInIframe || isInWebView;
|
|
4154
|
+
const effectiveLocale = localeProp ?? (inBridge ? iframeConfig?.locale : void 0) ?? useLocale().locale;
|
|
4045
4155
|
const merged = mergeLocale(effectiveLocale);
|
|
4046
4156
|
const dir = merged.direction;
|
|
4047
4157
|
(0, import_react14.useEffect)(() => {
|
|
4048
4158
|
client.setLocaleHeader(effectiveLocale);
|
|
4049
4159
|
}, [client, effectiveLocale]);
|
|
4050
|
-
const autoEmbedded =
|
|
4160
|
+
const autoEmbedded = inBridge && embedded !== false;
|
|
4051
4161
|
const effectiveEmbedded = embedded === true || autoEmbedded;
|
|
4052
4162
|
const effectiveMode = effectiveEmbedded ? "embedded" : initialMode;
|
|
4053
4163
|
const [theme, setTheme] = (0, import_react14.useState)(() => {
|
|
@@ -4118,7 +4228,7 @@ function ChatWidget({
|
|
|
4118
4228
|
if (!isSmallScreen) setIsOpen(true);
|
|
4119
4229
|
}, [initialMode, isSmallScreen]);
|
|
4120
4230
|
(0, import_react14.useEffect)(() => {
|
|
4121
|
-
if (
|
|
4231
|
+
if (inBridge) {
|
|
4122
4232
|
notifyViewportConfig("interactive-widget=resizes-content");
|
|
4123
4233
|
return () => notifyViewportConfig("");
|
|
4124
4234
|
}
|
|
@@ -4135,14 +4245,15 @@ function ChatWidget({
|
|
|
4135
4245
|
meta.setAttribute("content", original);
|
|
4136
4246
|
};
|
|
4137
4247
|
}
|
|
4138
|
-
}, [isOpen, displayMode, isSmallScreen,
|
|
4248
|
+
}, [isOpen, displayMode, isSmallScreen, inBridge, notifyViewportConfig]);
|
|
4139
4249
|
const { messages, sendMessage, loading, isLoadingHistory, reloadMessages } = useMessages(
|
|
4140
4250
|
client,
|
|
4141
4251
|
(isOpen || effectiveEmbedded) && !isPreEntry
|
|
4142
4252
|
);
|
|
4143
4253
|
const { isSomeoneTyping } = useTyping(client);
|
|
4254
|
+
const webPushEnabled = !!pushConfig && !hasBridgePush;
|
|
4144
4255
|
const push = usePushNotifications({
|
|
4145
|
-
enabled:
|
|
4256
|
+
enabled: webPushEnabled,
|
|
4146
4257
|
getVapidPublicKey: pushConfig?.getVapidPublicKey ?? (() => Promise.resolve("")),
|
|
4147
4258
|
onSubscribe: pushConfig?.onSubscribe ?? (async () => {
|
|
4148
4259
|
}),
|
|
@@ -4154,11 +4265,24 @@ function ChatWidget({
|
|
|
4154
4265
|
notificationOptions: pushConfig?.notificationOptions
|
|
4155
4266
|
});
|
|
4156
4267
|
const handlePushToggle = (0, import_react14.useCallback)(() => {
|
|
4157
|
-
if (
|
|
4158
|
-
|
|
4159
|
-
|
|
4268
|
+
if (hasBridgePush) {
|
|
4269
|
+
if (bridgePushState === "subscribed") requestPushUnsubscribe();
|
|
4270
|
+
else requestPushSubscribe();
|
|
4271
|
+
} else {
|
|
4272
|
+
if (push.isSubscribed) push.unsubscribe();
|
|
4273
|
+
else push.subscribe();
|
|
4274
|
+
}
|
|
4275
|
+
}, [
|
|
4276
|
+
hasBridgePush,
|
|
4277
|
+
bridgePushState,
|
|
4278
|
+
requestPushSubscribe,
|
|
4279
|
+
requestPushUnsubscribe,
|
|
4280
|
+
push.isSubscribed,
|
|
4281
|
+
push.subscribe,
|
|
4282
|
+
push.unsubscribe
|
|
4283
|
+
]);
|
|
4160
4284
|
(0, import_react14.useEffect)(() => {
|
|
4161
|
-
if (!
|
|
4285
|
+
if (!inBridge || !iframeConfig) return;
|
|
4162
4286
|
if (iframeConfig.theme?.cssVariables) {
|
|
4163
4287
|
const root = document.documentElement;
|
|
4164
4288
|
for (const [key, value] of Object.entries(iframeConfig.theme.cssVariables)) {
|
|
@@ -4169,24 +4293,24 @@ function ChatWidget({
|
|
|
4169
4293
|
if (mode === "light" || mode === "dark" || mode === "auto") {
|
|
4170
4294
|
setTheme(mode);
|
|
4171
4295
|
}
|
|
4172
|
-
}, [
|
|
4296
|
+
}, [inBridge, iframeConfig]);
|
|
4173
4297
|
(0, import_react14.useEffect)(() => {
|
|
4174
|
-
if (!
|
|
4298
|
+
if (!inBridge) return;
|
|
4175
4299
|
onNotificationClicked(() => {
|
|
4176
4300
|
reloadMessages();
|
|
4177
4301
|
});
|
|
4178
|
-
}, [
|
|
4179
|
-
const effectiveTitle =
|
|
4180
|
-
const effectivePlaceholder =
|
|
4302
|
+
}, [inBridge, onNotificationClicked, reloadMessages]);
|
|
4303
|
+
const effectiveTitle = inBridge && iframeConfig?.title || title;
|
|
4304
|
+
const effectivePlaceholder = inBridge && iframeConfig?.placeholder || placeholder || merged.chatWidget.placeholder;
|
|
4181
4305
|
const currentUserId = "getCurrentUserId" in client ? client.getCurrentUserId() : "";
|
|
4182
4306
|
const handleSend = (0, import_react14.useCallback)(
|
|
4183
4307
|
async (text, attachments = []) => {
|
|
4184
4308
|
await sendMessage(text, attachments);
|
|
4185
|
-
if (
|
|
4309
|
+
if (inBridge) {
|
|
4186
4310
|
notifyMessage(text);
|
|
4187
4311
|
}
|
|
4188
4312
|
},
|
|
4189
|
-
[sendMessage,
|
|
4313
|
+
[sendMessage, inBridge, notifyMessage]
|
|
4190
4314
|
);
|
|
4191
4315
|
const handleActionClick = (0, import_react14.useCallback)(
|
|
4192
4316
|
(messageId, actionId, value) => {
|
|
@@ -4211,19 +4335,26 @@ function ChatWidget({
|
|
|
4211
4335
|
const close = (0, import_react14.useCallback)(() => {
|
|
4212
4336
|
setIsOpen(false);
|
|
4213
4337
|
setDisplayMode("floating");
|
|
4338
|
+
if (isInWebView) {
|
|
4339
|
+
window.__chatBridge?.send?.({ type: "chat-close" });
|
|
4340
|
+
} else if (isInIframe) {
|
|
4341
|
+
window.parent.postMessage({ type: "chat-close" }, "*");
|
|
4342
|
+
}
|
|
4214
4343
|
onClose?.();
|
|
4215
|
-
}, [onClose]);
|
|
4344
|
+
}, [onClose, isInIframe, isInWebView]);
|
|
4216
4345
|
const embeddedClose = (0, import_react14.useCallback)(() => {
|
|
4217
|
-
if (
|
|
4346
|
+
if (isInWebView) {
|
|
4347
|
+
window.__chatBridge?.send?.({ type: "chat-close" });
|
|
4348
|
+
} else if (isInIframe) {
|
|
4218
4349
|
window.parent.postMessage({ type: "chat-close" }, "*");
|
|
4219
4350
|
}
|
|
4220
|
-
}, [isInIframe]);
|
|
4351
|
+
}, [isInIframe, isInWebView]);
|
|
4221
4352
|
const panelContent = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
4222
4353
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
4223
4354
|
Header,
|
|
4224
4355
|
{
|
|
4225
4356
|
title: effectiveTitle,
|
|
4226
|
-
onClose: effectiveEmbedded ?
|
|
4357
|
+
onClose: effectiveEmbedded ? inBridge ? embeddedClose : void 0 : displayMode === "floating" ? close : showClose ? close : void 0,
|
|
4227
4358
|
onToggleFullscreen: effectiveEmbedded ? void 0 : showFullscreenToggle && !isSmallScreen ? toggleFullscreen : void 0,
|
|
4228
4359
|
isFullscreen: effectiveEmbedded ? false : displayMode === "fullscreen",
|
|
4229
4360
|
showConnectionStatus: true,
|
|
@@ -4231,8 +4362,8 @@ function ChatWidget({
|
|
|
4231
4362
|
className: className?.header,
|
|
4232
4363
|
theme,
|
|
4233
4364
|
onThemeChange: handleThemeChange,
|
|
4234
|
-
pushStatus: pushConfig ? push.status : void 0,
|
|
4235
|
-
onPushToggle: pushConfig ? handlePushToggle : void 0
|
|
4365
|
+
pushStatus: hasBridgePush ? bridgePushState : pushConfig ? push.status : void 0,
|
|
4366
|
+
onPushToggle: hasBridgePush || pushConfig ? handlePushToggle : void 0
|
|
4236
4367
|
}
|
|
4237
4368
|
),
|
|
4238
4369
|
isPreEntry && preEntry ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex-1 overflow-y-auto p-4", children: preEntry.render({ start: handleStart }) }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
@@ -4248,14 +4379,14 @@ function ChatWidget({
|
|
|
4248
4379
|
}
|
|
4249
4380
|
),
|
|
4250
4381
|
isSomeoneTyping && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TypingIndicator, {}),
|
|
4251
|
-
pushConfig ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
4382
|
+
!hasBridgePush && pushConfig ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
4252
4383
|
PushPermissionPrompt,
|
|
4253
4384
|
{
|
|
4254
4385
|
getVapidPublicKey: pushConfig.getVapidPublicKey,
|
|
4255
4386
|
onSubscribe: pushConfig.onSubscribe,
|
|
4256
4387
|
onUnsubscribe: pushConfig.onUnsubscribe
|
|
4257
4388
|
}
|
|
4258
|
-
) : renderPushPrompt?.(),
|
|
4389
|
+
) : !hasBridgePush ? renderPushPrompt?.() : null,
|
|
4259
4390
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
4260
4391
|
InputArea,
|
|
4261
4392
|
{
|