@affiliateo/web 4.9.0 → 4.9.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 +15 -2
- package/dist/chat.d.ts +13 -0
- package/dist/chat.js +19 -2
- package/dist/provider.js +11 -1
- package/dist/queue.js +7 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -233,7 +233,8 @@ signs in next (or nobody) isn't counted as the person who just left. Their Live
|
|
|
233
233
|
Chat goes too: the next person to open the chat starts a new thread.
|
|
234
234
|
Everything already recorded stays with them. Tracking carries on as before:
|
|
235
235
|
same visitor id, same affiliate. If a different user signs in without a
|
|
236
|
-
sign-out in between, the browser switches to them on its own
|
|
236
|
+
sign-out in between, the browser switches to them on its own, and the last
|
|
237
|
+
person's Live Chat thread is forgotten first.
|
|
237
238
|
|
|
238
239
|
## Live Chat
|
|
239
240
|
|
|
@@ -254,7 +255,7 @@ import { openChat } from '@affiliateo/web'
|
|
|
254
255
|
// Never load the chat
|
|
255
256
|
<AffiliateoProvider appId="..." chat={false}>
|
|
256
257
|
|
|
257
|
-
// Your own look, over the dashboard's (4.
|
|
258
|
+
// Your own look, over the dashboard's (4.9.0); leave out what the dashboard should decide
|
|
258
259
|
<AffiliateoProvider appId="..." chatLook={{ color: '#7C3AED', iconColor: '#FFFFFF', sendColor: '#007AFF', placeholder: 'Ask us anything', style: 'glass', corner: 'bottom-left', size: 56 }}>
|
|
259
260
|
```
|
|
260
261
|
|
|
@@ -269,6 +270,18 @@ the script tag, `data-chat-color`, `data-chat-icon-color`, `data-chat-send-color
|
|
|
269
270
|
|
|
270
271
|
## Changelog
|
|
271
272
|
|
|
273
|
+
- **4.9.2:** events are sent as plain text, so the browser no longer makes an
|
|
274
|
+
extra check request before each one. Nothing to change in your code.
|
|
275
|
+
- **4.9.1:** a different user signing in with `identify()` (no `logout()` in
|
|
276
|
+
between) now forgets the last person's Live Chat thread first, as a sign-out
|
|
277
|
+
does, and the bubble learns the new user without a reload. Signing in from
|
|
278
|
+
anonymous keeps the person's own thread. With it, from the bubble itself
|
|
279
|
+
(chat.js, served by Affiliateo, so every site gets it with no update): its
|
|
280
|
+
screen-reader labels in the visitor's language, the visitor's chat key no
|
|
281
|
+
longer in the live connection's address (a one-minute ticket instead), only
|
|
282
|
+
the page's address without its query or fragment sent to the inbox, and a
|
|
283
|
+
size set in code kept within 40 to 80.
|
|
284
|
+
|
|
272
285
|
- **4.9.0:** Live Chat (see above): the chat bubble loads by itself once the
|
|
273
286
|
business switches it on; new `chat` prop and `openChat()`, `closeChat()`,
|
|
274
287
|
`showChatBubble()`, `hideChatBubble()`. `logout()` and `reset()` forget the
|
package/dist/chat.d.ts
CHANGED
|
@@ -18,6 +18,12 @@ export interface LoadLiveChatOptions {
|
|
|
18
18
|
visitorId: string | null;
|
|
19
19
|
/** The signed-in user id from identify(). Null while opted out. */
|
|
20
20
|
userId: string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Read again just before chat.js is added (4.9.1): a sign-in or a switch
|
|
23
|
+
* of person while the config check is on its way must not load the bubble
|
|
24
|
+
* with the last person's id. Falls back to `userId`.
|
|
25
|
+
*/
|
|
26
|
+
getUserId?: () => string | null;
|
|
21
27
|
/** 'hidden' loads the chat without a bubble, for a site that opens it from its own button. */
|
|
22
28
|
bubble?: 'visible' | 'hidden';
|
|
23
29
|
theme?: 'light' | 'dark';
|
|
@@ -34,6 +40,13 @@ export declare function closeChat(): void;
|
|
|
34
40
|
export declare function showChatBubble(): void;
|
|
35
41
|
/** Hide the chat bubble, and close the window. */
|
|
36
42
|
export declare function hideChatBubble(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Who is signed in now (identify(), 4.9.1). chat.js reads the user once, when
|
|
45
|
+
* it loads: a sign-in after that reaches it through __setUser, which also
|
|
46
|
+
* forgets a DIFFERENT person's thread (signing in from anonymous keeps it).
|
|
47
|
+
* Still loading: it reads data-user when it runs, so that is updated.
|
|
48
|
+
*/
|
|
49
|
+
export declare function setLiveChatUser(userId: string): void;
|
|
37
50
|
/**
|
|
38
51
|
* The person signed out (logout(), reset()): their chat goes, so whoever
|
|
39
52
|
* signs in next starts a new thread. The token chat.js keeps goes from
|
package/dist/chat.js
CHANGED
|
@@ -54,8 +54,9 @@ export function loadLiveChat(opts) {
|
|
|
54
54
|
script.setAttribute('data-app', opts.appId);
|
|
55
55
|
if (opts.visitorId)
|
|
56
56
|
script.setAttribute('data-visitor', opts.visitorId);
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
const userId = opts.getUserId ? opts.getUserId() : opts.userId;
|
|
58
|
+
if (userId)
|
|
59
|
+
script.setAttribute('data-user', userId.slice(0, 128));
|
|
59
60
|
if (opts.bubble === 'hidden')
|
|
60
61
|
script.setAttribute('data-bubble', 'hidden');
|
|
61
62
|
if (opts.theme)
|
|
@@ -96,6 +97,22 @@ export function showChatBubble() {
|
|
|
96
97
|
export function hideChatBubble() {
|
|
97
98
|
chatGlobal()?.hide?.();
|
|
98
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Who is signed in now (identify(), 4.9.1). chat.js reads the user once, when
|
|
102
|
+
* it loads: a sign-in after that reaches it through __setUser, which also
|
|
103
|
+
* forgets a DIFFERENT person's thread (signing in from anonymous keeps it).
|
|
104
|
+
* Still loading: it reads data-user when it runs, so that is updated.
|
|
105
|
+
*/
|
|
106
|
+
export function setLiveChatUser(userId) {
|
|
107
|
+
if (typeof window === 'undefined' || typeof document === 'undefined')
|
|
108
|
+
return;
|
|
109
|
+
const chat = chatGlobal();
|
|
110
|
+
if (chat?.__setUser) {
|
|
111
|
+
chat.__setUser(userId);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
document.querySelector('script[data-affiliateo-chat]')?.setAttribute('data-user', userId.slice(0, 128));
|
|
115
|
+
}
|
|
99
116
|
/**
|
|
100
117
|
* The person signed out (logout(), reset()): their chat goes, so whoever
|
|
101
118
|
* signs in next starts a new thread. The token chat.js keeps goes from
|
package/dist/provider.js
CHANGED
|
@@ -6,7 +6,7 @@ import { isLikelyBot } from './bot-detect';
|
|
|
6
6
|
import { isDevHost } from './dev-host';
|
|
7
7
|
import { mintTurnstileToken, consumeTurnstileToken } from './turnstile';
|
|
8
8
|
import { usableUserId } from './user-id';
|
|
9
|
-
import { forgetLiveChat, loadLiveChat } from './chat';
|
|
9
|
+
import { forgetLiveChat, loadLiveChat, setLiveChatUser } from './chat';
|
|
10
10
|
const DEFAULT_API_URL = 'https://affiliateo.com';
|
|
11
11
|
const LS_VISITOR_ID = 'affiliateo_visitor_id';
|
|
12
12
|
const LS_REF = 'affiliateo_ref';
|
|
@@ -336,7 +336,15 @@ export function AffiliateoProvider({ appId, campaignId, apiUrl, allowedHostnames
|
|
|
336
336
|
log('identify skipped: not a user id (empty, over 128 characters, or a placeholder like "null", "guest" or "0")', { user_id: userId });
|
|
337
337
|
return;
|
|
338
338
|
}
|
|
339
|
+
// A DIFFERENT person signing in on this browser (4.9.1): the last
|
|
340
|
+
// person's Live Chat thread was theirs, so it goes first, as on a
|
|
341
|
+
// sign-out. Signing in from anonymous keeps it (the same person who
|
|
342
|
+
// was writing). chat.js is told who is signed in now either way.
|
|
343
|
+
const previous = usableUserId(lsGet(LS_USER_ID));
|
|
344
|
+
if (previous && previous !== uid)
|
|
345
|
+
forgetLiveChat(appIdRef.current);
|
|
339
346
|
lsSet(LS_USER_ID, uid);
|
|
347
|
+
setLiveChatUser(uid);
|
|
340
348
|
const cid = appIdRef.current;
|
|
341
349
|
const vid = visitorIdRef.current;
|
|
342
350
|
const queue = queueRef.current;
|
|
@@ -1042,6 +1050,8 @@ export function AffiliateoProvider({ appId, campaignId, apiUrl, allowedHostnames
|
|
|
1042
1050
|
appId: resolvedAppId,
|
|
1043
1051
|
visitorId: optedOut ? null : visitorIdRef.current ?? lsGet(LS_VISITOR_ID),
|
|
1044
1052
|
userId: optedOut ? null : usableUserId(lsGet(LS_USER_ID)),
|
|
1053
|
+
// Read again when the bubble is added: a sign-in meanwhile counts.
|
|
1054
|
+
getUserId: () => (isOptedOut() ? null : usableUserId(lsGet(LS_USER_ID))),
|
|
1045
1055
|
bubble: chat === 'hidden' ? 'hidden' : 'visible',
|
|
1046
1056
|
look: chatLook,
|
|
1047
1057
|
log,
|
package/dist/queue.js
CHANGED
|
@@ -46,6 +46,10 @@ const MAX_RETRIES = 3;
|
|
|
46
46
|
const DEFAULT_MAX_QUEUE_SIZE = 100;
|
|
47
47
|
const DEFAULT_FLUSH_INTERVAL_MS = 5000;
|
|
48
48
|
const SIZE_FLUSH_THRESHOLD = 10;
|
|
49
|
+
// Plain text, not application/json: a JSON body makes the browser send an
|
|
50
|
+
// extra OPTIONS request before every event. The server reads the body as
|
|
51
|
+
// text and parses it itself, so nothing else changes.
|
|
52
|
+
const BODY_TYPE = 'text/plain;charset=UTF-8';
|
|
49
53
|
function lsGet(key) {
|
|
50
54
|
try {
|
|
51
55
|
return localStorage.getItem(key);
|
|
@@ -188,7 +192,7 @@ export class EventQueue {
|
|
|
188
192
|
try {
|
|
189
193
|
const res = await fetch(event.endpoint, {
|
|
190
194
|
method: 'POST',
|
|
191
|
-
headers: { 'Content-Type':
|
|
195
|
+
headers: { 'Content-Type': BODY_TYPE },
|
|
192
196
|
body: JSON.stringify(event.payload),
|
|
193
197
|
// keepalive lets the request survive a navigation. paired
|
|
194
198
|
// with the queue so even if this fetch errors, we'll retry.
|
|
@@ -243,7 +247,7 @@ export class EventQueue {
|
|
|
243
247
|
try {
|
|
244
248
|
fetch(event.endpoint, {
|
|
245
249
|
method: 'POST',
|
|
246
|
-
headers: { 'Content-Type':
|
|
250
|
+
headers: { 'Content-Type': BODY_TYPE },
|
|
247
251
|
body: JSON.stringify(event.payload),
|
|
248
252
|
keepalive: true,
|
|
249
253
|
}).catch(() => { });
|
|
@@ -256,7 +260,7 @@ export class EventQueue {
|
|
|
256
260
|
}
|
|
257
261
|
for (const event of this.queue) {
|
|
258
262
|
try {
|
|
259
|
-
navigator.sendBeacon(event.endpoint, new Blob([JSON.stringify(event.payload)], { type:
|
|
263
|
+
navigator.sendBeacon(event.endpoint, new Blob([JSON.stringify(event.payload)], { type: BODY_TYPE }));
|
|
260
264
|
}
|
|
261
265
|
catch { /* swallow */ }
|
|
262
266
|
}
|