@lorikeetai/react-native-sdk 2.0.0-alpha.3 → 2.1.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/README.md +117 -21
- package/dist/index.cjs +96 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +123 -66
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +123 -66
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +96 -86
- package/dist/index.js.map +1 -1
- package/package.json +7 -8
package/README.md
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
SDK containing embeddable React Native components that renders the LorikeetCX web widget inside a react-native-webview.
|
|
4
4
|
|
|
5
|
-
>
|
|
6
|
-
> token refresh are host responsibilities today; see [Resuming a
|
|
5
|
+
> Resume, storage and token refresh are host responsibilities; see [Resuming a
|
|
7
6
|
> conversation](#resuming-a-conversation) and [Identity](#identity).
|
|
8
7
|
|
|
9
8
|
## Installation
|
|
@@ -17,12 +16,11 @@ successfully, and the failure surfaces later, when Metro cannot resolve the
|
|
|
17
16
|
module.
|
|
18
17
|
Metro reports one missing module at a time, so install all three at once.
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
semver range reaches a prerelease either.
|
|
19
|
+
2.x is on `latest`, so a bare install resolves it. A host already on 1.x stays
|
|
20
|
+
there until it asks for the new major, because no 1.x range reaches it.
|
|
23
21
|
|
|
24
22
|
```bash
|
|
25
|
-
pnpm add @lorikeetai/react-native-sdk
|
|
23
|
+
pnpm add @lorikeetai/react-native-sdk \
|
|
26
24
|
react-native-webview@^13.15.0 \
|
|
27
25
|
react-native-safe-area-context@^5.6.0 \
|
|
28
26
|
react-native-svg@^15.13.0
|
|
@@ -42,9 +40,6 @@ major. Widening them is a change that needs testing, not a version bump.
|
|
|
42
40
|
- **`webviewProps` now reach the WebView.** The drop-in previously discarded
|
|
43
41
|
them. If you were passing props that silently did nothing, they take effect
|
|
44
42
|
now.
|
|
45
|
-
- **Lazy ticket creation.** A ticket is no longer created when the widget opens,
|
|
46
|
-
which changes when conversations appear in your Lorikeet queue and in anything
|
|
47
|
-
counting them.
|
|
48
43
|
|
|
49
44
|
## Basic Usage
|
|
50
45
|
|
|
@@ -54,7 +49,7 @@ import { LorikeetWidgetView } from '@lorikeetai/react-native-sdk'
|
|
|
54
49
|
export default function Screen() {
|
|
55
50
|
return (
|
|
56
51
|
<LorikeetWidgetView
|
|
57
|
-
publicKey="
|
|
52
|
+
publicKey="your-public-key"
|
|
58
53
|
authToken="your-auth-token"
|
|
59
54
|
onWebViewMessage={(event) => console.log(event.nativeEvent.data)}
|
|
60
55
|
/>
|
|
@@ -64,7 +59,9 @@ export default function Screen() {
|
|
|
64
59
|
|
|
65
60
|
**Props:**
|
|
66
61
|
|
|
67
|
-
- `publicKey` (required): The public key for your Lorikeet account
|
|
62
|
+
- `publicKey` (required): The public key issued for your Lorikeet account. Use the value you were
|
|
63
|
+
given rather than matching a shape: the format has changed over time, and keys issued under
|
|
64
|
+
earlier ones keep working
|
|
68
65
|
- `authToken` (required): Authentication token for the user
|
|
69
66
|
- `baseUrl` (optional): Base URL for the Lorikeet API (defaults to `https://chat.lorikeetcx.ai`)
|
|
70
67
|
- `customisations` (optional): Widget customisation options. Exported as
|
|
@@ -81,6 +78,48 @@ export default function Screen() {
|
|
|
81
78
|
defaults for media playback, scaling and the loading state can all be
|
|
82
79
|
overridden; `onMessage` is additive, so the SDK reads the bridge first and
|
|
83
80
|
then calls yours
|
|
81
|
+
- `avoidKeyboard` (optional): Pads the widget clear of the keyboard. Defaults to
|
|
82
|
+
`true`. See [The keyboard](#the-keyboard)
|
|
83
|
+
|
|
84
|
+
## The keyboard
|
|
85
|
+
|
|
86
|
+
Android apps drawn edge to edge, the default for current Expo and React Native
|
|
87
|
+
apps, no longer resize their window for the keyboard. `adjustResize` in the
|
|
88
|
+
manifest does not change that. Nothing shrinks the WebView, so the keyboard
|
|
89
|
+
covers the composer and the page is never told the keyboard is there.
|
|
90
|
+
|
|
91
|
+
`LorikeetWidgetView` handles this itself: it wraps its content in a
|
|
92
|
+
`KeyboardAvoidingView` with `behavior="padding"` on both platforms, and measures
|
|
93
|
+
its own position so it works below a navigation header or tab bar. On iOS the
|
|
94
|
+
same padding keeps the page's header on screen. If your screen already makes
|
|
95
|
+
room for the keyboard, pass `avoidKeyboard={false}`, or the widget is padded
|
|
96
|
+
twice.
|
|
97
|
+
|
|
98
|
+
If you compose the parts yourself, the keyboard is yours. Wrap them in a
|
|
99
|
+
`KeyboardAvoidingView` with `behavior="padding"` on both platforms, and set
|
|
100
|
+
`keyboardVerticalOffset` to their distance from the top of the screen.
|
|
101
|
+
`apps/rn-widget-demo/src/components/WidgetPane.tsx` measures it with
|
|
102
|
+
`measureInWindow`. Check it on an Android device: an iOS simulator will not
|
|
103
|
+
show the problem.
|
|
104
|
+
|
|
105
|
+
## Which header you get
|
|
106
|
+
|
|
107
|
+
`hideHeader` decides whose header the customer sees, not whether there is one.
|
|
108
|
+
`LorikeetWidgetView` defaults it to `true`.
|
|
109
|
+
|
|
110
|
+
| Value | The chat page's header | The SDK's own header |
|
|
111
|
+
| ------------------------------ | ---------------------- | -------------------------------------------------- |
|
|
112
|
+
| `false` | Shown | Not rendered |
|
|
113
|
+
| `true` (the drop-in's default) | Hidden | Rendered, with a new-chat control and nothing else |
|
|
114
|
+
|
|
115
|
+
The page's header is the one carrying the controls for leaving a conversation.
|
|
116
|
+
The SDK's carries a new-chat control and nothing else, so under the drop-in's
|
|
117
|
+
default a host that neither sets `hideHeader: false` nor builds its own header
|
|
118
|
+
leaves the customer no way out of the chat.
|
|
119
|
+
|
|
120
|
+
Which controls the page's header offers, and what they are called, depends on
|
|
121
|
+
the account. Build against "the page's own controls" rather than a fixed set,
|
|
122
|
+
and ask us what your account serves before reproducing them.
|
|
84
123
|
|
|
85
124
|
## Resuming a conversation
|
|
86
125
|
|
|
@@ -103,7 +142,7 @@ useEffect(() => {
|
|
|
103
142
|
|
|
104
143
|
return (
|
|
105
144
|
<LorikeetWidgetView
|
|
106
|
-
publicKey="
|
|
145
|
+
publicKey="your-public-key"
|
|
107
146
|
authToken={token}
|
|
108
147
|
ticketId={resume.ticketId}
|
|
109
148
|
session={resume.session}
|
|
@@ -156,6 +195,43 @@ not learn two vocabularies.
|
|
|
156
195
|
`preload_complete` is in the union but cannot reach React Native: the preload
|
|
157
196
|
page posts it to a web parent only. Do not wait on it.
|
|
158
197
|
|
|
198
|
+
## Minimise, end and new chat
|
|
199
|
+
|
|
200
|
+
`close` means the page hid itself. It may carry a `reason`: a minimise keeps the
|
|
201
|
+
conversation and offers a control that reopens it, an end offers one that starts a
|
|
202
|
+
replacement. Treat an absent reason as an end, because that is what the widget
|
|
203
|
+
does.
|
|
204
|
+
|
|
205
|
+
Do not unmount on `close`. The end and any CSAT rating are still in flight, and
|
|
206
|
+
tearing the page down cancels them. The widget hides the chat itself.
|
|
207
|
+
|
|
208
|
+
While `ticketId` is set, the new-chat control reloads that conversation rather
|
|
209
|
+
than replacing it. To start a new conversation, clear your stored `ticketId` and
|
|
210
|
+
`session` and stop passing them, so the next load falls back to `/widget`.
|
|
211
|
+
|
|
212
|
+
## The widget handle
|
|
213
|
+
|
|
214
|
+
Pass a `ref` to `LorikeetWidgetView` to reach these.
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
const widget = useRef<LorikeetWidgetHandle>(null)
|
|
218
|
+
|
|
219
|
+
widget.current?.getConversation() // { minimised, ticketId?, session? } | null
|
|
220
|
+
widget.current?.logout()
|
|
221
|
+
widget.current?.reopen()
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
- `getConversation()` reports the conversation the widget is on, which is not
|
|
225
|
+
always the one the host passed in: a refused resume is replaced, and a new chat
|
|
226
|
+
replaces the conversation before it. It returns `null` until the page has
|
|
227
|
+
reported a ticket, and it can report a ticket with no session, so it supplements
|
|
228
|
+
the persistence events rather than replacing them.
|
|
229
|
+
- `logout()` clears this customer's chat from the device. See
|
|
230
|
+
[Logging out](#logging-out).
|
|
231
|
+
- `reopen()` shows a minimised conversation again, exactly as the customer left
|
|
232
|
+
it. Nothing reloads and nothing is reported. A no-op unless the widget is
|
|
233
|
+
minimised, so a host with its own launcher can call it unconditionally.
|
|
234
|
+
|
|
159
235
|
## Logging out
|
|
160
236
|
|
|
161
237
|
Clearing your own stored `ticketId` and `session` is not enough. The chat keeps
|
|
@@ -196,11 +272,26 @@ previous customer's credentials still unlock. Call `logout()` again to retry.
|
|
|
196
272
|
That obligation outlives the component. The SDK stores nothing, so a widget
|
|
197
273
|
unmounted before the clear confirmed, by a screen change or an app restart,
|
|
198
274
|
comes back with no memory of it and opens a chat normally, on a device whose
|
|
199
|
-
cookies were never cleared.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
275
|
+
cookies were never cleared.
|
|
276
|
+
|
|
277
|
+
So persist the logout before you start it, rather than recording that it failed.
|
|
278
|
+
Write down the `baseUrl`, `publicKey` and cookie-store `webviewProps` you are
|
|
279
|
+
clearing, read it at launch, and delete it only once `logged_out` has arrived.
|
|
280
|
+
Waiting for `logout_failed` is not enough on its own: unmounting cancels its
|
|
281
|
+
timer, so an app that dies inside the ten seconds reports nothing at all.
|
|
282
|
+
|
|
283
|
+
Recovery then has to clear that recorded target rather than whatever the next
|
|
284
|
+
launch happens to configure. The page drops only that public key's entry, and
|
|
285
|
+
settings such as `sharedCookiesEnabled` decide which cookie store it reaches, so
|
|
286
|
+
clearing another account or another store reports `logged_out` just the same.
|
|
287
|
+
|
|
288
|
+
It does not need the customer who left, though. The page needs no `authToken`, so
|
|
289
|
+
a host that no longer holds the departed customer's, which after a restart is the
|
|
290
|
+
normal case, can still clear the device by navigating a WebView to
|
|
291
|
+
`getLogoutUrl()` and waiting for its confirmation, as below. Do not reach for the
|
|
292
|
+
drop-in's `logout()` here: it clears for whichever credentials are in props and
|
|
293
|
+
then will not open a chat for them until they change, so calling it with the next
|
|
294
|
+
customer's props locks that customer out instead of the last one.
|
|
204
295
|
|
|
205
296
|
If you compose the parts yourself, build the URL and navigate to it:
|
|
206
297
|
|
|
@@ -217,8 +308,7 @@ The anonymous customer's long-lived token is deliberately not among these
|
|
|
217
308
|
events. It identifies the same anonymous customer for a year, this SDK offers
|
|
218
309
|
no way to hand one back, and the web SDK keeps the same message inside the
|
|
219
310
|
frame. A host that genuinely needs it can read the raw bridge, but storing it
|
|
220
|
-
is storing a credential
|
|
221
|
-
is the work that should remove the need.
|
|
311
|
+
is storing a credential.
|
|
222
312
|
|
|
223
313
|
## Identity
|
|
224
314
|
|
|
@@ -309,8 +399,14 @@ WebView component that handles the widget content.
|
|
|
309
399
|
|
|
310
400
|
## Development
|
|
311
401
|
|
|
312
|
-
|
|
313
|
-
|
|
402
|
+
This package is outside the root pnpm workspace, on its own lockfile, so run these from `packages/react-native-widget` after `pnpm install` there.
|
|
403
|
+
|
|
404
|
+
- Build: `pnpm build`
|
|
405
|
+
- Test: `pnpm test`
|
|
406
|
+
- Typecheck: `pnpm typecheck`
|
|
407
|
+
- Lint: `pnpm lint`. It uses the root's oxlint and oxfmt, so run `pnpm install` at the repository root first.
|
|
408
|
+
|
|
409
|
+
To release, bump `version` in `package.json` and add its `CHANGELOG.md` section in the same pull request. See `docs/engineering/sdk-releases.md`.
|
|
314
410
|
|
|
315
411
|
## License
|
|
316
412
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
|
|
2
|
+
// \0rolldown/runtime.js
|
|
3
3
|
var __create = Object.create;
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -25,7 +25,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
enumerable: true
|
|
26
26
|
}) : target, mod));
|
|
27
27
|
|
|
28
|
-
//#endregion
|
|
29
28
|
let react = require("react");
|
|
30
29
|
let react_native = require("react-native");
|
|
31
30
|
let react_native_safe_area_context = require("react-native-safe-area-context");
|
|
@@ -34,7 +33,7 @@ let react_native_svg = require("react-native-svg");
|
|
|
34
33
|
react_native_svg = __toESM(react_native_svg, 1);
|
|
35
34
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
// src/bridge.ts
|
|
38
37
|
/**
|
|
39
38
|
* The public key, base URL and token are one unit, and a conversation belongs to
|
|
40
39
|
* the credentials that created it. Stamping an observation with them is what
|
|
@@ -54,8 +53,7 @@ const resumableConversation = (observed, identity, intendedTicketId) => {
|
|
|
54
53
|
};
|
|
55
54
|
};
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
//#region src/encode.ts
|
|
56
|
+
// src/encode.ts
|
|
59
57
|
const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
60
58
|
/**
|
|
61
59
|
* Base64 of a string's UTF-8 bytes.
|
|
@@ -82,8 +80,7 @@ function base64EncodeUtf8(value) {
|
|
|
82
80
|
return encoded;
|
|
83
81
|
}
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
//#region \0@oxc-project+runtime@0.147.0/helpers/esm/typeof.js
|
|
83
|
+
// \0@oxc-project+runtime@0.156.0/helpers/esm/typeof.js
|
|
87
84
|
function _typeof(o) {
|
|
88
85
|
"@babel/helpers - typeof";
|
|
89
86
|
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
@@ -93,8 +90,7 @@ function _typeof(o) {
|
|
|
93
90
|
}, _typeof(o);
|
|
94
91
|
}
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
//#region \0@oxc-project+runtime@0.147.0/helpers/esm/toPrimitive.js
|
|
93
|
+
// \0@oxc-project+runtime@0.156.0/helpers/esm/toPrimitive.js
|
|
98
94
|
function toPrimitive(t, r) {
|
|
99
95
|
if ("object" != _typeof(t) || !t) return t;
|
|
100
96
|
var e = t[Symbol.toPrimitive];
|
|
@@ -106,15 +102,13 @@ function toPrimitive(t, r) {
|
|
|
106
102
|
return ("string" === r ? String : Number)(t);
|
|
107
103
|
}
|
|
108
104
|
|
|
109
|
-
|
|
110
|
-
//#region \0@oxc-project+runtime@0.147.0/helpers/esm/toPropertyKey.js
|
|
105
|
+
// \0@oxc-project+runtime@0.156.0/helpers/esm/toPropertyKey.js
|
|
111
106
|
function toPropertyKey(t) {
|
|
112
107
|
var i = toPrimitive(t, "string");
|
|
113
108
|
return "symbol" == _typeof(i) ? i : i + "";
|
|
114
109
|
}
|
|
115
110
|
|
|
116
|
-
|
|
117
|
-
//#region \0@oxc-project+runtime@0.147.0/helpers/esm/defineProperty.js
|
|
111
|
+
// \0@oxc-project+runtime@0.156.0/helpers/esm/defineProperty.js
|
|
118
112
|
function _defineProperty(e, r, t) {
|
|
119
113
|
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
120
114
|
value: t,
|
|
@@ -124,8 +118,7 @@ function _defineProperty(e, r, t) {
|
|
|
124
118
|
}) : e[r] = t, e;
|
|
125
119
|
}
|
|
126
120
|
|
|
127
|
-
|
|
128
|
-
//#region src/client.ts
|
|
121
|
+
// src/client.ts
|
|
129
122
|
var LorikeetClient = class {
|
|
130
123
|
constructor({ customisations, api_base, metadata, public_key }) {
|
|
131
124
|
_defineProperty(this, "baseUrl", "https://chat.lorikeetcx.ai");
|
|
@@ -171,12 +164,16 @@ var LorikeetClient = class {
|
|
|
171
164
|
}
|
|
172
165
|
};
|
|
173
166
|
|
|
174
|
-
|
|
175
|
-
//#region src/errors.ts
|
|
167
|
+
// src/errors.ts
|
|
176
168
|
/**
|
|
177
|
-
*
|
|
178
|
-
* Mirrored here because the page publishes
|
|
179
|
-
* otherwise be matching on strings it discovered
|
|
169
|
+
* The chat page's error vocabulary, as it reaches a host in
|
|
170
|
+
* `{ type: 'error', error: CODE }`. Mirrored here because the page publishes it
|
|
171
|
+
* nowhere else, so a host would otherwise be matching on strings it discovered
|
|
172
|
+
* by observation.
|
|
173
|
+
*
|
|
174
|
+
* The page's own list rather than a list of codes seen in the wild:
|
|
175
|
+
* `MISSING_PUBLIC_KEY` has no producer today, and is kept because narrowing this
|
|
176
|
+
* would be a breaking change.
|
|
180
177
|
*/
|
|
181
178
|
const LORIKEET_ERROR_CODES = [
|
|
182
179
|
"MISSING_TOKEN",
|
|
@@ -214,8 +211,7 @@ function isUnusableConversation(message) {
|
|
|
214
211
|
return type === "error" && typeof error === "string" ? isUnusableConversationCode(error) : false;
|
|
215
212
|
}
|
|
216
213
|
|
|
217
|
-
|
|
218
|
-
//#region src/events.ts
|
|
214
|
+
// src/events.ts
|
|
219
215
|
const asRecord = (message) => typeof message === "object" && message !== null && !Array.isArray(message) ? message : null;
|
|
220
216
|
const asNonEmptyString = (value) => typeof value === "string" && value !== "" ? value : null;
|
|
221
217
|
/**
|
|
@@ -293,8 +289,7 @@ const resumedTicketIdFromUri = (uri) => {
|
|
|
293
289
|
return (_segments$ = segments[2]) !== null && _segments$ !== void 0 ? _segments$ : null;
|
|
294
290
|
};
|
|
295
291
|
|
|
296
|
-
|
|
297
|
-
//#region src/lifecycle.ts
|
|
292
|
+
// src/lifecycle.ts
|
|
298
293
|
/**
|
|
299
294
|
* The page gives its message stream ten attempts with exponential backoff
|
|
300
295
|
* capped at 30s (`message-stream-utils.tsx`), so the budget runs out roughly
|
|
@@ -331,8 +326,7 @@ const shouldReloadAfterBackground = (backgroundedAt, now, thresholdMs = BACKGROU
|
|
|
331
326
|
return elapsed >= thresholdMs;
|
|
332
327
|
};
|
|
333
328
|
|
|
334
|
-
|
|
335
|
-
//#region src/links.ts
|
|
329
|
+
// src/links.ts
|
|
336
330
|
/**
|
|
337
331
|
* Schemes the WebView resolves for itself. None of them is a page the customer
|
|
338
332
|
* asked to leave the app for, and the OS cannot open them anyway.
|
|
@@ -377,8 +371,7 @@ const isInternalNavigation = (url, baseUrl) => {
|
|
|
377
371
|
return target.origin === base.origin;
|
|
378
372
|
};
|
|
379
373
|
|
|
380
|
-
|
|
381
|
-
//#region src/message-circle-plus-icon.tsx
|
|
374
|
+
// src/message-circle-plus-icon.tsx
|
|
382
375
|
const MessageCirclePlusIcon = ({ size = 24, color = "currentColor", strokeWidth = 2 }) => {
|
|
383
376
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native_svg.default, {
|
|
384
377
|
fill: "none",
|
|
@@ -415,13 +408,13 @@ const MessageCirclePlusIcon = ({ size = 24, color = "currentColor", strokeWidth
|
|
|
415
408
|
});
|
|
416
409
|
};
|
|
417
410
|
|
|
418
|
-
|
|
419
|
-
//#region src/widget.tsx
|
|
411
|
+
// src/widget.tsx
|
|
420
412
|
const stylesheet = react_native.StyleSheet.create({
|
|
421
413
|
root: {
|
|
422
414
|
flex: 1,
|
|
423
415
|
backgroundColor: "#fff"
|
|
424
416
|
},
|
|
417
|
+
keyboardAvoiding: { flex: 1 },
|
|
425
418
|
container: {
|
|
426
419
|
flex: 1,
|
|
427
420
|
borderRadius: 8,
|
|
@@ -525,7 +518,7 @@ const LorikeetWidgetHeader = ({ title, style, icon = /* @__PURE__ */ (0, react_j
|
|
|
525
518
|
]
|
|
526
519
|
});
|
|
527
520
|
};
|
|
528
|
-
const LorikeetWidgetView = (0, react.forwardRef)(function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, styles, onEvent, onWebViewMessage, ticketId, session, metadata, webviewProps }, ref) {
|
|
521
|
+
const LorikeetWidgetView = (0, react.forwardRef)(function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, styles, onEvent, onWebViewMessage, ticketId, session, metadata, webviewProps, avoidKeyboard = true }, ref) {
|
|
529
522
|
var _customisations$hideH, _customisations$title;
|
|
530
523
|
const [lorikeetClient, setLorikeetClient] = (0, react.useState)(null);
|
|
531
524
|
const [loaded, setLoaded] = (0, react.useState)(null);
|
|
@@ -536,6 +529,15 @@ const LorikeetWidgetView = (0, react.forwardRef)(function LorikeetWidgetView({ p
|
|
|
536
529
|
setGeneration((previous) => previous + 1);
|
|
537
530
|
}, []);
|
|
538
531
|
const webViewRef = (0, react.useRef)(null);
|
|
532
|
+
const containerRef = (0, react.useRef)(null);
|
|
533
|
+
const [keyboardOffset, setKeyboardOffset] = (0, react.useState)(0);
|
|
534
|
+
const measureKeyboardOffset = (0, react.useCallback)((event) => {
|
|
535
|
+
var _containerRef$current;
|
|
536
|
+
const { y } = event.nativeEvent.layout;
|
|
537
|
+
(_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 || _containerRef$current.measureInWindow((_x, windowY) => {
|
|
538
|
+
setKeyboardOffset(windowY - y);
|
|
539
|
+
});
|
|
540
|
+
}, []);
|
|
539
541
|
const [refusedTicketId, setRefusedTicketId] = (0, react.useState)(null);
|
|
540
542
|
const observedRef = (0, react.useRef)(null);
|
|
541
543
|
const backgroundedAtRef = (0, react.useRef)(null);
|
|
@@ -823,68 +825,75 @@ const LorikeetWidgetView = (0, react.forwardRef)(function LorikeetWidgetView({ p
|
|
|
823
825
|
"right"
|
|
824
826
|
],
|
|
825
827
|
style: stylesheet.root,
|
|
826
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
style:
|
|
839
|
-
|
|
840
|
-
})
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
})
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
828
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.KeyboardAvoidingView, {
|
|
829
|
+
behavior: "padding",
|
|
830
|
+
enabled: avoidKeyboard,
|
|
831
|
+
keyboardVerticalOffset: keyboardOffset,
|
|
832
|
+
onLayout: measureKeyboardOffset,
|
|
833
|
+
style: stylesheet.keyboardAvoiding,
|
|
834
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
|
|
835
|
+
ref: containerRef,
|
|
836
|
+
style: stylesheet.container,
|
|
837
|
+
children: [
|
|
838
|
+
hideHeader && closed !== "minimised" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LorikeetWidgetHeader, {
|
|
839
|
+
onIconPress: startNewChat,
|
|
840
|
+
style: styles === null || styles === void 0 ? void 0 : styles.header,
|
|
841
|
+
title: (_customisations$title = customisations === null || customisations === void 0 ? void 0 : customisations.title) !== null && _customisations$title !== void 0 ? _customisations$title : "Lorikeet AI Agent"
|
|
842
|
+
}) : null,
|
|
843
|
+
closed === "minimised" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
|
|
844
|
+
onPress: reopen,
|
|
845
|
+
style: stylesheet.closedControl,
|
|
846
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
|
|
847
|
+
style: stylesheet.headerText,
|
|
848
|
+
children: "Reopen chat"
|
|
849
|
+
})
|
|
850
|
+
}) : null,
|
|
851
|
+
!hideHeader && closed === "ended" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
|
|
852
|
+
onPress: startNewChat,
|
|
853
|
+
style: stylesheet.closedControl,
|
|
854
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
|
|
855
|
+
style: stylesheet.headerText,
|
|
856
|
+
children: "Start new chat"
|
|
857
|
+
})
|
|
858
|
+
}) : null,
|
|
859
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
860
|
+
accessibilityElementsHidden: isClosed,
|
|
861
|
+
importantForAccessibility: isClosed ? "no-hide-descendants" : "auto",
|
|
862
|
+
pointerEvents: isClosed ? "none" : "auto",
|
|
863
|
+
style: isClosed ? stylesheet.closed : stylesheet.webView,
|
|
864
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LorikeetWidgetWebView, {
|
|
865
|
+
baseUrl: lorikeetClient.baseUrl,
|
|
866
|
+
onEvent: handleEvent,
|
|
867
|
+
ref: webViewRef,
|
|
868
|
+
uri: loaded.uri,
|
|
869
|
+
webviewProps: {
|
|
870
|
+
allowsInlineMediaPlayback: true,
|
|
871
|
+
mediaPlaybackRequiresUserAction: false,
|
|
872
|
+
scalesPageToFit: true,
|
|
873
|
+
startInLoadingState: true,
|
|
874
|
+
style: stylesheet.webView,
|
|
875
|
+
...webviewProps,
|
|
876
|
+
onMessage: handleMessage,
|
|
877
|
+
onRenderProcessGone: (event) => {
|
|
878
|
+
var _webviewProps$onRende;
|
|
879
|
+
if (generation === generationRef.current) reload();
|
|
880
|
+
webviewProps === null || webviewProps === void 0 || (_webviewProps$onRende = webviewProps.onRenderProcessGone) === null || _webviewProps$onRende === void 0 || _webviewProps$onRende.call(webviewProps, event);
|
|
881
|
+
},
|
|
882
|
+
onContentProcessDidTerminate: (event) => {
|
|
883
|
+
var _webviewProps$onConte;
|
|
884
|
+
if (generation === generationRef.current) reload();
|
|
885
|
+
webviewProps === null || webviewProps === void 0 || (_webviewProps$onConte = webviewProps.onContentProcessDidTerminate) === null || _webviewProps$onConte === void 0 || _webviewProps$onConte.call(webviewProps, event);
|
|
886
|
+
}
|
|
877
887
|
}
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
|
|
888
|
+
}, `${identity}|${resumingTicketId !== null && resumingTicketId !== void 0 ? resumingTicketId : ""}|${generation}`)
|
|
889
|
+
})
|
|
890
|
+
]
|
|
891
|
+
})
|
|
882
892
|
})
|
|
883
893
|
});
|
|
884
894
|
});
|
|
885
895
|
LorikeetWidgetView.displayName = "LorikeetWidgetView";
|
|
886
896
|
|
|
887
|
-
//#endregion
|
|
888
897
|
exports.LOGOUT_CONFIRMATION_TIMEOUT_MS = LOGOUT_CONFIRMATION_TIMEOUT_MS;
|
|
889
898
|
exports.LORIKEET_ERROR_CODES = LORIKEET_ERROR_CODES;
|
|
890
899
|
exports.LorikeetClient = LorikeetClient;
|