@mrrocket/sdk 0.17.0 → 0.21.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/CHANGELOG.md +30 -1
- package/README.md +24 -1
- package/dist/api-client.d.ts +28 -4
- package/dist/api-client.js +30 -13
- package/dist/api-client.js.map +1 -1
- package/dist/browser/mrrocket-flows.js +15 -3
- package/dist/browser/mrrocket-messenger.js +38 -5
- package/dist/browser/mrrocket.js +1 -1
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +2 -1
- package/dist/browser.js.map +1 -1
- package/dist/client.d.ts +28 -3
- package/dist/client.js +37 -5
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +3 -1
- package/dist/context.js.map +1 -1
- package/dist/core.d.ts +7 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/feedback.js +161 -119
- package/dist/feedback.js.map +1 -1
- package/dist/finish-web.d.ts +18 -0
- package/dist/finish-web.js +139 -0
- package/dist/finish-web.js.map +1 -0
- package/dist/flows-ui.d.ts +1 -10
- package/dist/flows-ui.js +175 -48
- package/dist/flows-ui.js.map +1 -1
- package/dist/flows-web.d.ts +8 -1
- package/dist/flows-web.js +10 -1
- package/dist/flows-web.js.map +1 -1
- package/dist/flows.d.ts +193 -9
- package/dist/flows.js +284 -38
- package/dist/flows.js.map +1 -1
- package/dist/i18n.d.ts +22 -0
- package/dist/i18n.js +74 -0
- package/dist/i18n.js.map +1 -0
- package/dist/icons.d.ts +24 -0
- package/dist/icons.js +61 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/messenger-ui.d.ts +9 -3
- package/dist/messenger-ui.js +388 -191
- package/dist/messenger-ui.js.map +1 -1
- package/dist/messenger.d.ts +3 -1
- package/dist/messenger.js +3 -2
- package/dist/messenger.js.map +1 -1
- package/dist/onboarding-state.d.ts +23 -0
- package/dist/onboarding-state.js +43 -0
- package/dist/onboarding-state.js.map +1 -0
- package/dist/paywall.js +2 -1
- package/dist/paywall.js.map +1 -1
- package/dist/react-native/index.d.cts +239 -19
- package/dist/react-native/index.d.ts +239 -19
- package/dist/react-native/index.js +1305 -518
- package/dist/react-native/index.js.map +1 -1
- package/dist/server.d.ts +4 -2
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/styles-css.d.ts +1 -1
- package/dist/styles-css.js +35 -2
- package/dist/styles-css.js.map +1 -1
- package/dist/survey.js +18 -14
- package/dist/survey.js.map +1 -1
- package/dist/ui.d.ts +2 -0
- package/dist/ui.js +15 -12
- package/dist/ui.js.map +1 -1
- package/package.json +6 -3
package/dist/messenger-ui.js
CHANGED
|
@@ -8,35 +8,40 @@ import {
|
|
|
8
8
|
inkOn,
|
|
9
9
|
replyLine
|
|
10
10
|
} from "./api-client.js";
|
|
11
|
+
import { currentLocale, onWords, t, tf, wordsLang, wordsVersion } from "./i18n.js";
|
|
11
12
|
import { injectStyles } from "./styles-css.js";
|
|
13
|
+
var UPDATES_SHOWN = 20;
|
|
12
14
|
var POLL_OPEN_MS = 4e3;
|
|
13
15
|
var POLL_WAITING_MS = 8e3;
|
|
14
16
|
var POLL_IDLE_MS = 3e4;
|
|
17
|
+
var POLL_QUIET_MS = 6e4;
|
|
15
18
|
var WAITING_FOR_MS = 10 * 6e4;
|
|
19
|
+
var SLOW_SEND_MS = 1500;
|
|
16
20
|
var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
|
|
17
21
|
var SEEN_KEY = "ckw_msgr_seen";
|
|
22
|
+
var SEEN_CHATS_KEY = "ckw_msgr_seen_chats";
|
|
18
23
|
var readSeen = () => {
|
|
19
24
|
try {
|
|
20
|
-
return window.localStorage.getItem(SEEN_KEY) ?? "";
|
|
25
|
+
return { old: window.localStorage.getItem(SEEN_KEY) ?? "", chats: JSON.parse(window.localStorage.getItem(SEEN_CHATS_KEY) ?? "{}") };
|
|
21
26
|
} catch {
|
|
22
|
-
return "";
|
|
27
|
+
return { old: "", chats: {} };
|
|
23
28
|
}
|
|
24
29
|
};
|
|
25
|
-
var writeSeen = (
|
|
30
|
+
var writeSeen = (chats) => {
|
|
26
31
|
try {
|
|
27
|
-
window.localStorage.setItem(
|
|
32
|
+
window.localStorage.setItem(SEEN_CHATS_KEY, JSON.stringify(chats));
|
|
28
33
|
} catch {
|
|
29
34
|
}
|
|
30
35
|
};
|
|
31
36
|
var timeAgo = (iso) => {
|
|
32
|
-
const
|
|
33
|
-
if (Number.isNaN(
|
|
34
|
-
const d = Math.max(0, Date.now() -
|
|
35
|
-
if (d < 60) return "just now";
|
|
36
|
-
if (d < 3600) return
|
|
37
|
-
if (d < 86400) return
|
|
38
|
-
if (d < 86400 * 30) return
|
|
39
|
-
return new Date(iso).toLocaleDateString(
|
|
37
|
+
const at = new Date(iso).getTime();
|
|
38
|
+
if (Number.isNaN(at)) return "";
|
|
39
|
+
const d = Math.max(0, Date.now() - at) / 1e3;
|
|
40
|
+
if (d < 60) return t("just now");
|
|
41
|
+
if (d < 3600) return tf("{n}m ago", { n: Math.floor(d / 60) });
|
|
42
|
+
if (d < 86400) return tf("{n}h ago", { n: Math.floor(d / 3600) });
|
|
43
|
+
if (d < 86400 * 30) return tf("{n}d ago", { n: Math.floor(d / 86400) });
|
|
44
|
+
return new Date(iso).toLocaleDateString(currentLocale(), { month: "short", day: "numeric", year: "numeric" });
|
|
40
45
|
};
|
|
41
46
|
var el = (tag, attrs = {}, ...kids) => {
|
|
42
47
|
const e = document.createElement(tag);
|
|
@@ -71,11 +76,12 @@ var I = {
|
|
|
71
76
|
minus: "M5 12h14",
|
|
72
77
|
down: "m6 9 6 6 6-6",
|
|
73
78
|
help: "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20zM9.1 9a3 3 0 0 1 5.8 1c0 2-3 3-3 3M12 17h.01",
|
|
79
|
+
updates: "m3 11 18-5v12L3 14zM11.6 16.8a3 3 0 1 1-5.8-1.6",
|
|
74
80
|
clip: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48",
|
|
75
81
|
image: "M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zM9 11a2 2 0 1 0 0-4 2 2 0 0 0 0 4zM21 15l-3.09-3.09a2 2 0 0 0-2.82 0L6 21",
|
|
76
82
|
screen: "M20 3H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zM8 21h8M12 17v4"
|
|
77
83
|
};
|
|
78
|
-
var lineOf = (m) => m.body || (m.attachments?.length === 1 ?
|
|
84
|
+
var lineOf = (m) => m.body || (m.attachments?.length === 1 ? tf("Sent {name}", { name: m.attachments[0].name }) : m.attachments?.length ? tf("Sent {n} files", { n: m.attachments.length }) : "");
|
|
79
85
|
var canShoot = () => typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getDisplayMedia === "function";
|
|
80
86
|
var BUBBLE = "M7.9 20A9 9 0 1 0 4 16.1L2 22z";
|
|
81
87
|
var filled = (d, size) => {
|
|
@@ -99,6 +105,7 @@ var HOUSE = "M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 1
|
|
|
99
105
|
var SMILE = "M9.5 15.5c1.4 1 3.6 1 5 0";
|
|
100
106
|
var DISC = "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z";
|
|
101
107
|
var QMARK = "M9.4 9.2a2.7 2.7 0 0 1 5.2 1c0 1.8-2.6 2.5-2.6 2.5M12 16.8h.01";
|
|
108
|
+
var MEGAPHONE = "m3 11 18-5v12L3 14zM5.8 15.2a3 3 0 0 0 5.8 1.6z";
|
|
102
109
|
var chime = /* @__PURE__ */ (() => {
|
|
103
110
|
let ctx = null;
|
|
104
111
|
const get = () => {
|
|
@@ -120,11 +127,11 @@ var chime = /* @__PURE__ */ (() => {
|
|
|
120
127
|
try {
|
|
121
128
|
const c = get();
|
|
122
129
|
if (!c || c.state !== "running") return;
|
|
123
|
-
const
|
|
130
|
+
const t2 = c.currentTime;
|
|
124
131
|
[880, 1318.5].forEach((f, i) => {
|
|
125
132
|
const o = c.createOscillator();
|
|
126
133
|
const g = c.createGain();
|
|
127
|
-
const at =
|
|
134
|
+
const at = t2 + i * 0.11;
|
|
128
135
|
o.type = "sine";
|
|
129
136
|
o.frequency.value = f;
|
|
130
137
|
g.gain.setValueAtTime(1e-4, at);
|
|
@@ -189,9 +196,11 @@ var mountMessenger = (o) => {
|
|
|
189
196
|
const later = (fn) => real ? fn(real) : queued.push(fn);
|
|
190
197
|
const ctrl = new AbortController();
|
|
191
198
|
const caching = !o.client && !!o.apiKey;
|
|
199
|
+
let lastLook = null;
|
|
192
200
|
const start = (look) => {
|
|
193
201
|
if (gone) return;
|
|
194
|
-
|
|
202
|
+
lastLook = look;
|
|
203
|
+
const key = `${wordsVersion()}:${JSON.stringify(look ?? {})}`;
|
|
195
204
|
if (real && key === shown) return;
|
|
196
205
|
if (real?.isOpen()) {
|
|
197
206
|
real.onClose(() => start(look));
|
|
@@ -206,18 +215,27 @@ var mountMessenger = (o) => {
|
|
|
206
215
|
const cached = caching ? readCachedLook(o.apiKey) : null;
|
|
207
216
|
if (cached) start(cached);
|
|
208
217
|
const timer = cached ? void 0 : setTimeout(() => start(null), LOOK_WAIT_MS);
|
|
209
|
-
(o.client ?? createApi({ host: o.host, apiKey: o.apiKey, distinctId: o.distinctId(), user: o.user(), userHash: o.userHash?.() })).messengerLook(ctrl.signal).then((look) => {
|
|
218
|
+
const readLook = () => (o.client ?? createApi({ host: o.host, apiKey: o.apiKey, distinctId: o.distinctId(), user: o.user(), userHash: o.userHash?.() })).messengerLook(ctrl.signal).then((look) => {
|
|
210
219
|
if (caching && look) writeCachedLook(o.apiKey, look);
|
|
211
220
|
start(look);
|
|
212
221
|
}).catch(() => {
|
|
213
|
-
|
|
222
|
+
start(lastLook);
|
|
214
223
|
}).finally(() => clearTimeout(timer));
|
|
224
|
+
let readIn = currentLocale();
|
|
225
|
+
void readLook();
|
|
226
|
+
const offWords = onWords(() => {
|
|
227
|
+
if (currentLocale() !== readIn) {
|
|
228
|
+
readIn = currentLocale();
|
|
229
|
+
void readLook();
|
|
230
|
+
} else if (real) start(lastLook);
|
|
231
|
+
});
|
|
215
232
|
return {
|
|
216
233
|
open: (v) => later((h) => h.open(v)),
|
|
217
234
|
close: () => real?.close(),
|
|
218
235
|
toggle: () => later((h) => h.toggle()),
|
|
219
236
|
destroy: () => {
|
|
220
237
|
gone = true;
|
|
238
|
+
offWords();
|
|
221
239
|
ctrl.abort();
|
|
222
240
|
clearTimeout(timer);
|
|
223
241
|
real?.destroy();
|
|
@@ -228,15 +246,16 @@ var PLACES = ["top-left", "top-center", "top-right", "middle-left", "middle-righ
|
|
|
228
246
|
var build = (o) => {
|
|
229
247
|
injectStyles();
|
|
230
248
|
const position = PLACES.find((p) => p === o.position) ?? "bottom-right";
|
|
231
|
-
const title = o.title || "Support";
|
|
232
|
-
const greeting = o.greeting || "Hi! How can we help?";
|
|
249
|
+
const title = o.title || t("Support");
|
|
250
|
+
const greeting = o.greeting || t("Hi! How can we help?");
|
|
233
251
|
const askEmail = o.askEmail ?? true;
|
|
234
252
|
const label = o.label ?? "";
|
|
235
253
|
const reply = replyLine(o.replyTime);
|
|
236
254
|
const initial = (title.trim()[0] ?? "?").toUpperCase();
|
|
237
|
-
const whoLabel = { visitor: "You", team: "Team", ai: o.agentName?.trim() || "AI agent", auto: "Team" };
|
|
255
|
+
const whoLabel = { visitor: t("You"), team: t("Team"), ai: o.agentName?.trim() || t("AI agent"), auto: t("Team") };
|
|
238
256
|
const launcherOn = o.launcher ?? true;
|
|
239
257
|
const helpOn = o.help ?? true;
|
|
258
|
+
const updatesOn = o.updates ?? true;
|
|
240
259
|
const soundOn = o.sound ?? true;
|
|
241
260
|
const previewOn = o.preview ?? true;
|
|
242
261
|
const api = () => o.client ?? createApi({ host: o.host, apiKey: o.apiKey, distinctId: o.distinctId(), user: o.user(), userHash: o.userHash?.() });
|
|
@@ -245,7 +264,9 @@ var build = (o) => {
|
|
|
245
264
|
"data-theme": o.theme ?? "auto",
|
|
246
265
|
"data-corners": o.corners === "soft" || o.corners === "square" ? o.corners : "round",
|
|
247
266
|
"data-font": o.font === "system" ? "system" : "site",
|
|
248
|
-
"data-header": o.header === "color" ? "color" : "plain"
|
|
267
|
+
"data-header": o.header === "color" ? "color" : "plain",
|
|
268
|
+
// Screen readers read the panel in the language of its words. Built anew when they change.
|
|
269
|
+
...wordsLang() ? { lang: wordsLang() } : {}
|
|
249
270
|
});
|
|
250
271
|
if (o.container) root.setAttribute("data-contained", "");
|
|
251
272
|
if (o.accent) {
|
|
@@ -258,49 +279,96 @@ var build = (o) => {
|
|
|
258
279
|
let afterClose = null;
|
|
259
280
|
let view = "home";
|
|
260
281
|
let thread = null;
|
|
282
|
+
let chats = [];
|
|
283
|
+
let hasEmail = false;
|
|
284
|
+
let chatId = null;
|
|
285
|
+
let draft = null;
|
|
286
|
+
let drafts = 0;
|
|
287
|
+
let listing = false;
|
|
261
288
|
let loaded = false;
|
|
262
289
|
let error = "";
|
|
263
|
-
|
|
264
|
-
let
|
|
290
|
+
const sayable = (e) => e.status === 401 ? t("The chat is not available right now. Please try again later.") : e.message;
|
|
291
|
+
let outbox = [];
|
|
292
|
+
let outboxSeq = 0;
|
|
293
|
+
let flushing = false;
|
|
294
|
+
const known = /* @__PURE__ */ new Map();
|
|
295
|
+
let seen = readSeen();
|
|
265
296
|
let unseen = [];
|
|
266
297
|
let previewGone = false;
|
|
267
298
|
let poll;
|
|
299
|
+
let refused = false;
|
|
268
300
|
let ctrl = null;
|
|
269
301
|
let articles = null;
|
|
270
302
|
let query = "";
|
|
271
303
|
let rated = /* @__PURE__ */ new Set();
|
|
272
304
|
let hasArticles = null;
|
|
305
|
+
let updates = null;
|
|
306
|
+
let updatesNode = null;
|
|
273
307
|
let collection = null;
|
|
274
308
|
let wide = false;
|
|
275
|
-
const
|
|
276
|
-
const lastTeamId = (t) => teamMsgs(t).at(-1)?.id ?? "";
|
|
309
|
+
const isAnswer = (m) => m.from === "team" || m.from === "ai";
|
|
277
310
|
const watching = () => open && view === "messages" && document.visibilityState !== "hidden";
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
311
|
+
const shownChat = () => listing || draft !== null ? null : chatId ?? thread?.conversation?.id ?? null;
|
|
312
|
+
const unseenIn = (chat) => unseen.filter((m) => m.chat === chat).length;
|
|
313
|
+
const learn = (chat, answers, newest, settled) => {
|
|
314
|
+
const was = known.get(chat);
|
|
315
|
+
const after = (at) => answers.filter((m) => m.at > at);
|
|
316
|
+
if (was === void 0) {
|
|
317
|
+
const read = seen.chats[chat];
|
|
318
|
+
const fresh = settled ? [] : read ? after(read) : answers.slice(answers.findIndex((m) => m.id === seen.old) + 1);
|
|
319
|
+
unseen = [...unseen, ...fresh.map((m) => ({ ...m, chat }))];
|
|
320
|
+
if (loaded && fresh.length) {
|
|
321
|
+
if (soundOn) chime.play();
|
|
322
|
+
previewGone = false;
|
|
323
|
+
}
|
|
284
324
|
} else {
|
|
285
|
-
const
|
|
286
|
-
const fresh = team.slice(i + 1).filter((m) => m.from !== "auto");
|
|
325
|
+
const fresh = after(was);
|
|
287
326
|
if (fresh.length) {
|
|
288
327
|
if (soundOn) chime.play();
|
|
289
|
-
if (!watching()) {
|
|
290
|
-
unseen = [...unseen, ...fresh];
|
|
328
|
+
if (!watching() || shownChat() !== chat) {
|
|
329
|
+
unseen = [...unseen, ...fresh.map((m) => ({ ...m, chat }))];
|
|
291
330
|
previewGone = false;
|
|
292
331
|
}
|
|
293
332
|
}
|
|
294
333
|
}
|
|
295
|
-
|
|
296
|
-
|
|
334
|
+
known.set(chat, newest ?? was ?? "");
|
|
335
|
+
};
|
|
336
|
+
const take = (t2) => {
|
|
337
|
+
const read = t2.conversation;
|
|
338
|
+
const last = t2.messages.at(-1);
|
|
339
|
+
chats = t2.conversations ?? (read && last ? [{ id: read.id, status: read.status, subject: lineOf(t2.messages[0]), last }] : []);
|
|
340
|
+
hasEmail = t2.hasEmail;
|
|
341
|
+
chats.forEach((c, i) => {
|
|
342
|
+
if (c.id !== read?.id) learn(c.id, c.last && isAnswer(c.last) ? [c.last] : [], c.last?.at, c.status === "solved" && i > 0);
|
|
343
|
+
});
|
|
344
|
+
if (read) learn(read.id, t2.messages.filter(isAnswer), last?.at, read.status === "solved" && chats[0]?.id !== read.id);
|
|
345
|
+
if (chatId === null || read?.id === chatId) thread = t2;
|
|
297
346
|
if (watching()) markSeen();
|
|
298
347
|
};
|
|
299
348
|
const markSeen = () => {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
349
|
+
const chat = shownChat();
|
|
350
|
+
if (!chat) return;
|
|
351
|
+
unseen = unseen.filter((m) => m.chat !== chat);
|
|
352
|
+
const at = known.get(chat);
|
|
353
|
+
if (!at || seen.chats[chat] === at) return;
|
|
354
|
+
const kept = Object.fromEntries(Object.entries(seen.chats).filter(([c]) => chats.some((x) => x.id === c)));
|
|
355
|
+
seen = { ...seen, chats: { ...kept, [chat]: at } };
|
|
356
|
+
writeSeen(seen.chats);
|
|
303
357
|
};
|
|
358
|
+
const enterChat = (id, d, list2 = false) => {
|
|
359
|
+
if (id !== chatId || d !== draft) {
|
|
360
|
+
ctrl?.abort();
|
|
361
|
+
ctrl = null;
|
|
362
|
+
thread = null;
|
|
363
|
+
listSig = "";
|
|
364
|
+
listCount = -1;
|
|
365
|
+
}
|
|
366
|
+
chatId = id;
|
|
367
|
+
draft = d;
|
|
368
|
+
listing = list2;
|
|
369
|
+
};
|
|
370
|
+
const enterMessages = () => enterChat(null, null, chats.length > 1);
|
|
371
|
+
const startChat = () => enterChat(null, ++drafts);
|
|
304
372
|
const letter = (size) => el("span", { class: `ckw-msgr-av${size ? " ckw-msgr-av-sm" : ""}`, "aria-hidden": "true" }, initial);
|
|
305
373
|
const avatar = (size) => {
|
|
306
374
|
if (!o.icon) return letter(size);
|
|
@@ -361,14 +429,14 @@ var build = (o) => {
|
|
|
361
429
|
if (!fabCount.isConnected) fab.append(fabCount);
|
|
362
430
|
} else fabCount.remove();
|
|
363
431
|
fab.setAttribute("aria-expanded", String(open));
|
|
364
|
-
fab.setAttribute("aria-label", open ? "Close chat" : unseen.length ?
|
|
432
|
+
fab.setAttribute("aria-label", open ? t("Close chat") : unseen.length === 1 ? t("Open chat, 1 new reply") : unseen.length ? tf("Open chat, {n} new replies", { n: unseen.length }) : t("Open chat"));
|
|
365
433
|
drawPreview();
|
|
366
434
|
};
|
|
367
435
|
const previewShown = () => previewOn && !open && !previewGone && !!unseen.length && launcherOn;
|
|
368
436
|
const drawPreview = () => {
|
|
369
437
|
const m = unseen.at(-1);
|
|
370
438
|
if (!previewShown() || !m) return leave(prev);
|
|
371
|
-
const x = el("button", { type: "button", class: "ckw-msgr-preview-x", "aria-label": "Dismiss" }, icon(I.cross, 14));
|
|
439
|
+
const x = el("button", { type: "button", class: "ckw-msgr-preview-x", "aria-label": t("Dismiss") }, icon(I.cross, 14));
|
|
372
440
|
x.addEventListener("click", (e) => {
|
|
373
441
|
e.stopPropagation();
|
|
374
442
|
previewGone = true;
|
|
@@ -380,18 +448,23 @@ var build = (o) => {
|
|
|
380
448
|
el(
|
|
381
449
|
"span",
|
|
382
450
|
{ class: "ckw-msgr-preview-main" },
|
|
383
|
-
el("span", { class: "ckw-msgr-preview-who" }, `${whoLabel[m.from]}${unseen.length > 1 ? ` \xB7 ${unseen.length}
|
|
451
|
+
el("span", { class: "ckw-msgr-preview-who" }, `${whoLabel[m.from]}${unseen.length > 1 ? ` \xB7 ${tf("{n} new", { n: unseen.length })}` : ""}`),
|
|
384
452
|
el("span", { class: "ckw-msgr-preview-text" }, lineOf(m))
|
|
385
453
|
)
|
|
386
454
|
);
|
|
387
|
-
card.addEventListener("click", () =>
|
|
455
|
+
card.addEventListener("click", () => {
|
|
456
|
+
enterChat(m.chat, null);
|
|
457
|
+
show("messages");
|
|
458
|
+
});
|
|
388
459
|
prev.replaceChildren(card, x);
|
|
389
460
|
enter(prev);
|
|
390
461
|
};
|
|
391
462
|
fab.addEventListener("click", () => {
|
|
392
463
|
chime.unlock();
|
|
393
|
-
if (open) close();
|
|
394
|
-
|
|
464
|
+
if (open) return close();
|
|
465
|
+
const news = unseen.at(-1);
|
|
466
|
+
if (news) enterChat(news.chat, null);
|
|
467
|
+
show(news ? "messages" : "home");
|
|
395
468
|
});
|
|
396
469
|
if (launcherOn) root.appendChild(fab);
|
|
397
470
|
const pop = el("div", { class: "ckw-pop ckw-msgr", "data-pos": position, role: "dialog", "aria-label": title });
|
|
@@ -400,21 +473,23 @@ var build = (o) => {
|
|
|
400
473
|
pop.addEventListener("click", () => chime.unlock());
|
|
401
474
|
const lead = el("span", { class: "ckw-msgr-lead" });
|
|
402
475
|
const mid = el("span", { class: "ckw-msgr-mid" });
|
|
403
|
-
const tabs = el("nav", { class: "ckw-msgr-tabs", "aria-label": "Messenger" });
|
|
476
|
+
const tabs = el("nav", { class: "ckw-msgr-tabs", "aria-label": t("Messenger") });
|
|
404
477
|
const tab = (v, name, line, shape, cut) => {
|
|
405
478
|
const b = el("button", { type: "button", class: "ckw-msgr-tab" }, el("span", { class: "ckw-msgr-tab-ic" }, icon(line, 22), duo(shape, cut, 22)), el("span", {}, name));
|
|
406
479
|
b.addEventListener("click", () => {
|
|
407
480
|
collection = null;
|
|
481
|
+
if (v === "messages") enterMessages();
|
|
408
482
|
go(v);
|
|
409
483
|
});
|
|
410
484
|
return b;
|
|
411
485
|
};
|
|
412
|
-
const homeTab = tab("home", "Home", I.home, HOUSE, SMILE);
|
|
413
|
-
const chatTab = tab("messages", "Messages", I.chat, BUBBLE, DOTS);
|
|
414
|
-
const helpTab = tab("help", "Help", I.help, DISC, QMARK);
|
|
486
|
+
const homeTab = tab("home", t("Home"), I.home, HOUSE, SMILE);
|
|
487
|
+
const chatTab = tab("messages", t("Messages"), I.chat, BUBBLE, DOTS);
|
|
488
|
+
const helpTab = tab("help", t("Help"), I.help, DISC, QMARK);
|
|
489
|
+
const updatesTab = tab("updates", t("Updates"), I.updates, MEGAPHONE, "");
|
|
415
490
|
const tabCount = el("span", { class: "ckw-msgr-count ckw-msgr-count-tab", "aria-hidden": "true" });
|
|
416
|
-
const menuBtn = el("button", { type: "button", class: "ckw-msgr-ib", "aria-label": "More", "aria-haspopup": "menu", "aria-expanded": "false" }, icon(I.dots, 18));
|
|
417
|
-
const xBtn = el("button", { type: "button", class: "ckw-msgr-ib ckw-msgr-x", "aria-label": "Close messenger" }, icon(I.cross));
|
|
491
|
+
const menuBtn = el("button", { type: "button", class: "ckw-msgr-ib", "aria-label": t("More"), "aria-haspopup": "menu", "aria-expanded": "false" }, icon(I.dots, 18));
|
|
492
|
+
const xBtn = el("button", { type: "button", class: "ckw-msgr-ib ckw-msgr-x", "aria-label": t("Close messenger") }, icon(I.cross));
|
|
418
493
|
xBtn.addEventListener("click", () => close());
|
|
419
494
|
bar.append(lead, mid, el("span", { class: "ckw-msgr-trail" }, menuBtn, xBtn));
|
|
420
495
|
const menu = el("div", { class: "ckw-msgr-menu", role: "menu" });
|
|
@@ -436,9 +511,9 @@ var build = (o) => {
|
|
|
436
511
|
e.stopPropagation();
|
|
437
512
|
if (menuOpen()) return closeMenu();
|
|
438
513
|
menu.replaceChildren(
|
|
439
|
-
el("span", { class: "ckw-msgr-menu-h" }, "Quick actions"),
|
|
440
|
-
menuItem(I.minus, "Minimize chat", () => close()),
|
|
441
|
-
menuItem(wide ? I.collapse : I.expand, wide ? "Collapse view" : "Expand view", () => {
|
|
514
|
+
el("span", { class: "ckw-msgr-menu-h" }, t("Quick actions")),
|
|
515
|
+
menuItem(I.minus, t("Minimize chat"), () => close()),
|
|
516
|
+
menuItem(wide ? I.collapse : I.expand, wide ? t("Collapse view") : t("Expand view"), () => {
|
|
442
517
|
wide = !wide;
|
|
443
518
|
pop.toggleAttribute("data-wide", wide);
|
|
444
519
|
})
|
|
@@ -456,26 +531,37 @@ var build = (o) => {
|
|
|
456
531
|
};
|
|
457
532
|
if (typeof view !== "string")
|
|
458
533
|
lead.replaceChildren(
|
|
459
|
-
back(() => go("help"), "Back to help")
|
|
534
|
+
back(() => go("help"), t("Back to help"))
|
|
460
535
|
);
|
|
461
536
|
else if (view === "help" && collection && !query.trim())
|
|
462
537
|
lead.replaceChildren(
|
|
463
538
|
back(() => {
|
|
464
539
|
collection = null;
|
|
465
540
|
go("help");
|
|
466
|
-
}, "All collections")
|
|
541
|
+
}, t("All collections"))
|
|
467
542
|
);
|
|
468
|
-
else if (view === "messages"
|
|
543
|
+
else if (view === "messages" && !listing && chats.length > 1)
|
|
544
|
+
lead.replaceChildren(
|
|
545
|
+
back(() => {
|
|
546
|
+
enterMessages();
|
|
547
|
+
go("messages");
|
|
548
|
+
}, t("All conversations"))
|
|
549
|
+
);
|
|
550
|
+
else if (view === "messages") lead.replaceChildren(back(() => go("home"), t("Back to home")));
|
|
469
551
|
else if (view === "home") lead.replaceChildren(logo);
|
|
470
552
|
else lead.replaceChildren();
|
|
471
|
-
if (view === "messages") mid.replaceChildren(
|
|
472
|
-
else if (view === "
|
|
553
|
+
if (view === "messages" && listing) mid.replaceChildren(el("b", {}, t("Messages")));
|
|
554
|
+
else if (view === "messages") mid.replaceChildren(avatar("sm"), el("span", { class: "ckw-msgr-mid-t" }, el("b", {}, title), el("span", { class: "ckw-msgr-online" }, reply)));
|
|
555
|
+
else if (view === "help") mid.replaceChildren(el("b", {}, t("Help")));
|
|
556
|
+
else if (view === "updates") mid.replaceChildren(el("b", {}, t("Updates")));
|
|
473
557
|
else mid.replaceChildren();
|
|
474
|
-
const items = helpOn && hasArticles !== false ? [
|
|
475
|
-
if (tabs.childNodes.length !== items.length) tabs.replaceChildren(...items);
|
|
558
|
+
const items = [homeTab, chatTab, ...helpOn && hasArticles !== false ? [helpTab] : [], ...updatesOn && updates?.length ? [updatesTab] : []];
|
|
559
|
+
if (tabs.childNodes.length !== items.length || items.some((t2, i) => tabs.childNodes[i] !== t2)) tabs.replaceChildren(...items);
|
|
560
|
+
tabs.toggleAttribute("data-four", items.length > 3);
|
|
476
561
|
homeTab.setAttribute("aria-current", view === "home" ? "page" : "false");
|
|
477
562
|
chatTab.setAttribute("aria-current", view === "messages" ? "page" : "false");
|
|
478
|
-
helpTab.setAttribute("aria-current", view !== "home" && view !== "messages" ? "page" : "false");
|
|
563
|
+
helpTab.setAttribute("aria-current", view !== "home" && view !== "messages" && view !== "updates" ? "page" : "false");
|
|
564
|
+
updatesTab.setAttribute("aria-current", view === "updates" ? "page" : "false");
|
|
479
565
|
if (unseen.length && view !== "messages") {
|
|
480
566
|
tabCount.textContent = String(Math.min(unseen.length, 9));
|
|
481
567
|
if (!tabCount.isConnected) chatTab.firstChild.appendChild(tabCount);
|
|
@@ -483,14 +569,14 @@ var build = (o) => {
|
|
|
483
569
|
};
|
|
484
570
|
const form = el("form", { class: "ckw-msgr-compose" });
|
|
485
571
|
const err = el("p", { class: "ckw-msgr-error", role: "alert" });
|
|
486
|
-
const email = el("input", { class: "ckw-input ckw-msgr-email", type: "email", inputmode: "email", autocomplete: "email", placeholder: "Email for our reply (optional)", "aria-label": "Your email (optional)", maxlength: "200", "data-1p-ignore": "", "data-lpignore": "true", "data-bwignore": "", "data-form-type": "other" });
|
|
572
|
+
const email = el("input", { class: "ckw-input ckw-msgr-email", type: "email", inputmode: "email", autocomplete: "email", placeholder: t("Email for our reply (optional)"), "aria-label": t("Your email (optional)"), maxlength: "200", "data-1p-ignore": "", "data-lpignore": "true", "data-bwignore": "", "data-form-type": "other" });
|
|
487
573
|
const row = el("div", { class: "ckw-msgr-box" });
|
|
488
|
-
const text = el("textarea", { class: "ckw-textarea ckw-msgr-input", rows: "1", placeholder: "Write your message\u2026", "aria-label": "Message", maxlength: "4000" });
|
|
489
|
-
const send = el("button", { type: "submit", class: "ckw-msgr-send", "aria-label": "Send" }, icon(I.up));
|
|
574
|
+
const text = el("textarea", { class: "ckw-textarea ckw-msgr-input", rows: "1", placeholder: t("Write your message\u2026"), "aria-label": t("Message"), maxlength: "4000" });
|
|
575
|
+
const send = el("button", { type: "submit", class: "ckw-msgr-send", "aria-label": t("Send") }, icon(I.up));
|
|
490
576
|
const shots = canShoot();
|
|
491
577
|
const clip = el(
|
|
492
578
|
"button",
|
|
493
|
-
{ type: "button", class: "ckw-msgr-clip", "aria-label": shots ? "Attach a file or a screenshot" : "Attach a file", ...shots ? { "aria-haspopup": "menu", "aria-expanded": "false" } : {} },
|
|
579
|
+
{ type: "button", class: "ckw-msgr-clip", "aria-label": shots ? t("Attach a file or a screenshot") : t("Attach a file"), ...shots ? { "aria-haspopup": "menu", "aria-expanded": "false" } : {} },
|
|
494
580
|
icon(I.clip, 18)
|
|
495
581
|
);
|
|
496
582
|
row.append(text, el("div", { class: "ckw-msgr-tools" }, clip, el("span", { class: "ckw-msgr-sp" }), send));
|
|
@@ -498,7 +584,7 @@ var build = (o) => {
|
|
|
498
584
|
form.append(row);
|
|
499
585
|
const picker = el("input", { type: "file", accept: CHAT_FILE_TYPES.join(","), multiple: "", hidden: "", tabindex: "-1", "aria-hidden": "true" });
|
|
500
586
|
form.append(picker);
|
|
501
|
-
const tray = el("div", { class: "ckw-msgr-files", role: "list", "aria-label": "Files to send" });
|
|
587
|
+
const tray = el("div", { class: "ckw-msgr-files", role: "list", "aria-label": t("Files to send") });
|
|
502
588
|
let picked = [];
|
|
503
589
|
const localUrls = /* @__PURE__ */ new Map();
|
|
504
590
|
const fileUrl = (id) => localUrls.get(id) ?? api().attachmentUrl(id);
|
|
@@ -515,15 +601,15 @@ var build = (o) => {
|
|
|
515
601
|
for (const f of list2) {
|
|
516
602
|
const name = f.name || "screenshot.png";
|
|
517
603
|
if (picked.length >= CHAT_FILES_PER_MESSAGE) {
|
|
518
|
-
error =
|
|
604
|
+
error = tf("Up to {n} files per message.", { n: CHAT_FILES_PER_MESSAGE });
|
|
519
605
|
break;
|
|
520
606
|
}
|
|
521
607
|
if (!CHAT_FILE_TYPES.includes(f.type)) {
|
|
522
|
-
error = "Only pictures (PNG, JPEG, WebP, GIF) and PDFs can be sent.";
|
|
608
|
+
error = t("Only pictures (PNG, JPEG, WebP, GIF) and PDFs can be sent.");
|
|
523
609
|
continue;
|
|
524
610
|
}
|
|
525
611
|
if (f.size > CHAT_FILE_MAX_BYTES) {
|
|
526
|
-
error =
|
|
612
|
+
error = tf("{name} is larger than 10 MB.", { name });
|
|
527
613
|
continue;
|
|
528
614
|
}
|
|
529
615
|
const p = { key: `local-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 7)}`, name, type: f.type, size: f.size, local: URL.createObjectURL(f), up: Promise.resolve(), ctrl: new AbortController() };
|
|
@@ -541,8 +627,10 @@ var build = (o) => {
|
|
|
541
627
|
);
|
|
542
628
|
picked.push(p);
|
|
543
629
|
}
|
|
544
|
-
if (open && view !== "messages")
|
|
545
|
-
|
|
630
|
+
if (open && (view !== "messages" || listing)) {
|
|
631
|
+
startChat();
|
|
632
|
+
go("messages");
|
|
633
|
+
} else draw();
|
|
546
634
|
text.focus();
|
|
547
635
|
};
|
|
548
636
|
let traySig = "";
|
|
@@ -552,14 +640,14 @@ var build = (o) => {
|
|
|
552
640
|
traySig = sig;
|
|
553
641
|
tray.replaceChildren(
|
|
554
642
|
...picked.map((p) => {
|
|
555
|
-
const x = el("button", { type: "button", class: "ckw-msgr-file-x", "aria-label":
|
|
643
|
+
const x = el("button", { type: "button", class: "ckw-msgr-file-x", "aria-label": tf("Remove {name}", { name: p.name }) }, icon(I.cross, 12));
|
|
556
644
|
x.addEventListener("click", () => {
|
|
557
645
|
dropFile(p);
|
|
558
646
|
draw();
|
|
559
647
|
text.focus();
|
|
560
648
|
});
|
|
561
649
|
const face = p.type.startsWith("image/") ? el("img", { src: p.local, alt: "" }) : el("span", { class: "ckw-msgr-file-doc" }, icon(I.doc, 18), el("span", {}, p.name));
|
|
562
|
-
return el("div", { class: "ckw-msgr-file", role: "listitem", title: p.name, "aria-label": p.id ? p.name :
|
|
650
|
+
return el("div", { class: "ckw-msgr-file", role: "listitem", title: p.name, "aria-label": p.id ? p.name : tf("{name}, uploading", { name: p.name }), ...p.id ? {} : { "aria-busy": "true" } }, face, x);
|
|
563
651
|
})
|
|
564
652
|
);
|
|
565
653
|
}
|
|
@@ -572,7 +660,7 @@ var build = (o) => {
|
|
|
572
660
|
stream = await navigator.mediaDevices.getDisplayMedia({ video: { displaySurface: "browser" }, audio: false, preferCurrentTab: true, selfBrowserSurface: "include" });
|
|
573
661
|
} catch (e) {
|
|
574
662
|
if (e.name !== "NotAllowedError" && e.name !== "AbortError") {
|
|
575
|
-
error = "The screenshot did not work here. Attach a picture instead.";
|
|
663
|
+
error = t("The screenshot did not work here. Attach a picture instead.");
|
|
576
664
|
draw();
|
|
577
665
|
}
|
|
578
666
|
return;
|
|
@@ -598,11 +686,11 @@ var build = (o) => {
|
|
|
598
686
|
} catch {
|
|
599
687
|
blob = null;
|
|
600
688
|
} finally {
|
|
601
|
-
for (const
|
|
689
|
+
for (const t2 of stream.getTracks()) t2.stop();
|
|
602
690
|
root.style.visibility = "";
|
|
603
691
|
}
|
|
604
692
|
if (!blob || !blob.size) {
|
|
605
|
-
error = "The screenshot did not work here. Attach a picture instead.";
|
|
693
|
+
error = t("The screenshot did not work here. Attach a picture instead.");
|
|
606
694
|
return draw();
|
|
607
695
|
}
|
|
608
696
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace(/[:T]/g, "-");
|
|
@@ -629,8 +717,8 @@ var build = (o) => {
|
|
|
629
717
|
if (!shots) return picker.click();
|
|
630
718
|
if (amenuOpen()) return closeAmenu();
|
|
631
719
|
amenu.replaceChildren(
|
|
632
|
-
amenuItem(I.image, "Photo or file", () => picker.click()),
|
|
633
|
-
amenuItem(I.screen, "Screenshot of this page", () => void shoot())
|
|
720
|
+
amenuItem(I.image, t("Photo or file"), () => picker.click()),
|
|
721
|
+
amenuItem(I.screen, t("Screenshot of this page"), () => void shoot())
|
|
634
722
|
);
|
|
635
723
|
clip.setAttribute("aria-expanded", "true");
|
|
636
724
|
enter(amenu, form);
|
|
@@ -640,11 +728,11 @@ var build = (o) => {
|
|
|
640
728
|
addFiles([...picker.files ?? []]);
|
|
641
729
|
picker.value = "";
|
|
642
730
|
});
|
|
643
|
-
const brand = el("a", { class: "ckw-msgr-powered", href: BRAND_URL, target: "_blank", rel: "noopener" }, "Powered by ", el("b", {}, "MRRocket"));
|
|
644
|
-
const list = el("ol", { class: "ckw-msgs", "aria-live": "polite", "aria-label": "Messages" });
|
|
645
|
-
const needEmail = () => askEmail && !o.user()?.email && !
|
|
731
|
+
const brand = el("a", { class: "ckw-msgr-powered", href: BRAND_URL, target: "_blank", rel: "noopener" }, t("Powered by {brand}").split("{brand}")[0], el("b", {}, "MRRocket"), t("Powered by {brand}").split("{brand}")[1]);
|
|
732
|
+
const list = el("ol", { class: "ckw-msgs", "aria-live": "polite", "aria-label": t("Messages") });
|
|
733
|
+
const needEmail = () => askEmail && !o.user()?.email && !hasEmail;
|
|
646
734
|
const emailOk = () => !email.value.trim() || EMAIL.test(email.value.trim());
|
|
647
|
-
const canSend = () => (!!text.value.trim() || picked.length > 0) &&
|
|
735
|
+
const canSend = () => (!!text.value.trim() || picked.length > 0) && emailOk();
|
|
648
736
|
const go = (v) => {
|
|
649
737
|
const changed = JSON.stringify(v) !== JSON.stringify(view);
|
|
650
738
|
view = v;
|
|
@@ -666,6 +754,7 @@ var build = (o) => {
|
|
|
666
754
|
return img;
|
|
667
755
|
};
|
|
668
756
|
const bubble = (m, isPending = false) => {
|
|
757
|
+
const slow = isPending && Date.now() - Date.parse(m.at) >= SLOW_SEND_MS;
|
|
669
758
|
const li = el("li", { class: "ckw-msg", "data-from": m.from === "visitor" ? "me" : "them" }, m.body || !m.attachments?.length ? el("div", { class: "ckw-bubble" }, m.body) : null);
|
|
670
759
|
if (isPending) li.setAttribute("aria-busy", "true");
|
|
671
760
|
if (m.attachments?.length)
|
|
@@ -683,7 +772,7 @@ var build = (o) => {
|
|
|
683
772
|
chip.addEventListener("click", () => go({ article: a.id }));
|
|
684
773
|
li.append(chip);
|
|
685
774
|
}
|
|
686
|
-
li.append(el("span", { class: "ckw-msg-meta" }, m.from === "ai" ? agentFace() : null, `${m.from !== "visitor" ? `${whoLabel[m.from]} \xB7 ` : ""}${
|
|
775
|
+
li.append(el("span", { class: "ckw-msg-meta" }, m.from === "ai" ? agentFace() : null, `${m.from !== "visitor" ? `${whoLabel[m.from]} \xB7 ` : ""}${slow ? t("Sending\u2026") : timeAgo(m.at)}`));
|
|
687
776
|
return li;
|
|
688
777
|
};
|
|
689
778
|
const articleRow = (a) => {
|
|
@@ -701,15 +790,15 @@ var build = (o) => {
|
|
|
701
790
|
b.addEventListener("click", act);
|
|
702
791
|
return b;
|
|
703
792
|
};
|
|
704
|
-
const searchBox = (placeholder = "Search for help") => {
|
|
793
|
+
const searchBox = (placeholder = t("Search for help")) => {
|
|
705
794
|
const wrap = el("label", { class: "ckw-msgr-search" });
|
|
706
|
-
const input = el("input", { class: "ckw-msgr-search-in", type: "search", placeholder, "aria-label": "Search for help", maxlength: "200" });
|
|
795
|
+
const input = el("input", { class: "ckw-msgr-search-in", type: "search", placeholder, "aria-label": t("Search for help"), maxlength: "200" });
|
|
707
796
|
input.value = query;
|
|
708
|
-
let
|
|
797
|
+
let wait;
|
|
709
798
|
input.addEventListener("input", () => {
|
|
710
799
|
query = input.value;
|
|
711
|
-
clearTimeout(
|
|
712
|
-
|
|
800
|
+
clearTimeout(wait);
|
|
801
|
+
wait = setTimeout(() => {
|
|
713
802
|
if (view !== "help") return go("help");
|
|
714
803
|
void loadArticles(true);
|
|
715
804
|
}, 250);
|
|
@@ -723,9 +812,13 @@ var build = (o) => {
|
|
|
723
812
|
wrap.append(icon(I.search, 16), input);
|
|
724
813
|
return wrap;
|
|
725
814
|
};
|
|
726
|
-
const helpSearch = searchBox("Search our help center\u2026");
|
|
815
|
+
const helpSearch = searchBox(t("Search our help center\u2026"));
|
|
727
816
|
const helpInput = helpSearch.querySelector("input");
|
|
728
817
|
let loadingArticles = false;
|
|
818
|
+
const askAnew = () => {
|
|
819
|
+
startChat();
|
|
820
|
+
go("messages");
|
|
821
|
+
};
|
|
729
822
|
const loadArticles = async (force = false) => {
|
|
730
823
|
if (!helpOn || loadingArticles && !force) return;
|
|
731
824
|
loadingArticles = true;
|
|
@@ -742,50 +835,92 @@ var build = (o) => {
|
|
|
742
835
|
}
|
|
743
836
|
draw();
|
|
744
837
|
};
|
|
838
|
+
const loadUpdates = async () => {
|
|
839
|
+
try {
|
|
840
|
+
updates = (await api().changelog()).entries.slice(0, UPDATES_SHOWN);
|
|
841
|
+
} catch {
|
|
842
|
+
updates = [];
|
|
843
|
+
}
|
|
844
|
+
updatesNode = null;
|
|
845
|
+
draw();
|
|
846
|
+
};
|
|
745
847
|
const askForm = el("form", { class: "ckw-msgr-askq" });
|
|
746
|
-
const askIn = el("input", { class: "ckw-msgr-askq-in", placeholder: "Ask a question", "aria-label": "Ask a question", maxlength: "4000", autocomplete: "off" });
|
|
747
|
-
const askGo = el("button", { type: "submit", class: "ckw-msgr-send", "aria-label": "Send", disabled: "" }, icon(I.up));
|
|
848
|
+
const askIn = el("input", { class: "ckw-msgr-askq-in", placeholder: t("Ask a question"), "aria-label": t("Ask a question"), maxlength: "4000", autocomplete: "off" });
|
|
849
|
+
const askGo = el("button", { type: "submit", class: "ckw-msgr-send", "aria-label": t("Send"), disabled: "" }, icon(I.up));
|
|
748
850
|
askForm.append(askIn, askGo);
|
|
749
851
|
askIn.addEventListener("input", () => askGo.disabled = !askIn.value.trim());
|
|
750
852
|
askForm.addEventListener("submit", (e) => {
|
|
751
853
|
e.preventDefault();
|
|
752
|
-
if (!askIn.value.trim())
|
|
854
|
+
if (!askIn.value.trim()) {
|
|
855
|
+
enterMessages();
|
|
856
|
+
return go("messages");
|
|
857
|
+
}
|
|
753
858
|
text.value = askIn.value;
|
|
754
859
|
askIn.value = "";
|
|
755
860
|
askGo.disabled = true;
|
|
861
|
+
startChat();
|
|
756
862
|
go("messages");
|
|
757
|
-
if (canSend())
|
|
863
|
+
if (canSend()) submit();
|
|
758
864
|
});
|
|
865
|
+
const chatRow = (c) => {
|
|
866
|
+
const m = c.last;
|
|
867
|
+
const n = unseenIn(c.id);
|
|
868
|
+
const closed = c.status === "solved" ? ` \xB7 ${t("Closed")}` : "";
|
|
869
|
+
const row2 = el(
|
|
870
|
+
"button",
|
|
871
|
+
{ type: "button", class: "ckw-msgr-card ckw-msgr-recent", "aria-label": `${tf("Conversation: {subject}", { subject: c.subject })}${closed}${n ? `, ${tf("{n} new", { n })}` : ""}` },
|
|
872
|
+
el(
|
|
873
|
+
"span",
|
|
874
|
+
{ class: "ckw-msgr-recent-row" },
|
|
875
|
+
avatar("sm"),
|
|
876
|
+
el(
|
|
877
|
+
"span",
|
|
878
|
+
{ class: "ckw-msgr-card-main" },
|
|
879
|
+
el("span", { class: "ckw-msgr-card-sub ckw-msgr-recent-text" }, c.subject),
|
|
880
|
+
m && lineOf(m) !== c.subject ? el("span", { class: "ckw-msgr-card-sub" }, lineOf(m)) : null,
|
|
881
|
+
m ? el("span", { class: "ckw-msgr-card-meta" }, `${whoLabel[m.from]} \xB7 ${timeAgo(m.at)}${closed}`) : null
|
|
882
|
+
),
|
|
883
|
+
n ? el("span", { class: "ckw-msgr-badge" }, String(n)) : icon(I.next, 14)
|
|
884
|
+
)
|
|
885
|
+
);
|
|
886
|
+
row2.addEventListener("click", () => {
|
|
887
|
+
enterChat(c.id, null);
|
|
888
|
+
go("messages");
|
|
889
|
+
});
|
|
890
|
+
return row2;
|
|
891
|
+
};
|
|
892
|
+
const chatsBox = el("div", { class: "ckw-msgr-chats" });
|
|
893
|
+
const newChat = el("button", { type: "button", class: "ckw-btn ckw-btn-primary ckw-msgr-new" }, icon(I.chat), t("New conversation"));
|
|
894
|
+
newChat.addEventListener("click", askAnew);
|
|
895
|
+
let chatsSig = "";
|
|
896
|
+
const drawChats = () => {
|
|
897
|
+
const sig = `${chats.map((c) => `${c.id}:${c.status}:${c.last?.id}:${unseenIn(c.id)}`).join()}|${Math.floor(Date.now() / 6e4)}`;
|
|
898
|
+
if (sig !== chatsSig) {
|
|
899
|
+
chatsSig = sig;
|
|
900
|
+
chatsBox.replaceChildren(...chats.map(chatRow));
|
|
901
|
+
}
|
|
902
|
+
if (body.childNodes[0] !== chatsBox) body.replaceChildren(chatsBox, newChat);
|
|
903
|
+
};
|
|
759
904
|
const drawHome = () => {
|
|
760
905
|
const first = o.user()?.name?.trim().split(/\s+/)[0];
|
|
761
|
-
const rest = greeting.replace(/^(hi|hello|hey|hallo)( there)?\s*[
|
|
906
|
+
const rest = (o.greeting ? greeting.replace(/^(hi|hello|hey|hallo|moin|servus|bonjour|salut|hola|ciao|salve|hoi|ol[áa]|cze[śs][ćc]|hej|こんにちは)( there)?\s*[!,.!、。]*\s*/i, "").trim() : t("How can we help?")) || t("How can we help you today?");
|
|
762
907
|
const hero = el(
|
|
763
908
|
"h2",
|
|
764
909
|
{ class: "ckw-msgr-hero ckw-msgr-hello" },
|
|
765
|
-
el("span", {}, first ?
|
|
910
|
+
el("span", {}, `${first ? tf("Hi, {name}", { name: first }) : t("Hi there")} `, el("span", { class: "ckw-msgr-wave", "aria-hidden": "true" }, "\u{1F44B}")),
|
|
766
911
|
el("span", { class: "ckw-msgr-hello-sub" }, rest)
|
|
767
912
|
);
|
|
768
|
-
const last = (thread?.messages ?? []).at(-1);
|
|
769
913
|
const parts = [hero, askForm];
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
{ class: "ckw-msgr-card-main" },
|
|
781
|
-
el("span", { class: "ckw-msgr-card-sub ckw-msgr-recent-text" }, lineOf(last)),
|
|
782
|
-
el("span", { class: "ckw-msgr-card-meta" }, `${last.from === "visitor" ? "You" : whoLabel[last.from]} \xB7 ${timeAgo(last.at)}`)
|
|
783
|
-
),
|
|
784
|
-
unseen.length ? el("span", { class: "ckw-msgr-badge" }, String(unseen.length)) : icon(I.next, 14)
|
|
785
|
-
)
|
|
786
|
-
);
|
|
787
|
-
recent.addEventListener("click", () => go("messages"));
|
|
788
|
-
parts.push(recent);
|
|
914
|
+
const running = chats.filter((c) => c.status !== "solved");
|
|
915
|
+
const rows = (running.length ? running : chats).slice(0, 2);
|
|
916
|
+
parts.push(...rows.map(chatRow));
|
|
917
|
+
if (chats.length > rows.length) {
|
|
918
|
+
const all = el("button", { type: "button", class: "ckw-msgr-all" }, tf("All conversations ({n})", { n: chats.length }), icon(I.next, 14));
|
|
919
|
+
all.addEventListener("click", () => {
|
|
920
|
+
enterMessages();
|
|
921
|
+
go("messages");
|
|
922
|
+
});
|
|
923
|
+
parts.push(all);
|
|
789
924
|
}
|
|
790
925
|
if (helpOn && hasArticles !== false) {
|
|
791
926
|
const helpCard = el("div", { class: "ckw-msgr-card ckw-msgr-card-col" });
|
|
@@ -803,13 +938,13 @@ var build = (o) => {
|
|
|
803
938
|
const groups = /* @__PURE__ */ new Map();
|
|
804
939
|
for (const a of all) groups.set(a.collection, [...groups.get(a.collection) ?? [], a]);
|
|
805
940
|
const inside = !q && collection !== null ? groups.get(collection) ?? [] : null;
|
|
806
|
-
const heading = q ? "Search results" : inside ? collection ?? "" : "Most read articles";
|
|
941
|
+
const heading = q ? t("Search results") : inside ? collection ?? "" : t("Most read articles");
|
|
807
942
|
const parts = [el("h2", { class: "ckw-msgr-help-h" }, heading), helpSearch];
|
|
808
943
|
if (articles === null) parts.push(el("div", { class: "ckw-skel" }));
|
|
809
944
|
else if (!all.length) {
|
|
810
|
-
const ask = el("button", { type: "button", class: "ckw-btn ckw-btn-primary" }, "Send us a message");
|
|
811
|
-
ask.addEventListener("click",
|
|
812
|
-
parts.push(el("div", { class: "ckw-msgr-empty" }, el("p", {}, q ?
|
|
945
|
+
const ask = el("button", { type: "button", class: "ckw-btn ckw-btn-primary" }, t("Send us a message"));
|
|
946
|
+
ask.addEventListener("click", askAnew);
|
|
947
|
+
parts.push(el("div", { class: "ckw-msgr-empty" }, el("p", {}, q ? tf("Nothing found for \u201C{q}\u201D.", { q }) : t("No help articles yet.")), ask));
|
|
813
948
|
} else if (q || inside)
|
|
814
949
|
parts.push(
|
|
815
950
|
el(
|
|
@@ -824,7 +959,7 @@ var build = (o) => {
|
|
|
824
959
|
"div",
|
|
825
960
|
{ class: "ckw-msgr-hlist" },
|
|
826
961
|
...[...groups].map(
|
|
827
|
-
([name, items]) => helpRow(name, items.map((a) => a.title).join(", "),
|
|
962
|
+
([name, items]) => helpRow(name, items.map((a) => a.title).join(", "), items.length === 1 ? t("1 article") : tf("{n} articles", { n: items.length }), () => {
|
|
828
963
|
collection = name;
|
|
829
964
|
go("help");
|
|
830
965
|
replay(body, "ckw-msgr-enter");
|
|
@@ -839,6 +974,14 @@ var build = (o) => {
|
|
|
839
974
|
body.append(...parts.slice(2));
|
|
840
975
|
}
|
|
841
976
|
};
|
|
977
|
+
const drawUpdates = () => {
|
|
978
|
+
updatesNode ??= !updates ? el("div", { class: "ckw-skel" }) : !updates.length ? el("div", { class: "ckw-msgr-empty" }, el("p", {}, t("No updates yet."))) : el(
|
|
979
|
+
"div",
|
|
980
|
+
{ class: "ckw-msgr-updates" },
|
|
981
|
+
...updates.map((e) => el("article", { class: "ckw-msgr-update" }, el("p", { class: "ckw-msgr-doc-c" }, timeAgo(e.publishedAt)), el("h2", { class: "ckw-msgr-update-t" }, e.title), e.body ? el("p", { class: "ckw-msgr-update-b" }, e.body) : null))
|
|
982
|
+
);
|
|
983
|
+
if (body.firstChild !== updatesNode || body.childNodes.length !== 1) body.replaceChildren(updatesNode);
|
|
984
|
+
};
|
|
842
985
|
let articleShown = null;
|
|
843
986
|
const drawArticle = (id) => {
|
|
844
987
|
if (articleShown?.id === id) return body.replaceChildren(articleShown.node);
|
|
@@ -848,26 +991,26 @@ var build = (o) => {
|
|
|
848
991
|
api().article(id).then((a) => {
|
|
849
992
|
const foot = el("div", { class: "ckw-msgr-rate" });
|
|
850
993
|
const drawRate = () => {
|
|
851
|
-
if (rated.has(id)) return foot.replaceChildren(el("span", {}, "Thanks for letting us know."));
|
|
852
|
-
const yes = el("button", { type: "button", class: "ckw-btn" }, "Yes");
|
|
853
|
-
const no = el("button", { type: "button", class: "ckw-btn" }, "No");
|
|
994
|
+
if (rated.has(id)) return foot.replaceChildren(el("span", {}, t("Thanks for letting us know.")));
|
|
995
|
+
const yes = el("button", { type: "button", class: "ckw-btn" }, t("Yes"));
|
|
996
|
+
const no = el("button", { type: "button", class: "ckw-btn" }, t("No"));
|
|
854
997
|
const rate = (helpful) => {
|
|
855
998
|
rated = new Set(rated).add(id);
|
|
856
999
|
drawRate();
|
|
857
1000
|
api().rateArticle(id, helpful).catch(() => void 0);
|
|
858
|
-
if (!helpful)
|
|
1001
|
+
if (!helpful) askAnew();
|
|
859
1002
|
};
|
|
860
1003
|
yes.addEventListener("click", () => rate(true));
|
|
861
1004
|
no.addEventListener("click", () => rate(false));
|
|
862
|
-
foot.replaceChildren(el("span", {}, "Did this answer your question?"), el("span", { class: "ckw-msgr-rate-b" }, yes, no));
|
|
1005
|
+
foot.replaceChildren(el("span", {}, t("Did this answer your question?")), el("span", { class: "ckw-msgr-rate-b" }, yes, no));
|
|
863
1006
|
};
|
|
864
1007
|
drawRate();
|
|
865
|
-
const ask = el("button", { type: "button", class: "ckw-btn ckw-msgr-ask" }, icon(I.chat), "Still need help? Send us a message");
|
|
866
|
-
ask.addEventListener("click",
|
|
1008
|
+
const ask = el("button", { type: "button", class: "ckw-btn ckw-msgr-ask" }, icon(I.chat), t("Still need help? Send us a message"));
|
|
1009
|
+
ask.addEventListener("click", askAnew);
|
|
867
1010
|
node.replaceChildren(
|
|
868
1011
|
el("p", { class: "ckw-msgr-doc-c" }, a.collection),
|
|
869
1012
|
el("h2", { class: "ckw-msgr-doc-t" }, a.title),
|
|
870
|
-
el("p", { class: "ckw-msgr-doc-c" },
|
|
1013
|
+
el("p", { class: "ckw-msgr-doc-c" }, tf("Updated {when}", { when: timeAgo(a.updatedAt) })),
|
|
871
1014
|
el("div", { class: "ckw-msgr-doc-b" }, ...a.blocks.map(blockOf)),
|
|
872
1015
|
foot,
|
|
873
1016
|
ask
|
|
@@ -881,22 +1024,25 @@ var build = (o) => {
|
|
|
881
1024
|
let listSig = "";
|
|
882
1025
|
let listCount = -1;
|
|
883
1026
|
const suggestions = el("div", { class: "ckw-msgr-sugg" });
|
|
1027
|
+
const outboxHere = () => outbox.filter((x) => x.draft === draft && x.chat === chatId);
|
|
884
1028
|
const drawMessages = () => {
|
|
885
|
-
const
|
|
1029
|
+
const current = draft === null ? thread : null;
|
|
1030
|
+
const msgs = current?.messages ?? [];
|
|
1031
|
+
const here = outboxHere();
|
|
886
1032
|
const parts = [];
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
const sig = `${msgs.map((m) => m.id).join(",")}|${
|
|
1033
|
+
{
|
|
1034
|
+
const waiting = here.map((o2) => o2.message);
|
|
1035
|
+
const sig = `${msgs.map((m) => m.id).join(",")}|${waiting.map((m) => `${m.id}:${Date.now() - Date.parse(m.at) >= SLOW_SEND_MS}`).join(",")}|${Math.floor(Date.now() / 6e4)}`;
|
|
890
1036
|
if (sig !== listSig) {
|
|
891
1037
|
listSig = sig;
|
|
892
|
-
const greet = el("li", { class: "ckw-msg", "data-from": "them" }, el("div", { class: "ckw-bubble" }, greeting));
|
|
893
|
-
const items = [greet, ...msgs.map((m) => bubble(m)), ...
|
|
1038
|
+
const greet = msgs.length || waiting.length || chatId ? [] : [el("li", { class: "ckw-msg", "data-from": "them" }, el("div", { class: "ckw-bubble" }, greeting))];
|
|
1039
|
+
const items = [...greet, ...msgs.map((m) => bubble(m)), ...waiting.map((m) => bubble(m, true))];
|
|
894
1040
|
if (listCount >= 0) items.slice(listCount).forEach((li) => li.setAttribute("data-new", ""));
|
|
895
1041
|
listCount = items.length;
|
|
896
1042
|
list.replaceChildren(...items);
|
|
897
1043
|
}
|
|
898
1044
|
parts.push(list);
|
|
899
|
-
const top = !msgs.some((m) => m.from === "visitor") && !
|
|
1045
|
+
const top = !msgs.some((m) => m.from === "visitor") && !here.length && !chatId ? (articles ?? []).slice(0, 3) : [];
|
|
900
1046
|
if (top.length) {
|
|
901
1047
|
if (suggestions.childNodes.length !== top.length)
|
|
902
1048
|
suggestions.replaceChildren(
|
|
@@ -909,9 +1055,9 @@ var build = (o) => {
|
|
|
909
1055
|
parts.push(suggestions);
|
|
910
1056
|
}
|
|
911
1057
|
}
|
|
912
|
-
if (
|
|
1058
|
+
if (current?.conversation?.status === "solved" && !here.length) parts.push(el("p", { class: "ckw-fine ckw-msgr-note" }, t("This conversation is closed. Write to start a new one.")));
|
|
913
1059
|
if (body.childNodes.length !== parts.length || parts.some((x, i) => body.childNodes[i] !== x)) body.replaceChildren(...parts);
|
|
914
|
-
const count = msgs.length +
|
|
1060
|
+
const count = msgs.length + here.length;
|
|
915
1061
|
if (count !== drawn) {
|
|
916
1062
|
drawn = count;
|
|
917
1063
|
body.scrollTop = body.scrollHeight;
|
|
@@ -932,24 +1078,35 @@ var build = (o) => {
|
|
|
932
1078
|
pop.dataset.view = typeof view === "string" ? view : "article";
|
|
933
1079
|
drawBar();
|
|
934
1080
|
if (view === "home") drawHome();
|
|
935
|
-
else if (view === "messages") drawMessages();
|
|
1081
|
+
else if (view === "messages") (listing ? drawChats : drawMessages)();
|
|
936
1082
|
else if (view === "help") drawHelp();
|
|
1083
|
+
else if (view === "updates") drawUpdates();
|
|
937
1084
|
else drawArticle(view.article);
|
|
938
1085
|
const kids = view === "home" ? [sky, bar, body] : [bar, body];
|
|
939
|
-
kids.push(view === "messages" ? form : tabs);
|
|
1086
|
+
kids.push(view === "messages" && !listing ? form : tabs);
|
|
940
1087
|
if (o.branding !== false) kids.push(brand);
|
|
941
1088
|
if (menu.isConnected) kids.push(menu);
|
|
942
1089
|
if (pop.childNodes.length !== kids.length || kids.some((k, i) => pop.childNodes[i] !== k)) pop.replaceChildren(...kids);
|
|
943
1090
|
};
|
|
944
1091
|
const tick = async () => {
|
|
945
|
-
ctrl
|
|
946
|
-
ctrl = new AbortController();
|
|
1092
|
+
if (ctrl) return;
|
|
1093
|
+
const mine = ctrl = new AbortController();
|
|
947
1094
|
try {
|
|
948
|
-
take(await api().chat(
|
|
1095
|
+
take(await api().chat(mine.signal, chatId ?? void 0));
|
|
949
1096
|
error = "";
|
|
1097
|
+
refused = false;
|
|
950
1098
|
} catch (e) {
|
|
951
1099
|
if (e.name === "AbortError") return;
|
|
952
|
-
|
|
1100
|
+
const status = e.status;
|
|
1101
|
+
refused = status === 401 || status === 403;
|
|
1102
|
+
if (status === 404 && chatId) enterMessages();
|
|
1103
|
+
else if (open && view === "messages") error = sayable(e);
|
|
1104
|
+
} finally {
|
|
1105
|
+
if (ctrl === mine) ctrl = null;
|
|
1106
|
+
}
|
|
1107
|
+
if (!loaded) {
|
|
1108
|
+
listCount = -1;
|
|
1109
|
+
if (view === "messages" && chatId === null && draft === null && !text.value.trim() && !outbox.length && !picked.length) enterMessages();
|
|
953
1110
|
}
|
|
954
1111
|
loaded = true;
|
|
955
1112
|
draw();
|
|
@@ -958,69 +1115,105 @@ var build = (o) => {
|
|
|
958
1115
|
};
|
|
959
1116
|
function schedule(now) {
|
|
960
1117
|
clearTimeout(poll);
|
|
961
|
-
const
|
|
962
|
-
const
|
|
963
|
-
const recent =
|
|
964
|
-
const
|
|
965
|
-
|
|
1118
|
+
const running = chats.filter((c) => c.status !== "solved");
|
|
1119
|
+
const waiting = running.length > 0;
|
|
1120
|
+
const recent = running.some((c) => c.last && Date.now() - new Date(c.last.at).getTime() < WAITING_FOR_MS);
|
|
1121
|
+
const quiet = now === void 0 && !open && !waiting;
|
|
1122
|
+
const ms = now ?? (document.visibilityState === "hidden" ? 0 : watching() ? POLL_OPEN_MS : waiting && recent ? POLL_WAITING_MS : quiet ? POLL_QUIET_MS : POLL_IDLE_MS);
|
|
1123
|
+
if (ms || now === 0) poll = setTimeout(() => quiet && (refused || !(o.user()?.id || chats.length)) ? schedule() : void tick(), ms);
|
|
966
1124
|
}
|
|
967
1125
|
const onVisible = () => {
|
|
968
1126
|
if (document.visibilityState === "visible") schedule(0);
|
|
969
1127
|
else clearTimeout(poll);
|
|
970
1128
|
};
|
|
971
1129
|
document.addEventListener("visibilitychange", onVisible);
|
|
972
|
-
const
|
|
1130
|
+
const flush = async () => {
|
|
1131
|
+
if (flushing) return;
|
|
1132
|
+
flushing = true;
|
|
1133
|
+
while (outbox.length) {
|
|
1134
|
+
const item = outbox[0];
|
|
1135
|
+
const { message, files, email: given } = item;
|
|
1136
|
+
await Promise.all(files.map((p) => p.up));
|
|
1137
|
+
const ids = files.filter((p) => p.id).map((p) => p.id);
|
|
1138
|
+
try {
|
|
1139
|
+
if (!message.body && !ids.length) throw new Error(error || t("The file could not be sent."));
|
|
1140
|
+
const sent = await api().sendChat({
|
|
1141
|
+
body: message.body,
|
|
1142
|
+
...item.draft !== null ? { conversation: "new" } : item.chat ? { conversation: item.chat } : {},
|
|
1143
|
+
email: given,
|
|
1144
|
+
url: window.location.href.slice(0, 500),
|
|
1145
|
+
...ids.length ? { attachments: ids } : {},
|
|
1146
|
+
// A little context for a bug report; the server reads browser and system from the user agent.
|
|
1147
|
+
context: { viewport: `${window.innerWidth}\xD7${window.innerHeight}` }
|
|
1148
|
+
});
|
|
1149
|
+
ctrl?.abort();
|
|
1150
|
+
ctrl = null;
|
|
1151
|
+
const went = sent.conversation?.id ?? null;
|
|
1152
|
+
if (went) {
|
|
1153
|
+
const after = outbox.slice(1).filter((x) => x.draft === item.draft && x.chat === item.chat);
|
|
1154
|
+
if (item.draft === draft && item.chat === chatId) {
|
|
1155
|
+
draft = null;
|
|
1156
|
+
chatId = went;
|
|
1157
|
+
}
|
|
1158
|
+
for (const x of after) {
|
|
1159
|
+
x.draft = null;
|
|
1160
|
+
x.chat = went;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
take(sent);
|
|
1164
|
+
outbox.shift();
|
|
1165
|
+
for (const p of files) {
|
|
1166
|
+
localUrls.delete(p.key);
|
|
1167
|
+
URL.revokeObjectURL(p.local);
|
|
1168
|
+
}
|
|
1169
|
+
} catch (e) {
|
|
1170
|
+
const back = outbox.splice(0);
|
|
1171
|
+
const gone = e.status === 404;
|
|
1172
|
+
enterChat(gone ? null : back[0].chat, gone ? ++drafts : back[0].draft);
|
|
1173
|
+
text.value = [...back.map((b) => b.message.body), text.value.trim()].filter(Boolean).join("\n");
|
|
1174
|
+
picked = [...back.flatMap((b) => b.files.filter((p) => p.id)), ...picked];
|
|
1175
|
+
for (const b of back) for (const p of b.files) localUrls.delete(p.key);
|
|
1176
|
+
error = gone ? t("This conversation is no longer available. Your message is still here to send as a new one.") : sayable(e);
|
|
1177
|
+
}
|
|
1178
|
+
draw();
|
|
1179
|
+
}
|
|
1180
|
+
flushing = false;
|
|
1181
|
+
schedule();
|
|
1182
|
+
};
|
|
1183
|
+
const submit = () => {
|
|
973
1184
|
const msg = text.value.trim();
|
|
974
1185
|
const files = picked;
|
|
975
|
-
if (!msg && !files.length ||
|
|
1186
|
+
if (!msg && !files.length || !emailOk()) return;
|
|
976
1187
|
chime.unlock();
|
|
977
1188
|
for (const p of files) localUrls.set(p.key, p.local);
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1189
|
+
outbox.push({
|
|
1190
|
+
message: {
|
|
1191
|
+
id: `pending-${++outboxSeq}`,
|
|
1192
|
+
from: "visitor",
|
|
1193
|
+
body: msg,
|
|
1194
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1195
|
+
...files.length ? { attachments: files.map((p) => ({ id: p.key, name: p.name, type: p.type, size: p.size })) } : {}
|
|
1196
|
+
},
|
|
1197
|
+
files,
|
|
1198
|
+
email: needEmail() && email.value.trim() ? email.value.trim() : void 0,
|
|
1199
|
+
chat: chatId,
|
|
1200
|
+
draft
|
|
1201
|
+
});
|
|
985
1202
|
text.value = "";
|
|
986
1203
|
picked = [];
|
|
987
1204
|
error = "";
|
|
988
1205
|
draw();
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
let sent = false;
|
|
992
|
-
try {
|
|
993
|
-
if (!msg && !ids.length) throw new Error(error || "The file could not be sent.");
|
|
994
|
-
take(
|
|
995
|
-
await api().sendChat({
|
|
996
|
-
body: msg,
|
|
997
|
-
email: needEmail() && email.value.trim() ? email.value.trim() : void 0,
|
|
998
|
-
url: window.location.href.slice(0, 500),
|
|
999
|
-
...ids.length ? { attachments: ids } : {},
|
|
1000
|
-
// A little context for a bug report; the server reads browser and system from the user agent.
|
|
1001
|
-
context: { viewport: `${window.innerWidth}\xD7${window.innerHeight}` }
|
|
1002
|
-
})
|
|
1003
|
-
);
|
|
1004
|
-
sent = true;
|
|
1005
|
-
} catch (e) {
|
|
1006
|
-
text.value = msg;
|
|
1007
|
-
picked = files.filter((p) => p.id);
|
|
1008
|
-
error = e.message;
|
|
1009
|
-
}
|
|
1010
|
-
pending = null;
|
|
1011
|
-
for (const p of files) localUrls.delete(p.key);
|
|
1012
|
-
draw();
|
|
1013
|
-
if (sent) for (const p of files) URL.revokeObjectURL(p.local);
|
|
1014
|
-
schedule();
|
|
1206
|
+
setTimeout(draw, SLOW_SEND_MS + 50);
|
|
1207
|
+
void flush();
|
|
1015
1208
|
};
|
|
1016
1209
|
form.addEventListener("submit", (e) => {
|
|
1017
1210
|
e.preventDefault();
|
|
1018
|
-
|
|
1211
|
+
submit();
|
|
1019
1212
|
});
|
|
1020
1213
|
text.addEventListener("keydown", (e) => {
|
|
1021
1214
|
if (e.key === "Enter" && !e.shiftKey && !e.isComposing) {
|
|
1022
1215
|
e.preventDefault();
|
|
1023
|
-
|
|
1216
|
+
submit();
|
|
1024
1217
|
}
|
|
1025
1218
|
});
|
|
1026
1219
|
text.addEventListener("input", () => send.disabled = !canSend());
|
|
@@ -1049,7 +1242,7 @@ var build = (o) => {
|
|
|
1049
1242
|
function show(v = "home", still = false) {
|
|
1050
1243
|
const was = open;
|
|
1051
1244
|
open = true;
|
|
1052
|
-
view = v === "help" && !helpOn ? "home" : v;
|
|
1245
|
+
view = v === "help" && !helpOn || v === "updates" && !updatesOn ? "home" : v;
|
|
1053
1246
|
drawn = -1;
|
|
1054
1247
|
previewGone = true;
|
|
1055
1248
|
pop.toggleAttribute("data-still", still);
|
|
@@ -1090,10 +1283,14 @@ var build = (o) => {
|
|
|
1090
1283
|
document.addEventListener("keydown", onKey);
|
|
1091
1284
|
void tick();
|
|
1092
1285
|
if (helpOn) void loadArticles();
|
|
1286
|
+
if (updatesOn) void loadUpdates();
|
|
1093
1287
|
drawFab();
|
|
1094
1288
|
if (o.opened) show(o.opened, true);
|
|
1095
1289
|
return {
|
|
1096
|
-
open: (v) =>
|
|
1290
|
+
open: (v) => {
|
|
1291
|
+
if (v === "messages") enterMessages();
|
|
1292
|
+
show(v ?? "home");
|
|
1293
|
+
},
|
|
1097
1294
|
close,
|
|
1098
1295
|
toggle: () => open ? close() : show(),
|
|
1099
1296
|
isOpen: () => open,
|
|
@@ -1102,7 +1299,7 @@ var build = (o) => {
|
|
|
1102
1299
|
},
|
|
1103
1300
|
destroy: () => {
|
|
1104
1301
|
clearTimeout(poll);
|
|
1105
|
-
for (const
|
|
1302
|
+
for (const t2 of leaving.values()) clearTimeout(t2);
|
|
1106
1303
|
ctrl?.abort();
|
|
1107
1304
|
for (const p of picked) forget(p);
|
|
1108
1305
|
for (const u of localUrls.values()) URL.revokeObjectURL(u);
|