@lorikeetai/react-native-sdk 2.0.0-alpha.1 → 2.0.0-alpha.2
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/README.md +97 -5
- package/dist/index.cjs +222 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +70 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +223 -45
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
|
|
1
|
+
import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
|
|
2
2
|
import { AppState, Linking, StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
|
3
3
|
import { SafeAreaView } from "react-native-safe-area-context";
|
|
4
4
|
import { WebView } from "react-native-webview";
|
|
@@ -115,6 +115,26 @@ var LorikeetClient = class {
|
|
|
115
115
|
if (this.metadata) url.searchParams.append("metadata", JSON.stringify(this.metadata));
|
|
116
116
|
return url.toString();
|
|
117
117
|
});
|
|
118
|
+
_defineProperty(
|
|
119
|
+
this,
|
|
120
|
+
/**
|
|
121
|
+
* The page that clears this key's credentials from the WebView: its cookies,
|
|
122
|
+
* server-side, and the storage the frame keeps. Navigating a WebView here is
|
|
123
|
+
* the only way to reach either, so a host that only drops its own copy of
|
|
124
|
+
* `ticketId` and `session` leaves the next person on the device able to
|
|
125
|
+
* reopen the conversation.
|
|
126
|
+
*
|
|
127
|
+
* The page confirms with a `loggedOut` message, which the SDK reports as
|
|
128
|
+
* `logged_out`. The load event does not: an error page fires that too.
|
|
129
|
+
*/
|
|
130
|
+
"getLogoutUrl",
|
|
131
|
+
() => {
|
|
132
|
+
const url = new URL(this.baseUrl);
|
|
133
|
+
url.pathname = "/logout";
|
|
134
|
+
url.searchParams.append("public_key", this.publicKey);
|
|
135
|
+
return url.toString();
|
|
136
|
+
}
|
|
137
|
+
);
|
|
118
138
|
if (api_base) this.baseUrl = api_base;
|
|
119
139
|
this.publicKey = public_key;
|
|
120
140
|
if (customisations) this.customisations = customisations;
|
|
@@ -181,7 +201,10 @@ const eventFromMessage = (message) => {
|
|
|
181
201
|
switch (record.type) {
|
|
182
202
|
case "ready": return { type: "ready" };
|
|
183
203
|
case "customerMessage": return { type: "customer_message" };
|
|
184
|
-
case "agentMessage": return
|
|
204
|
+
case "agentMessage": return typeof record.markUnread === "boolean" ? {
|
|
205
|
+
type: "agent_message",
|
|
206
|
+
markUnread: record.markUnread
|
|
207
|
+
} : { type: "agent_message" };
|
|
185
208
|
case "closeChat": {
|
|
186
209
|
const reason = asNonEmptyString(record.reason);
|
|
187
210
|
return reason === "ended_by_customer" || reason === "minimised" ? {
|
|
@@ -205,6 +228,7 @@ const eventFromMessage = (message) => {
|
|
|
205
228
|
session
|
|
206
229
|
};
|
|
207
230
|
}
|
|
231
|
+
case "loggedOut": return { type: "logged_out" };
|
|
208
232
|
case "error": {
|
|
209
233
|
const error = asNonEmptyString(record.error);
|
|
210
234
|
return error === null ? null : {
|
|
@@ -255,6 +279,13 @@ const resumedTicketIdFromUri = (uri) => {
|
|
|
255
279
|
*/
|
|
256
280
|
const BACKGROUND_RELOAD_THRESHOLD_MS = 18e4;
|
|
257
281
|
/**
|
|
282
|
+
* How long `/logout` gets to confirm it cleared the frame before the host is
|
|
283
|
+
* told it did not. Matches the web SDK's own budget, and like it, a logout that
|
|
284
|
+
* misses the deadline leaves the widget unavailable rather than letting the next
|
|
285
|
+
* customer start a chat on a device the previous one's credentials still open.
|
|
286
|
+
*/
|
|
287
|
+
const LOGOUT_CONFIRMATION_TIMEOUT_MS = 1e4;
|
|
288
|
+
/**
|
|
258
289
|
* Elapsed time is the only signal available: the bridge reports `ready`,
|
|
259
290
|
* `error` and the persistence messages, but never stream health, so the SDK
|
|
260
291
|
* cannot ask whether the connection actually survived.
|
|
@@ -381,6 +412,7 @@ const stylesheet = StyleSheet.create({
|
|
|
381
412
|
textAlign: "center",
|
|
382
413
|
fontSize: 16
|
|
383
414
|
},
|
|
415
|
+
closedControl: { padding: 16 },
|
|
384
416
|
webView: { flex: 1 },
|
|
385
417
|
closed: {
|
|
386
418
|
flex: 1,
|
|
@@ -459,8 +491,8 @@ const LorikeetWidgetHeader = ({ title, style, icon = /* @__PURE__ */ jsx(Message
|
|
|
459
491
|
]
|
|
460
492
|
});
|
|
461
493
|
};
|
|
462
|
-
function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, styles, onEvent, onWebViewMessage, ticketId, session, metadata, webviewProps }) {
|
|
463
|
-
var _customisations$title;
|
|
494
|
+
const LorikeetWidgetView = forwardRef(function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, styles, onEvent, onWebViewMessage, ticketId, session, metadata, webviewProps }, ref) {
|
|
495
|
+
var _customisations$hideH, _customisations$title;
|
|
464
496
|
const [lorikeetClient, setLorikeetClient] = useState(null);
|
|
465
497
|
const [loaded, setLoaded] = useState(null);
|
|
466
498
|
const [generation, setGeneration] = useState(0);
|
|
@@ -485,7 +517,12 @@ function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, sty
|
|
|
485
517
|
});
|
|
486
518
|
const resumingTicketId = ticketId !== void 0 && ticketId !== refusedTicketId ? ticketId : void 0;
|
|
487
519
|
const [attemptedTicketId, setAttemptedTicketId] = useState(void 0);
|
|
488
|
-
const [closed, setClosed] = useState(
|
|
520
|
+
const [closed, setClosed] = useState(null);
|
|
521
|
+
const [clearing, setClearing] = useState(null);
|
|
522
|
+
const [loggedOutIdentity, setLoggedOutIdentity] = useState(null);
|
|
523
|
+
const onEventRef = useRef(onEvent);
|
|
524
|
+
onEventRef.current = onEvent;
|
|
525
|
+
const hideHeader = (_customisations$hideH = customisations === null || customisations === void 0 ? void 0 : customisations.hideHeader) !== null && _customisations$hideH !== void 0 ? _customisations$hideH : true;
|
|
489
526
|
const load = useCallback((client, forIdentity, intendedTicketId, resume, force = false) => {
|
|
490
527
|
const next = client.getWebviewUrl({
|
|
491
528
|
token: authToken,
|
|
@@ -493,7 +530,7 @@ function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, sty
|
|
|
493
530
|
session: resume === null || resume === void 0 ? void 0 : resume.session
|
|
494
531
|
});
|
|
495
532
|
const unchanged = uriRef.current === next;
|
|
496
|
-
if (loadedForRef.current.identity !== forIdentity || loadedForRef.current.intendedTicketId !== intendedTicketId) setClosed(
|
|
533
|
+
if (loadedForRef.current.identity !== forIdentity || loadedForRef.current.intendedTicketId !== intendedTicketId) setClosed(null);
|
|
497
534
|
uriRef.current = next;
|
|
498
535
|
loadedForRef.current = {
|
|
499
536
|
identity: forIdentity,
|
|
@@ -522,10 +559,12 @@ function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, sty
|
|
|
522
559
|
metadata,
|
|
523
560
|
customisations: {
|
|
524
561
|
...customisations,
|
|
525
|
-
hideHeader
|
|
562
|
+
hideHeader
|
|
526
563
|
}
|
|
527
564
|
});
|
|
528
565
|
setLorikeetClient(lorikeetClient);
|
|
566
|
+
if (clearing !== null || loggedOutIdentity === identity) return;
|
|
567
|
+
if (loggedOutIdentity !== null) setLoggedOutIdentity(null);
|
|
529
568
|
const resume = resumingTicketId !== void 0 ? {
|
|
530
569
|
session,
|
|
531
570
|
ticketId: resumingTicketId
|
|
@@ -535,13 +574,108 @@ function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, sty
|
|
|
535
574
|
publicKey,
|
|
536
575
|
baseUrl,
|
|
537
576
|
authToken,
|
|
577
|
+
clearing,
|
|
538
578
|
customisations,
|
|
579
|
+
hideHeader,
|
|
580
|
+
loggedOutIdentity,
|
|
539
581
|
metadata,
|
|
540
582
|
session,
|
|
541
583
|
resumingTicketId,
|
|
542
584
|
identity,
|
|
543
585
|
load
|
|
544
586
|
]);
|
|
587
|
+
/**
|
|
588
|
+
* Clear this customer's chat from the device. Loads the page's own `/logout`,
|
|
589
|
+
* the only thing that can drop the chat's cookies and the storage the page
|
|
590
|
+
* keeps; a host clearing its stored `ticketId` and `session` leaves both
|
|
591
|
+
* behind.
|
|
592
|
+
*
|
|
593
|
+
* Nothing else loads until the page confirms with `logged_out`, and nothing
|
|
594
|
+
* loads for these credentials after it, so pass the next customer's in to
|
|
595
|
+
* start their chat. Clear your own stored `ticketId` and `session` too.
|
|
596
|
+
*/
|
|
597
|
+
const logout = useCallback(() => {
|
|
598
|
+
observedRef.current = null;
|
|
599
|
+
setClosed(null);
|
|
600
|
+
setClearing((previous) => {
|
|
601
|
+
if (previous !== null) return {
|
|
602
|
+
...previous,
|
|
603
|
+
attempt: previous.attempt + 1
|
|
604
|
+
};
|
|
605
|
+
setLoggedOutIdentity(identity);
|
|
606
|
+
return {
|
|
607
|
+
attempt: 1,
|
|
608
|
+
baseUrl,
|
|
609
|
+
identity,
|
|
610
|
+
uri: new LorikeetClient({
|
|
611
|
+
api_base: baseUrl,
|
|
612
|
+
public_key: publicKey
|
|
613
|
+
}).getLogoutUrl(),
|
|
614
|
+
webviewProps: {
|
|
615
|
+
...webviewProps,
|
|
616
|
+
injectedJavaScript: void 0,
|
|
617
|
+
injectedJavaScriptBeforeContentLoaded: void 0,
|
|
618
|
+
onMessage: void 0,
|
|
619
|
+
onShouldStartLoadWithRequest: void 0
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
});
|
|
623
|
+
}, [
|
|
624
|
+
baseUrl,
|
|
625
|
+
identity,
|
|
626
|
+
publicKey,
|
|
627
|
+
webviewProps
|
|
628
|
+
]);
|
|
629
|
+
/**
|
|
630
|
+
* Show a minimised conversation again, exactly as the customer left it.
|
|
631
|
+
*
|
|
632
|
+
* A no-op unless something is minimised, so a host with its own launcher can
|
|
633
|
+
* call it without first asking.
|
|
634
|
+
*/
|
|
635
|
+
const reopen = useCallback(() => {
|
|
636
|
+
setClosed((previous) => previous === "minimised" ? null : previous);
|
|
637
|
+
}, []);
|
|
638
|
+
/**
|
|
639
|
+
* What the widget is actually on, for a host that has to store it or draw its
|
|
640
|
+
* own launcher around it.
|
|
641
|
+
*/
|
|
642
|
+
const getConversation = useCallback(() => {
|
|
643
|
+
const resumable = resumableConversation(observedRef.current, identity, resumingTicketId);
|
|
644
|
+
if (resumable === null) return null;
|
|
645
|
+
return {
|
|
646
|
+
minimised: closed === "minimised",
|
|
647
|
+
session: resumable.session,
|
|
648
|
+
ticketId: resumable.ticketId
|
|
649
|
+
};
|
|
650
|
+
}, [
|
|
651
|
+
closed,
|
|
652
|
+
identity,
|
|
653
|
+
resumingTicketId
|
|
654
|
+
]);
|
|
655
|
+
useImperativeHandle(ref, () => ({
|
|
656
|
+
getConversation,
|
|
657
|
+
logout,
|
|
658
|
+
reopen
|
|
659
|
+
}), [
|
|
660
|
+
getConversation,
|
|
661
|
+
logout,
|
|
662
|
+
reopen
|
|
663
|
+
]);
|
|
664
|
+
const clearingAttempt = clearing === null || clearing === void 0 ? void 0 : clearing.attempt;
|
|
665
|
+
useEffect(() => {
|
|
666
|
+
if (clearingAttempt === void 0) return;
|
|
667
|
+
const timer = setTimeout(() => {
|
|
668
|
+
var _onEventRef$current;
|
|
669
|
+
(_onEventRef$current = onEventRef.current) === null || _onEventRef$current === void 0 || _onEventRef$current.call(onEventRef, { type: "logout_failed" });
|
|
670
|
+
}, LOGOUT_CONFIRMATION_TIMEOUT_MS);
|
|
671
|
+
return () => {
|
|
672
|
+
clearTimeout(timer);
|
|
673
|
+
};
|
|
674
|
+
}, [clearingAttempt]);
|
|
675
|
+
const handleClearingEvent = useCallback((event) => {
|
|
676
|
+
if (event.type === "logged_out") setClearing((previous) => (previous === null || previous === void 0 ? void 0 : previous.attempt) === clearingAttempt ? null : previous);
|
|
677
|
+
onEvent === null || onEvent === void 0 || onEvent(event);
|
|
678
|
+
}, [clearingAttempt, onEvent]);
|
|
545
679
|
const reload = useCallback(() => {
|
|
546
680
|
const resume = resumableConversation(observedRef.current, identity, resumingTicketId);
|
|
547
681
|
if (lorikeetClient !== null && resume !== null) {
|
|
@@ -573,7 +707,7 @@ function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, sty
|
|
|
573
707
|
};
|
|
574
708
|
}, [reload]);
|
|
575
709
|
const startNewChat = useCallback(() => {
|
|
576
|
-
setClosed(
|
|
710
|
+
setClosed(null);
|
|
577
711
|
if (lorikeetClient !== null && resumingTicketId === void 0) {
|
|
578
712
|
observedRef.current = null;
|
|
579
713
|
load(lorikeetClient, identity, void 0, null, true);
|
|
@@ -599,7 +733,7 @@ function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, sty
|
|
|
599
733
|
intendedTicketId: resumingTicketId
|
|
600
734
|
};
|
|
601
735
|
}
|
|
602
|
-
if (event.type === "close") setClosed(
|
|
736
|
+
if (event.type === "close") setClosed(event.reason === "minimised" ? "minimised" : "ended");
|
|
603
737
|
if (event.type === "new_chat") startNewChat();
|
|
604
738
|
if (event.type === "error" && attemptedTicketId !== void 0 && isUnusableConversationCode(event.error)) {
|
|
605
739
|
observedRef.current = null;
|
|
@@ -623,7 +757,31 @@ function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, sty
|
|
|
623
757
|
onWebViewMessage === null || onWebViewMessage === void 0 || onWebViewMessage(event);
|
|
624
758
|
webviewProps === null || webviewProps === void 0 || (_webviewProps$onMessa2 = webviewProps.onMessage) === null || _webviewProps$onMessa2 === void 0 || _webviewProps$onMessa2.call(webviewProps, event);
|
|
625
759
|
}, [onWebViewMessage, webviewProps]);
|
|
626
|
-
if (
|
|
760
|
+
if (clearing !== null) return /* @__PURE__ */ jsx(SafeAreaView, {
|
|
761
|
+
edges: [
|
|
762
|
+
"top",
|
|
763
|
+
"left",
|
|
764
|
+
"right"
|
|
765
|
+
],
|
|
766
|
+
style: stylesheet.root,
|
|
767
|
+
children: /* @__PURE__ */ jsx(View, {
|
|
768
|
+
accessibilityElementsHidden: true,
|
|
769
|
+
importantForAccessibility: "no-hide-descendants",
|
|
770
|
+
pointerEvents: "none",
|
|
771
|
+
style: stylesheet.closed,
|
|
772
|
+
children: /* @__PURE__ */ jsx(LorikeetWidgetWebView, {
|
|
773
|
+
baseUrl: clearing.baseUrl,
|
|
774
|
+
onEvent: handleClearingEvent,
|
|
775
|
+
uri: clearing.uri,
|
|
776
|
+
webviewProps: {
|
|
777
|
+
...clearing.webviewProps,
|
|
778
|
+
style: stylesheet.webView
|
|
779
|
+
}
|
|
780
|
+
}, `logout|${clearing.attempt}`)
|
|
781
|
+
})
|
|
782
|
+
});
|
|
783
|
+
if (lorikeetClient === null || loaded === null || loaded.identity !== identity || loaded.intendedTicketId !== resumingTicketId || loggedOutIdentity === identity) return null;
|
|
784
|
+
const isClosed = closed !== null;
|
|
627
785
|
return /* @__PURE__ */ jsx(SafeAreaView, {
|
|
628
786
|
edges: [
|
|
629
787
|
"top",
|
|
@@ -633,44 +791,64 @@ function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, sty
|
|
|
633
791
|
style: stylesheet.root,
|
|
634
792
|
children: /* @__PURE__ */ jsxs(View, {
|
|
635
793
|
style: stylesheet.container,
|
|
636
|
-
children: [
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
794
|
+
children: [
|
|
795
|
+
hideHeader && closed !== "minimised" ? /* @__PURE__ */ jsx(LorikeetWidgetHeader, {
|
|
796
|
+
onIconPress: startNewChat,
|
|
797
|
+
style: styles === null || styles === void 0 ? void 0 : styles.header,
|
|
798
|
+
title: (_customisations$title = customisations === null || customisations === void 0 ? void 0 : customisations.title) !== null && _customisations$title !== void 0 ? _customisations$title : "Lorikeet AI Agent"
|
|
799
|
+
}) : null,
|
|
800
|
+
closed === "minimised" ? /* @__PURE__ */ jsx(TouchableOpacity, {
|
|
801
|
+
onPress: reopen,
|
|
802
|
+
style: stylesheet.closedControl,
|
|
803
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
804
|
+
style: stylesheet.headerText,
|
|
805
|
+
children: "Reopen chat"
|
|
806
|
+
})
|
|
807
|
+
}) : null,
|
|
808
|
+
!hideHeader && closed === "ended" ? /* @__PURE__ */ jsx(TouchableOpacity, {
|
|
809
|
+
onPress: startNewChat,
|
|
810
|
+
style: stylesheet.closedControl,
|
|
811
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
812
|
+
style: stylesheet.headerText,
|
|
813
|
+
children: "Start new chat"
|
|
814
|
+
})
|
|
815
|
+
}) : null,
|
|
816
|
+
/* @__PURE__ */ jsx(View, {
|
|
817
|
+
accessibilityElementsHidden: isClosed,
|
|
818
|
+
importantForAccessibility: isClosed ? "no-hide-descendants" : "auto",
|
|
819
|
+
pointerEvents: isClosed ? "none" : "auto",
|
|
820
|
+
style: isClosed ? stylesheet.closed : stylesheet.webView,
|
|
821
|
+
children: /* @__PURE__ */ jsx(LorikeetWidgetWebView, {
|
|
822
|
+
baseUrl: lorikeetClient.baseUrl,
|
|
823
|
+
onEvent: handleEvent,
|
|
824
|
+
ref: webViewRef,
|
|
825
|
+
uri: loaded.uri,
|
|
826
|
+
webviewProps: {
|
|
827
|
+
allowsInlineMediaPlayback: true,
|
|
828
|
+
mediaPlaybackRequiresUserAction: false,
|
|
829
|
+
scalesPageToFit: true,
|
|
830
|
+
startInLoadingState: true,
|
|
831
|
+
style: stylesheet.webView,
|
|
832
|
+
...webviewProps,
|
|
833
|
+
onMessage: handleMessage,
|
|
834
|
+
onRenderProcessGone: (event) => {
|
|
835
|
+
var _webviewProps$onRende;
|
|
836
|
+
if (generation === generationRef.current) reload();
|
|
837
|
+
webviewProps === null || webviewProps === void 0 || (_webviewProps$onRende = webviewProps.onRenderProcessGone) === null || _webviewProps$onRende === void 0 || _webviewProps$onRende.call(webviewProps, event);
|
|
838
|
+
},
|
|
839
|
+
onContentProcessDidTerminate: (event) => {
|
|
840
|
+
var _webviewProps$onConte;
|
|
841
|
+
if (generation === generationRef.current) reload();
|
|
842
|
+
webviewProps === null || webviewProps === void 0 || (_webviewProps$onConte = webviewProps.onContentProcessDidTerminate) === null || _webviewProps$onConte === void 0 || _webviewProps$onConte.call(webviewProps, event);
|
|
843
|
+
}
|
|
667
844
|
}
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
|
|
845
|
+
}, `${identity}|${resumingTicketId !== null && resumingTicketId !== void 0 ? resumingTicketId : ""}|${generation}`)
|
|
846
|
+
})
|
|
847
|
+
]
|
|
671
848
|
})
|
|
672
849
|
});
|
|
673
|
-
}
|
|
850
|
+
});
|
|
851
|
+
LorikeetWidgetView.displayName = "LorikeetWidgetView";
|
|
674
852
|
|
|
675
853
|
//#endregion
|
|
676
854
|
export { LORIKEET_ERROR_CODES, LorikeetClient, LorikeetWidgetHeader, LorikeetWidgetView, LorikeetWidgetWebView, isUnusableConversation, isUnusableConversationCode };
|