@bootdesk/js-web-adapter-react 0.3.4 → 0.3.6
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.js
CHANGED
|
@@ -526,29 +526,101 @@ function useAttachmentUpload(uploadConfig) {
|
|
|
526
526
|
|
|
527
527
|
// src/hooks/useBridge.ts
|
|
528
528
|
import { useState as useState6, useCallback as useCallback4, useEffect as useEffect6, useRef as useRef5 } from "react";
|
|
529
|
+
function getBridge() {
|
|
530
|
+
return typeof window !== "undefined" ? window.__chatBridge : null;
|
|
531
|
+
}
|
|
532
|
+
function hasNativeBridge() {
|
|
533
|
+
if (typeof window === "undefined") return false;
|
|
534
|
+
return !!getBridge() || !!window.webkit?.messageHandlers?.chatBridge || !!window.ReactNativeWebView || !!window.AndroidBridge;
|
|
535
|
+
}
|
|
536
|
+
function bridgeSend(msg) {
|
|
537
|
+
const bridge = getBridge();
|
|
538
|
+
if (bridge?.send) {
|
|
539
|
+
bridge.send(msg);
|
|
540
|
+
} else if (typeof window !== "undefined") {
|
|
541
|
+
window.parent.postMessage(msg, "*");
|
|
542
|
+
}
|
|
543
|
+
}
|
|
529
544
|
function useBridge() {
|
|
530
545
|
const notificationCbRef = useRef5(null);
|
|
531
546
|
const [config, setConfig] = useState6(null);
|
|
547
|
+
const [pushState, setPushState] = useState6(null);
|
|
532
548
|
const isInIframe = typeof window !== "undefined" && window !== window.parent;
|
|
549
|
+
const isInWebView = !isInIframe && hasNativeBridge();
|
|
533
550
|
const notifyMessage = useCallback4(
|
|
534
551
|
(text) => {
|
|
535
|
-
if (!isInIframe) return;
|
|
536
|
-
|
|
552
|
+
if (!isInIframe && !isInWebView) return;
|
|
553
|
+
if (isInWebView) {
|
|
554
|
+
bridgeSend({ type: "chat-message", text });
|
|
555
|
+
} else {
|
|
556
|
+
window.parent.postMessage({ type: "chat-message", text }, "*");
|
|
557
|
+
}
|
|
537
558
|
},
|
|
538
|
-
[isInIframe]
|
|
559
|
+
[isInIframe, isInWebView]
|
|
539
560
|
);
|
|
540
561
|
const notifyViewportConfig = useCallback4(
|
|
541
562
|
(viewportContent) => {
|
|
542
|
-
if (!isInIframe) return;
|
|
543
|
-
|
|
563
|
+
if (!isInIframe && !isInWebView) return;
|
|
564
|
+
if (isInWebView) {
|
|
565
|
+
bridgeSend({ type: "chat-viewport-config", content: viewportContent });
|
|
566
|
+
} else {
|
|
567
|
+
window.parent.postMessage({ type: "chat-viewport-config", content: viewportContent }, "*");
|
|
568
|
+
}
|
|
544
569
|
},
|
|
545
|
-
[isInIframe]
|
|
570
|
+
[isInIframe, isInWebView]
|
|
546
571
|
);
|
|
547
572
|
const onNotificationClicked = useCallback4((cb) => {
|
|
548
573
|
notificationCbRef.current = cb;
|
|
549
574
|
}, []);
|
|
575
|
+
const requestPushSubscribe = useCallback4(() => {
|
|
576
|
+
if (!isInIframe && !isInWebView) return;
|
|
577
|
+
if (isInWebView) {
|
|
578
|
+
bridgeSend({ type: "chat-push-subscribe" });
|
|
579
|
+
} else {
|
|
580
|
+
window.parent.postMessage({ type: "chat-push-subscribe" }, "*");
|
|
581
|
+
}
|
|
582
|
+
}, [isInIframe, isInWebView]);
|
|
583
|
+
const requestPushUnsubscribe = useCallback4(() => {
|
|
584
|
+
if (!isInIframe && !isInWebView) return;
|
|
585
|
+
if (isInWebView) {
|
|
586
|
+
bridgeSend({ type: "chat-push-unsubscribe" });
|
|
587
|
+
} else {
|
|
588
|
+
window.parent.postMessage({ type: "chat-push-unsubscribe" }, "*");
|
|
589
|
+
}
|
|
590
|
+
}, [isInIframe, isInWebView]);
|
|
591
|
+
const bridgeInScope = typeof window !== "undefined" ? getBridge() : null;
|
|
592
|
+
useEffect6(() => {
|
|
593
|
+
if (bridgeInScope?._pushState) {
|
|
594
|
+
setPushState(bridgeInScope._pushState);
|
|
595
|
+
}
|
|
596
|
+
}, [bridgeInScope?._pushState]);
|
|
597
|
+
useEffect6(() => {
|
|
598
|
+
if (!isInIframe && !isInWebView) return;
|
|
599
|
+
if (!getBridge()?._ready) {
|
|
600
|
+
const bridge = getBridge();
|
|
601
|
+
if (bridge) {
|
|
602
|
+
bridge._ready = true;
|
|
603
|
+
bridgeSend({ type: "chat-ready" });
|
|
604
|
+
} else {
|
|
605
|
+
if (!window.__chatBridge) {
|
|
606
|
+
window.__chatBridge = {};
|
|
607
|
+
}
|
|
608
|
+
window.__chatBridge._ready = true;
|
|
609
|
+
if (!window.__chatBridge.send) {
|
|
610
|
+
window.__chatBridge.send = function(msg) {
|
|
611
|
+
if (window.ReactNativeWebView?.postMessage) {
|
|
612
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(msg));
|
|
613
|
+
} else {
|
|
614
|
+
window.parent.postMessage(msg, "*");
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
window.__chatBridge.send({ type: "chat-ready" });
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}, [isInIframe, isInWebView]);
|
|
550
622
|
useEffect6(() => {
|
|
551
|
-
if (!isInIframe) return;
|
|
623
|
+
if (!isInIframe && !isInWebView) return;
|
|
552
624
|
function handleMessage(event) {
|
|
553
625
|
const data = event.data;
|
|
554
626
|
if (!data || typeof data !== "object" || !data.type) return;
|
|
@@ -560,11 +632,43 @@ function useBridge() {
|
|
|
560
632
|
if (data.type === "chat-notification-clicked") {
|
|
561
633
|
notificationCbRef.current?.();
|
|
562
634
|
}
|
|
635
|
+
if (data.type === "chat-push-state" && typeof data.status === "string") {
|
|
636
|
+
setPushState(data.status);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function handleCustomEvent(event) {
|
|
640
|
+
const detail = event.detail;
|
|
641
|
+
if (!detail || typeof detail !== "object") return;
|
|
642
|
+
if (detail.type === "chat-config") {
|
|
643
|
+
const configData = { ...detail };
|
|
644
|
+
delete configData.type;
|
|
645
|
+
setConfig(configData);
|
|
646
|
+
}
|
|
647
|
+
if (detail.type === "chat-notification-clicked") {
|
|
648
|
+
notificationCbRef.current?.();
|
|
649
|
+
}
|
|
650
|
+
if (detail.type === "chat-push-state" && typeof detail.status === "string") {
|
|
651
|
+
setPushState(detail.status);
|
|
652
|
+
}
|
|
563
653
|
}
|
|
564
654
|
window.addEventListener("message", handleMessage);
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
655
|
+
window.addEventListener("chat-bridge", handleCustomEvent);
|
|
656
|
+
return () => {
|
|
657
|
+
window.removeEventListener("message", handleMessage);
|
|
658
|
+
window.removeEventListener("chat-bridge", handleCustomEvent);
|
|
659
|
+
};
|
|
660
|
+
}, [isInIframe, isInWebView]);
|
|
661
|
+
return {
|
|
662
|
+
config,
|
|
663
|
+
isInIframe,
|
|
664
|
+
isInWebView,
|
|
665
|
+
notifyMessage,
|
|
666
|
+
notifyViewportConfig,
|
|
667
|
+
onNotificationClicked,
|
|
668
|
+
pushState,
|
|
669
|
+
requestPushSubscribe,
|
|
670
|
+
requestPushUnsubscribe
|
|
671
|
+
};
|
|
568
672
|
}
|
|
569
673
|
|
|
570
674
|
// src/i18n/LocaleProvider.tsx
|
|
@@ -3964,17 +4068,23 @@ function ChatWidget({
|
|
|
3964
4068
|
const {
|
|
3965
4069
|
config: iframeConfig,
|
|
3966
4070
|
isInIframe,
|
|
4071
|
+
isInWebView,
|
|
3967
4072
|
notifyMessage,
|
|
3968
4073
|
notifyViewportConfig,
|
|
3969
|
-
onNotificationClicked
|
|
4074
|
+
onNotificationClicked,
|
|
4075
|
+
pushState: bridgePushState,
|
|
4076
|
+
requestPushSubscribe,
|
|
4077
|
+
requestPushUnsubscribe
|
|
3970
4078
|
} = useBridge();
|
|
3971
|
-
const
|
|
4079
|
+
const hasBridgePush = (isInIframe || isInWebView) && bridgePushState !== null;
|
|
4080
|
+
const inBridge = isInIframe || isInWebView;
|
|
4081
|
+
const effectiveLocale = localeProp ?? (inBridge ? iframeConfig?.locale : void 0) ?? useLocale().locale;
|
|
3972
4082
|
const merged = mergeLocale(effectiveLocale);
|
|
3973
4083
|
const dir = merged.direction;
|
|
3974
4084
|
useEffect9(() => {
|
|
3975
4085
|
client.setLocaleHeader(effectiveLocale);
|
|
3976
4086
|
}, [client, effectiveLocale]);
|
|
3977
|
-
const autoEmbedded =
|
|
4087
|
+
const autoEmbedded = inBridge && embedded !== false;
|
|
3978
4088
|
const effectiveEmbedded = embedded === true || autoEmbedded;
|
|
3979
4089
|
const effectiveMode = effectiveEmbedded ? "embedded" : initialMode;
|
|
3980
4090
|
const [theme, setTheme] = useState10(() => {
|
|
@@ -4045,7 +4155,7 @@ function ChatWidget({
|
|
|
4045
4155
|
if (!isSmallScreen) setIsOpen(true);
|
|
4046
4156
|
}, [initialMode, isSmallScreen]);
|
|
4047
4157
|
useEffect9(() => {
|
|
4048
|
-
if (
|
|
4158
|
+
if (inBridge) {
|
|
4049
4159
|
notifyViewportConfig("interactive-widget=resizes-content");
|
|
4050
4160
|
return () => notifyViewportConfig("");
|
|
4051
4161
|
}
|
|
@@ -4062,14 +4172,15 @@ function ChatWidget({
|
|
|
4062
4172
|
meta.setAttribute("content", original);
|
|
4063
4173
|
};
|
|
4064
4174
|
}
|
|
4065
|
-
}, [isOpen, displayMode, isSmallScreen,
|
|
4175
|
+
}, [isOpen, displayMode, isSmallScreen, inBridge, notifyViewportConfig]);
|
|
4066
4176
|
const { messages, sendMessage, loading, isLoadingHistory, reloadMessages } = useMessages(
|
|
4067
4177
|
client,
|
|
4068
4178
|
(isOpen || effectiveEmbedded) && !isPreEntry
|
|
4069
4179
|
);
|
|
4070
4180
|
const { isSomeoneTyping } = useTyping(client);
|
|
4181
|
+
const webPushEnabled = !!pushConfig && !hasBridgePush;
|
|
4071
4182
|
const push = usePushNotifications({
|
|
4072
|
-
enabled:
|
|
4183
|
+
enabled: webPushEnabled,
|
|
4073
4184
|
getVapidPublicKey: pushConfig?.getVapidPublicKey ?? (() => Promise.resolve("")),
|
|
4074
4185
|
onSubscribe: pushConfig?.onSubscribe ?? (async () => {
|
|
4075
4186
|
}),
|
|
@@ -4081,11 +4192,24 @@ function ChatWidget({
|
|
|
4081
4192
|
notificationOptions: pushConfig?.notificationOptions
|
|
4082
4193
|
});
|
|
4083
4194
|
const handlePushToggle = useCallback6(() => {
|
|
4084
|
-
if (
|
|
4085
|
-
|
|
4086
|
-
|
|
4195
|
+
if (hasBridgePush) {
|
|
4196
|
+
if (bridgePushState === "subscribed") requestPushUnsubscribe();
|
|
4197
|
+
else requestPushSubscribe();
|
|
4198
|
+
} else {
|
|
4199
|
+
if (push.isSubscribed) push.unsubscribe();
|
|
4200
|
+
else push.subscribe();
|
|
4201
|
+
}
|
|
4202
|
+
}, [
|
|
4203
|
+
hasBridgePush,
|
|
4204
|
+
bridgePushState,
|
|
4205
|
+
requestPushSubscribe,
|
|
4206
|
+
requestPushUnsubscribe,
|
|
4207
|
+
push.isSubscribed,
|
|
4208
|
+
push.subscribe,
|
|
4209
|
+
push.unsubscribe
|
|
4210
|
+
]);
|
|
4087
4211
|
useEffect9(() => {
|
|
4088
|
-
if (!
|
|
4212
|
+
if (!inBridge || !iframeConfig) return;
|
|
4089
4213
|
if (iframeConfig.theme?.cssVariables) {
|
|
4090
4214
|
const root = document.documentElement;
|
|
4091
4215
|
for (const [key, value] of Object.entries(iframeConfig.theme.cssVariables)) {
|
|
@@ -4096,24 +4220,24 @@ function ChatWidget({
|
|
|
4096
4220
|
if (mode === "light" || mode === "dark" || mode === "auto") {
|
|
4097
4221
|
setTheme(mode);
|
|
4098
4222
|
}
|
|
4099
|
-
}, [
|
|
4223
|
+
}, [inBridge, iframeConfig]);
|
|
4100
4224
|
useEffect9(() => {
|
|
4101
|
-
if (!
|
|
4225
|
+
if (!inBridge) return;
|
|
4102
4226
|
onNotificationClicked(() => {
|
|
4103
4227
|
reloadMessages();
|
|
4104
4228
|
});
|
|
4105
|
-
}, [
|
|
4106
|
-
const effectiveTitle =
|
|
4107
|
-
const effectivePlaceholder =
|
|
4229
|
+
}, [inBridge, onNotificationClicked, reloadMessages]);
|
|
4230
|
+
const effectiveTitle = inBridge && iframeConfig?.title || title;
|
|
4231
|
+
const effectivePlaceholder = inBridge && iframeConfig?.placeholder || placeholder || merged.chatWidget.placeholder;
|
|
4108
4232
|
const currentUserId = "getCurrentUserId" in client ? client.getCurrentUserId() : "";
|
|
4109
4233
|
const handleSend = useCallback6(
|
|
4110
4234
|
async (text, attachments = []) => {
|
|
4111
4235
|
await sendMessage(text, attachments);
|
|
4112
|
-
if (
|
|
4236
|
+
if (inBridge) {
|
|
4113
4237
|
notifyMessage(text);
|
|
4114
4238
|
}
|
|
4115
4239
|
},
|
|
4116
|
-
[sendMessage,
|
|
4240
|
+
[sendMessage, inBridge, notifyMessage]
|
|
4117
4241
|
);
|
|
4118
4242
|
const handleActionClick = useCallback6(
|
|
4119
4243
|
(messageId, actionId, value) => {
|
|
@@ -4138,19 +4262,26 @@ function ChatWidget({
|
|
|
4138
4262
|
const close = useCallback6(() => {
|
|
4139
4263
|
setIsOpen(false);
|
|
4140
4264
|
setDisplayMode("floating");
|
|
4265
|
+
if (isInWebView) {
|
|
4266
|
+
window.__chatBridge?.send?.({ type: "chat-close" });
|
|
4267
|
+
} else if (isInIframe) {
|
|
4268
|
+
window.parent.postMessage({ type: "chat-close" }, "*");
|
|
4269
|
+
}
|
|
4141
4270
|
onClose?.();
|
|
4142
|
-
}, [onClose]);
|
|
4271
|
+
}, [onClose, isInIframe, isInWebView]);
|
|
4143
4272
|
const embeddedClose = useCallback6(() => {
|
|
4144
|
-
if (
|
|
4273
|
+
if (isInWebView) {
|
|
4274
|
+
window.__chatBridge?.send?.({ type: "chat-close" });
|
|
4275
|
+
} else if (isInIframe) {
|
|
4145
4276
|
window.parent.postMessage({ type: "chat-close" }, "*");
|
|
4146
4277
|
}
|
|
4147
|
-
}, [isInIframe]);
|
|
4278
|
+
}, [isInIframe, isInWebView]);
|
|
4148
4279
|
const panelContent = /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
4149
4280
|
/* @__PURE__ */ jsx17(
|
|
4150
4281
|
Header,
|
|
4151
4282
|
{
|
|
4152
4283
|
title: effectiveTitle,
|
|
4153
|
-
onClose: effectiveEmbedded ?
|
|
4284
|
+
onClose: effectiveEmbedded ? inBridge ? embeddedClose : void 0 : displayMode === "floating" ? close : showClose ? close : void 0,
|
|
4154
4285
|
onToggleFullscreen: effectiveEmbedded ? void 0 : showFullscreenToggle && !isSmallScreen ? toggleFullscreen : void 0,
|
|
4155
4286
|
isFullscreen: effectiveEmbedded ? false : displayMode === "fullscreen",
|
|
4156
4287
|
showConnectionStatus: true,
|
|
@@ -4158,8 +4289,8 @@ function ChatWidget({
|
|
|
4158
4289
|
className: className?.header,
|
|
4159
4290
|
theme,
|
|
4160
4291
|
onThemeChange: handleThemeChange,
|
|
4161
|
-
pushStatus: pushConfig ? push.status : void 0,
|
|
4162
|
-
onPushToggle: pushConfig ? handlePushToggle : void 0
|
|
4292
|
+
pushStatus: hasBridgePush ? bridgePushState : pushConfig ? push.status : void 0,
|
|
4293
|
+
onPushToggle: hasBridgePush || pushConfig ? handlePushToggle : void 0
|
|
4163
4294
|
}
|
|
4164
4295
|
),
|
|
4165
4296
|
isPreEntry && preEntry ? /* @__PURE__ */ jsx17("div", { className: "flex-1 overflow-y-auto p-4", children: preEntry.render({ start: handleStart }) }) : /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
@@ -4175,14 +4306,14 @@ function ChatWidget({
|
|
|
4175
4306
|
}
|
|
4176
4307
|
),
|
|
4177
4308
|
isSomeoneTyping && /* @__PURE__ */ jsx17(TypingIndicator, {}),
|
|
4178
|
-
pushConfig ? /* @__PURE__ */ jsx17(
|
|
4309
|
+
!hasBridgePush && pushConfig ? /* @__PURE__ */ jsx17(
|
|
4179
4310
|
PushPermissionPrompt,
|
|
4180
4311
|
{
|
|
4181
4312
|
getVapidPublicKey: pushConfig.getVapidPublicKey,
|
|
4182
4313
|
onSubscribe: pushConfig.onSubscribe,
|
|
4183
4314
|
onUnsubscribe: pushConfig.onUnsubscribe
|
|
4184
4315
|
}
|
|
4185
|
-
) : renderPushPrompt?.(),
|
|
4316
|
+
) : !hasBridgePush ? renderPushPrompt?.() : null,
|
|
4186
4317
|
/* @__PURE__ */ jsx17(
|
|
4187
4318
|
InputArea,
|
|
4188
4319
|
{
|