@planningcenter/topbar 11.2.4 → 11.2.5-qa-832.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/dist/topbar.js +770 -22
- package/dist/topbar.js.map +1 -1
- package/dist/topbar.modern.js +771 -23
- package/dist/topbar.modern.js.map +1 -1
- package/dist/topbar.module.js +771 -23
- package/dist/topbar.module.js.map +1 -1
- package/dist/topbar.umd.js +773 -26
- package/dist/topbar.umd.js.map +1 -1
- package/dist/types/index.d.ts +16 -16
- package/package.json +4 -2
package/dist/topbar.module.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Link } from '@planningcenter/tapestry';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import React__default, { useState, useEffect, useMemo, useLayoutEffect, useCallback, useRef } from 'react';
|
|
3
|
+
import React__default, { useState, useEffect, useMemo, useLayoutEffect, useContext, createContext, useCallback, useRef } from 'react';
|
|
4
4
|
import pcoUrl$1 from '@planningcenter/url';
|
|
5
5
|
import * as general from '@planningcenter/icons/paths/general';
|
|
6
6
|
import Jolt from '@planningcenter/jolt-client';
|
|
7
7
|
import { useSyncExternalStore } from 'use-sync-external-store/shim';
|
|
8
|
+
import 'react/jsx-runtime';
|
|
8
9
|
import { ThemeProvider, Divider, Button, StackView as StackView$2, Text, Icon, designSystem, Box, Heading, GridView, Avatar as Avatar$1, Link as Link$1, HeadingUppercase, List, Combobox, Logo, Dropdown, Spinner, Badge } from '@planningcenter/tapestry-react';
|
|
9
10
|
import { components, UiExtensionFirstPartyComponent, UiExtensionDataContext } from '@planningcenter/add-ons';
|
|
10
11
|
import * as churchCenter from '@planningcenter/icons/paths/church-center';
|
|
@@ -2224,10 +2225,750 @@ const sortByAttribute = attr => arr => arr.slice().sort((a, b) => a[attr].locale
|
|
|
2224
2225
|
const appsMenuFormatter = apps => sortByAttribute("name")(apps).reduce((acc, item) => (item.name || "") === "Church Center" || (item.name || "") === "API" ? acc : item.name === "Home" ? [item, ...acc] : [...acc, item], []);
|
|
2225
2226
|
const connectedPeopleMenuFormatter = connectedPeople => connectedPeople.slice().sort((a, b) => a.attributes["organization_name"].localeCompare(b.attributes["organization_name"]));
|
|
2226
2227
|
|
|
2228
|
+
// Generated by scripts/write-version.cjs from package.json. Do not edit.
|
|
2229
|
+
const BRIDGE_VERSION = "0.0.7-qa-41.0";
|
|
2230
|
+
|
|
2231
|
+
/**
|
|
2232
|
+
* PCA_BRIDGE_BUS_INIT — the script injected via
|
|
2233
|
+
* `injectedJavaScriptBeforeContentLoaded` before any web content loads.
|
|
2234
|
+
*
|
|
2235
|
+
* Installs `window.__pcaBridge` in the WebView's JS context. The bus:
|
|
2236
|
+
* - is per-WebView-context-by-construction (each WebView has its own JS
|
|
2237
|
+
* context; the global lives only in that context)
|
|
2238
|
+
* - accepts multiple registered handlers per reserved message type so
|
|
2239
|
+
* multiple Bridge instances in the same WebView can coexist
|
|
2240
|
+
* - buffers messages of a type with no registered handlers for up to 5s
|
|
2241
|
+
* - pings native with `__bridge/busReady` after install so the native
|
|
2242
|
+
* transport can flush its outgoing queue and trigger `reannounceLocal()`
|
|
2243
|
+
*
|
|
2244
|
+
* The bus runs as an IIFE so consumers can dispatch and register against it
|
|
2245
|
+
* immediately. Internal state (buffers, handlers) is closure-private.
|
|
2246
|
+
*/
|
|
2247
|
+
|
|
2248
|
+
const PCA_BRIDGE_BUS_INIT = `
|
|
2249
|
+
(function () {
|
|
2250
|
+
if (typeof window === 'undefined') return
|
|
2251
|
+
if (window.__pcaBridge) return // idempotent in case it's injected twice
|
|
2252
|
+
|
|
2253
|
+
var BRIDGE_VERSION = ${JSON.stringify(BRIDGE_VERSION)}
|
|
2254
|
+
var BUFFER_TTL_MS = 5000
|
|
2255
|
+
var handlers = Object.create(null) // type -> Array<fn>
|
|
2256
|
+
var buffer = Object.create(null) // type -> Array<{msg, receivedAt}>
|
|
2257
|
+
var logger = {
|
|
2258
|
+
debug: function () {},
|
|
2259
|
+
warn: function (msg, ctx) {
|
|
2260
|
+
try { console.warn('[pca-bridge]', msg, ctx) } catch (e) {}
|
|
2261
|
+
},
|
|
2262
|
+
error: function (msg, ctx) {
|
|
2263
|
+
try { console.error('[pca-bridge]', msg, ctx) } catch (e) {}
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
function now() { return Date.now() }
|
|
2268
|
+
|
|
2269
|
+
function sweepBuffer() {
|
|
2270
|
+
var cutoff = now() - BUFFER_TTL_MS
|
|
2271
|
+
var dropped = []
|
|
2272
|
+
var hasAny = false
|
|
2273
|
+
for (var type in buffer) {
|
|
2274
|
+
var entries = buffer[type]
|
|
2275
|
+
var kept = []
|
|
2276
|
+
for (var i = 0; i < entries.length; i++) {
|
|
2277
|
+
if (entries[i].receivedAt >= cutoff) {
|
|
2278
|
+
kept.push(entries[i])
|
|
2279
|
+
} else {
|
|
2280
|
+
dropped.push({ type: type, ageMs: now() - entries[i].receivedAt })
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
if (kept.length === 0) delete buffer[type]
|
|
2284
|
+
else {
|
|
2285
|
+
buffer[type] = kept
|
|
2286
|
+
hasAny = true
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
if (dropped.length > 0 && window.ReactNativeWebView && typeof window.ReactNativeWebView.postMessage === 'function') {
|
|
2290
|
+
try {
|
|
2291
|
+
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
2292
|
+
type: '__bridge/drop',
|
|
2293
|
+
bridgeVersion: BRIDGE_VERSION,
|
|
2294
|
+
messages: dropped
|
|
2295
|
+
}))
|
|
2296
|
+
} catch (e) {}
|
|
2297
|
+
}
|
|
2298
|
+
// Stop ticking when the buffer drains; ensureSweep() restarts on demand.
|
|
2299
|
+
if (!hasAny && sweepInterval !== null) {
|
|
2300
|
+
clearInterval(sweepInterval)
|
|
2301
|
+
sweepInterval = null
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
var sweepInterval = null
|
|
2306
|
+
function ensureSweep() {
|
|
2307
|
+
if (sweepInterval !== null) return
|
|
2308
|
+
sweepInterval = setInterval(sweepBuffer, 1000)
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
function register(type, fn) {
|
|
2312
|
+
if (!handlers[type]) handlers[type] = []
|
|
2313
|
+
handlers[type].push(fn)
|
|
2314
|
+
// Drain any buffered messages for this type, applying the same TTL the
|
|
2315
|
+
// background sweep uses so entries that already expired don't get
|
|
2316
|
+
// delivered just because sweep hasn't run yet.
|
|
2317
|
+
var pending = buffer[type]
|
|
2318
|
+
if (pending && pending.length > 0) {
|
|
2319
|
+
delete buffer[type]
|
|
2320
|
+
var cutoff = now() - BUFFER_TTL_MS
|
|
2321
|
+
for (var i = 0; i < pending.length; i++) {
|
|
2322
|
+
if (pending[i].receivedAt < cutoff) continue
|
|
2323
|
+
try { fn(pending[i].msg) } catch (e) {
|
|
2324
|
+
logger.error('Handler threw while draining buffer', { type: type, error: e })
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
function unregister(type, fn) {
|
|
2331
|
+
var arr = handlers[type]
|
|
2332
|
+
if (!arr) return
|
|
2333
|
+
var idx = arr.indexOf(fn)
|
|
2334
|
+
if (idx >= 0) arr.splice(idx, 1)
|
|
2335
|
+
if (arr.length === 0) delete handlers[type]
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
function dispatch(msg) {
|
|
2339
|
+
if (!msg || typeof msg !== 'object' || typeof msg.type !== 'string') return
|
|
2340
|
+
var type = msg.type
|
|
2341
|
+
var arr = handlers[type]
|
|
2342
|
+
if (arr && arr.length > 0) {
|
|
2343
|
+
// Snapshot the array to avoid issues if a handler registers/unregisters during dispatch
|
|
2344
|
+
var snapshot = arr.slice()
|
|
2345
|
+
for (var i = 0; i < snapshot.length; i++) {
|
|
2346
|
+
try { snapshot[i](msg) } catch (e) {
|
|
2347
|
+
logger.error('Handler threw during dispatch', { type: type, error: e })
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
} else {
|
|
2351
|
+
if (!buffer[type]) buffer[type] = []
|
|
2352
|
+
buffer[type].push({ msg: msg, receivedAt: now() })
|
|
2353
|
+
ensureSweep()
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
function setLogger(next) {
|
|
2358
|
+
// Validate both methods the bus actually calls; a partial logger would
|
|
2359
|
+
// throw TypeError when dispatch/register tries to invoke a missing one.
|
|
2360
|
+
if (next && typeof next.warn === 'function' && typeof next.error === 'function') {
|
|
2361
|
+
logger = next
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
window.__pcaBridge = {
|
|
2366
|
+
register: register,
|
|
2367
|
+
unregister: unregister,
|
|
2368
|
+
dispatch: dispatch,
|
|
2369
|
+
setLogger: setLogger
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
// Ping native: the bus is ready in this JS context. Native transport
|
|
2373
|
+
// listens for this to (a) flush its outgoing queue and (b) call
|
|
2374
|
+
// bridge.reannounceLocal() so the freshly-loaded web context learns about
|
|
2375
|
+
// already-exposed local actions/values.
|
|
2376
|
+
try {
|
|
2377
|
+
if (window.ReactNativeWebView && typeof window.ReactNativeWebView.postMessage === 'function') {
|
|
2378
|
+
window.ReactNativeWebView.postMessage(JSON.stringify({ type: '__bridge/busReady', bridgeVersion: BRIDGE_VERSION }))
|
|
2379
|
+
}
|
|
2380
|
+
} catch (e) {}
|
|
2381
|
+
})();
|
|
2382
|
+
true;
|
|
2383
|
+
`;
|
|
2384
|
+
|
|
2385
|
+
class TimeoutError extends Error {
|
|
2386
|
+
constructor(name, timeoutMs) {
|
|
2387
|
+
super(`Call to "${name}" timed out after ${timeoutMs}ms`);
|
|
2388
|
+
this.name = 'TimeoutError';
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
class HandlerError extends Error {
|
|
2392
|
+
constructor(name, code, originalMessage) {
|
|
2393
|
+
super(`Handler for "${name}" threw [${code}]: ${originalMessage}`);
|
|
2394
|
+
this.name = 'HandlerError';
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
class DisposedError extends Error {
|
|
2398
|
+
constructor(name) {
|
|
2399
|
+
super(`Bridge disposed while call to "${name}" was in flight`);
|
|
2400
|
+
this.name = 'DisposedError';
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
let idCounter = 0;
|
|
2405
|
+
function generateId() {
|
|
2406
|
+
idCounter += 1;
|
|
2407
|
+
return `pca-bridge-${idCounter}`;
|
|
2408
|
+
}
|
|
2409
|
+
const noopLogger = {
|
|
2410
|
+
debug: () => {},
|
|
2411
|
+
warn: () => {},
|
|
2412
|
+
error: () => {}
|
|
2413
|
+
};
|
|
2414
|
+
function isIndexable(x) {
|
|
2415
|
+
return typeof x === 'object' && x !== null;
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
/**
|
|
2419
|
+
* Shallow equality, one level deep. Used by `BridgeCore.publish` to dedup
|
|
2420
|
+
* re-emissions of new-but-content-equal values (e.g., a freshly-built
|
|
2421
|
+
* `modalButtons` array whose contents didn't change between renders).
|
|
2422
|
+
*/
|
|
2423
|
+
function shallowEqual(a, b) {
|
|
2424
|
+
const sameRef = Object.is(a, b);
|
|
2425
|
+
const bothComparableObjects = isIndexable(a) && isIndexable(b) && Array.isArray(a) === Array.isArray(b);
|
|
2426
|
+
let result;
|
|
2427
|
+
if (sameRef) {
|
|
2428
|
+
result = true;
|
|
2429
|
+
} else if (!bothComparableObjects) {
|
|
2430
|
+
result = false;
|
|
2431
|
+
} else {
|
|
2432
|
+
const keys = Object.keys(a);
|
|
2433
|
+
result = keys.length === Object.keys(b).length && keys.every(k => Object.prototype.hasOwnProperty.call(b, k) && Object.is(a[k], b[k]));
|
|
2434
|
+
}
|
|
2435
|
+
return result;
|
|
2436
|
+
}
|
|
2437
|
+
function isWireEnvelope(parsed) {
|
|
2438
|
+
return typeof parsed === 'object' && parsed !== null && 'type' in parsed && typeof parsed.type === 'string' && parsed.type.startsWith('__bridge/') && 'bridgeVersion' in parsed && typeof parsed.bridgeVersion === 'string';
|
|
2439
|
+
}
|
|
2440
|
+
function parseWireEnvelope(rawData) {
|
|
2441
|
+
let parsed;
|
|
2442
|
+
try {
|
|
2443
|
+
parsed = JSON.parse(rawData);
|
|
2444
|
+
} catch {
|
|
2445
|
+
parsed = null;
|
|
2446
|
+
}
|
|
2447
|
+
return isWireEnvelope(parsed) ? parsed : null;
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
2451
|
+
const INCOMING_CALL_QUEUE_TTL_MS = 5000;
|
|
2452
|
+
const INCOMING_CALL_QUEUE_MAX_PER_ACTION = 32;
|
|
2453
|
+
class BridgeCore {
|
|
2454
|
+
injectedJavaScriptBeforeContentLoaded = PCA_BRIDGE_BUS_INIT;
|
|
2455
|
+
localVersion = BRIDGE_VERSION;
|
|
2456
|
+
// `never` arg type lets us widen any user-defined `LocalActions[K]` callable
|
|
2457
|
+
// with a single `as` cast inside `expose()`.
|
|
2458
|
+
localActions = new Map();
|
|
2459
|
+
localValues = new Map();
|
|
2460
|
+
remoteActions = new Set();
|
|
2461
|
+
remoteValues = new Map();
|
|
2462
|
+
actionListeners = new Map();
|
|
2463
|
+
valueListeners = new Map();
|
|
2464
|
+
pendingCalls = new Map();
|
|
2465
|
+
callQueue = new Map();
|
|
2466
|
+
|
|
2467
|
+
// Private backing field + public getter so the `readonly` declared on the
|
|
2468
|
+
// Bridge interface is actually enforced — not just at the interface level
|
|
2469
|
+
// but for any code holding a concrete BridgeCore reference.
|
|
2470
|
+
_remoteVersion = undefined;
|
|
2471
|
+
get remoteVersion() {
|
|
2472
|
+
return this._remoteVersion;
|
|
2473
|
+
}
|
|
2474
|
+
disposed = false;
|
|
2475
|
+
constructor(options) {
|
|
2476
|
+
const sendRaw = options.send;
|
|
2477
|
+
this.send = msg => {
|
|
2478
|
+
sendRaw({
|
|
2479
|
+
...msg,
|
|
2480
|
+
bridgeVersion: BRIDGE_VERSION
|
|
2481
|
+
});
|
|
2482
|
+
};
|
|
2483
|
+
this.logger = options.logger ?? noopLogger;
|
|
2484
|
+
this.defaultTimeoutMs = options.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
2485
|
+
this.now = options.now ?? Date.now;
|
|
2486
|
+
}
|
|
2487
|
+
call(name, args, options) {
|
|
2488
|
+
const requestId = generateId();
|
|
2489
|
+
const timeoutMs = options?.timeoutMs ?? this.defaultTimeoutMs;
|
|
2490
|
+
return new Promise((resolve, reject) => {
|
|
2491
|
+
const timer = setTimeout(() => {
|
|
2492
|
+
if (this.pendingCalls.delete(requestId)) {
|
|
2493
|
+
reject(new TimeoutError(name, timeoutMs));
|
|
2494
|
+
}
|
|
2495
|
+
}, timeoutMs);
|
|
2496
|
+
// Heterogeneous map: cast the typed resolve to the loose storage slot.
|
|
2497
|
+
this.pendingCalls.set(requestId, {
|
|
2498
|
+
name,
|
|
2499
|
+
resolve: resolve,
|
|
2500
|
+
reject,
|
|
2501
|
+
timer
|
|
2502
|
+
});
|
|
2503
|
+
this.send({
|
|
2504
|
+
type: '__bridge/call',
|
|
2505
|
+
requestId,
|
|
2506
|
+
name,
|
|
2507
|
+
args
|
|
2508
|
+
});
|
|
2509
|
+
});
|
|
2510
|
+
}
|
|
2511
|
+
expose(name, handler) {
|
|
2512
|
+
if (this.localActions.has(name)) {
|
|
2513
|
+
this.logger.warn('Action already exposed; last-write-wins (the previous handler is gone)', {
|
|
2514
|
+
name
|
|
2515
|
+
});
|
|
2516
|
+
}
|
|
2517
|
+
this.localActions.set(name, handler);
|
|
2518
|
+
this.send({
|
|
2519
|
+
type: '__bridge/exposed',
|
|
2520
|
+
name,
|
|
2521
|
+
kind: 'action'
|
|
2522
|
+
});
|
|
2523
|
+
const queued = this.callQueue.get(name);
|
|
2524
|
+
if (queued) {
|
|
2525
|
+
this.callQueue.delete(name);
|
|
2526
|
+
const cutoff = this.now() - INCOMING_CALL_QUEUE_TTL_MS;
|
|
2527
|
+
for (const entry of queued) {
|
|
2528
|
+
if (entry.receivedAt >= cutoff) {
|
|
2529
|
+
void this.invokeLocalHandler(entry.msg);
|
|
2530
|
+
} else {
|
|
2531
|
+
this.logger.warn('Queued incoming call expired before handler registered', {
|
|
2532
|
+
name,
|
|
2533
|
+
ageMs: this.now() - entry.receivedAt
|
|
2534
|
+
});
|
|
2535
|
+
this.rejectQueued(entry, 'expired');
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
unexpose(name) {
|
|
2541
|
+
if (this.localActions.delete(name)) {
|
|
2542
|
+
this.send({
|
|
2543
|
+
type: '__bridge/unexposed',
|
|
2544
|
+
name,
|
|
2545
|
+
kind: 'action'
|
|
2546
|
+
});
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
hasRemoteAction(name) {
|
|
2550
|
+
return this.remoteActions.has(name);
|
|
2551
|
+
}
|
|
2552
|
+
onRemoteAction(name, callback) {
|
|
2553
|
+
let set = this.actionListeners.get(name);
|
|
2554
|
+
if (!set) {
|
|
2555
|
+
set = new Set();
|
|
2556
|
+
this.actionListeners.set(name, set);
|
|
2557
|
+
}
|
|
2558
|
+
set.add(callback);
|
|
2559
|
+
return () => {
|
|
2560
|
+
const current = this.actionListeners.get(name);
|
|
2561
|
+
if (current) {
|
|
2562
|
+
current.delete(callback);
|
|
2563
|
+
if (current.size === 0) {
|
|
2564
|
+
this.actionListeners.delete(name);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
};
|
|
2568
|
+
}
|
|
2569
|
+
publish(name, value) {
|
|
2570
|
+
// Skip wire send if content unchanged — callers may publish on every
|
|
2571
|
+
// render with a fresh-but-content-equal value.
|
|
2572
|
+
if (!shallowEqual(this.localValues.get(name), value)) {
|
|
2573
|
+
this.localValues.set(name, value);
|
|
2574
|
+
this.send({
|
|
2575
|
+
type: '__bridge/publish',
|
|
2576
|
+
name,
|
|
2577
|
+
value
|
|
2578
|
+
});
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
unpublish(name) {
|
|
2582
|
+
if (this.localValues.delete(name)) {
|
|
2583
|
+
this.send({
|
|
2584
|
+
type: '__bridge/unexposed',
|
|
2585
|
+
name,
|
|
2586
|
+
kind: 'value'
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
seedLocalValue(name, value) {
|
|
2591
|
+
this.localValues.set(name, value);
|
|
2592
|
+
}
|
|
2593
|
+
getRemoteValue(name) {
|
|
2594
|
+
return this.remoteValues.get(name);
|
|
2595
|
+
}
|
|
2596
|
+
onRemoteValue(name, callback) {
|
|
2597
|
+
let set = this.valueListeners.get(name);
|
|
2598
|
+
if (!set) {
|
|
2599
|
+
set = new Set();
|
|
2600
|
+
this.valueListeners.set(name, set);
|
|
2601
|
+
}
|
|
2602
|
+
const wrapped = callback;
|
|
2603
|
+
set.add(wrapped);
|
|
2604
|
+
return () => {
|
|
2605
|
+
const current = this.valueListeners.get(name);
|
|
2606
|
+
if (current) {
|
|
2607
|
+
current.delete(wrapped);
|
|
2608
|
+
if (current.size === 0) {
|
|
2609
|
+
this.valueListeners.delete(name);
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
};
|
|
2613
|
+
}
|
|
2614
|
+
reannounceLocal() {
|
|
2615
|
+
for (const name of this.localActions.keys()) {
|
|
2616
|
+
this.send({
|
|
2617
|
+
type: '__bridge/exposed',
|
|
2618
|
+
name,
|
|
2619
|
+
kind: 'action'
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
for (const [name, value] of this.localValues) {
|
|
2623
|
+
this.send({
|
|
2624
|
+
type: '__bridge/exposed',
|
|
2625
|
+
name,
|
|
2626
|
+
kind: 'value',
|
|
2627
|
+
currentValue: value
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
/**
|
|
2633
|
+
* Drop remote-side cached state. Called by transports when the remote JS
|
|
2634
|
+
* context is about to be torn down (e.g., WebView reload): the new context
|
|
2635
|
+
* will re-announce what it actually exposes, and stale entries would
|
|
2636
|
+
* otherwise silently mask removed actions/values.
|
|
2637
|
+
*/
|
|
2638
|
+
clearRemoteState() {
|
|
2639
|
+
for (const name of this.remoteActions) {
|
|
2640
|
+
this.notifySet(this.actionListeners, name, false, 'action availability');
|
|
2641
|
+
}
|
|
2642
|
+
this.remoteActions.clear();
|
|
2643
|
+
for (const name of this.remoteValues.keys()) {
|
|
2644
|
+
this.notifySet(this.valueListeners, name, undefined, 'remote value change');
|
|
2645
|
+
}
|
|
2646
|
+
this.remoteValues.clear();
|
|
2647
|
+
this._remoteVersion = undefined;
|
|
2648
|
+
}
|
|
2649
|
+
handleMessage(rawData) {
|
|
2650
|
+
const msg = parseWireEnvelope(rawData);
|
|
2651
|
+
let consumed = false;
|
|
2652
|
+
if (msg) {
|
|
2653
|
+
this.handleIncomingMessage(msg);
|
|
2654
|
+
consumed = true;
|
|
2655
|
+
}
|
|
2656
|
+
return consumed;
|
|
2657
|
+
}
|
|
2658
|
+
applyLogger(logger) {
|
|
2659
|
+
if (this.logger === noopLogger) {
|
|
2660
|
+
this.logger = logger;
|
|
2661
|
+
return true;
|
|
2662
|
+
}
|
|
2663
|
+
return false;
|
|
2664
|
+
}
|
|
2665
|
+
handleLoadStart() {}
|
|
2666
|
+
handleLoadEnd() {}
|
|
2667
|
+
handleNavigationStateChange(navState) {
|
|
2668
|
+
if (!navState.loading) {
|
|
2669
|
+
this.handleLoadEnd();
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
handleIncomingMessage(msg) {
|
|
2673
|
+
if (!this.disposed) {
|
|
2674
|
+
this._remoteVersion = msg.bridgeVersion;
|
|
2675
|
+
switch (msg.type) {
|
|
2676
|
+
case '__bridge/call':
|
|
2677
|
+
void this.routeIncomingCall(msg);
|
|
2678
|
+
break;
|
|
2679
|
+
case '__bridge/response':
|
|
2680
|
+
this.handleIncomingResponse(msg);
|
|
2681
|
+
break;
|
|
2682
|
+
case '__bridge/exposed':
|
|
2683
|
+
this.handleIncomingExposed(msg);
|
|
2684
|
+
break;
|
|
2685
|
+
case '__bridge/unexposed':
|
|
2686
|
+
this.handleIncomingUnexposed(msg);
|
|
2687
|
+
break;
|
|
2688
|
+
case '__bridge/publish':
|
|
2689
|
+
this.handleIncomingPublish(msg);
|
|
2690
|
+
break;
|
|
2691
|
+
case '__bridge/busReady':
|
|
2692
|
+
case '__bridge/drop':
|
|
2693
|
+
break;
|
|
2694
|
+
default:
|
|
2695
|
+
{
|
|
2696
|
+
// parseWireEnvelope admits any `__bridge/*` type; this catches
|
|
2697
|
+
// future message types a remote on a newer bridge sends to us.
|
|
2698
|
+
const unknown = msg;
|
|
2699
|
+
this.logger.warn('Received unknown bridge message type; ignoring', {
|
|
2700
|
+
type: unknown.type,
|
|
2701
|
+
remoteVersion: this.remoteVersion
|
|
2702
|
+
});
|
|
2703
|
+
break;
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
get debug() {
|
|
2709
|
+
return {
|
|
2710
|
+
listLocalActions: () => Array.from(this.localActions.keys()),
|
|
2711
|
+
listRemoteActions: () => Array.from(this.remoteActions),
|
|
2712
|
+
listLocalValues: () => Array.from(this.localValues.keys()),
|
|
2713
|
+
listRemoteValues: () => Array.from(this.remoteValues.keys())
|
|
2714
|
+
};
|
|
2715
|
+
}
|
|
2716
|
+
dispose() {
|
|
2717
|
+
if (!this.disposed) {
|
|
2718
|
+
this.disposed = true;
|
|
2719
|
+
for (const pending of this.pendingCalls.values()) {
|
|
2720
|
+
clearTimeout(pending.timer);
|
|
2721
|
+
pending.reject(new DisposedError(pending.name));
|
|
2722
|
+
}
|
|
2723
|
+
this.pendingCalls.clear();
|
|
2724
|
+
this.callQueue.clear();
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
async routeIncomingCall(msg) {
|
|
2728
|
+
if (this.localActions.has(msg.name)) {
|
|
2729
|
+
await this.invokeLocalHandler(msg);
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
let queued = this.callQueue.get(msg.name);
|
|
2733
|
+
if (!queued) {
|
|
2734
|
+
queued = [];
|
|
2735
|
+
this.callQueue.set(msg.name, queued);
|
|
2736
|
+
}
|
|
2737
|
+
// Evict TTL-expired and over-capacity entries so a remote that spams an
|
|
2738
|
+
// unknown action can't grow the queue without bound. Each evicted call
|
|
2739
|
+
// gets an error response so the caller fails fast.
|
|
2740
|
+
const cutoff = this.now() - INCOMING_CALL_QUEUE_TTL_MS;
|
|
2741
|
+
while (queued.length > 0) {
|
|
2742
|
+
const head = queued[0];
|
|
2743
|
+
if (!head || head.receivedAt >= cutoff) break;
|
|
2744
|
+
queued.shift();
|
|
2745
|
+
this.rejectQueued(head, 'expired');
|
|
2746
|
+
}
|
|
2747
|
+
while (queued.length >= INCOMING_CALL_QUEUE_MAX_PER_ACTION) {
|
|
2748
|
+
const evicted = queued.shift();
|
|
2749
|
+
if (!evicted) break;
|
|
2750
|
+
this.rejectQueued(evicted, 'queue_full');
|
|
2751
|
+
}
|
|
2752
|
+
queued.push({
|
|
2753
|
+
msg,
|
|
2754
|
+
receivedAt: this.now()
|
|
2755
|
+
});
|
|
2756
|
+
}
|
|
2757
|
+
rejectQueued(entry, code) {
|
|
2758
|
+
this.send({
|
|
2759
|
+
type: '__bridge/response',
|
|
2760
|
+
requestId: entry.msg.requestId,
|
|
2761
|
+
ok: false,
|
|
2762
|
+
code,
|
|
2763
|
+
message: `Queued call to "${entry.msg.name}" dropped (${code})`
|
|
2764
|
+
});
|
|
2765
|
+
}
|
|
2766
|
+
async invokeLocalHandler(msg) {
|
|
2767
|
+
// Handler may have been unregistered between queue push and drain. Reply
|
|
2768
|
+
// with an explicit error so the caller fails fast instead of timing out.
|
|
2769
|
+
const handler = this.localActions.get(msg.name);
|
|
2770
|
+
if (!handler) {
|
|
2771
|
+
this.send({
|
|
2772
|
+
type: '__bridge/response',
|
|
2773
|
+
requestId: msg.requestId,
|
|
2774
|
+
ok: false,
|
|
2775
|
+
code: 'unexposed',
|
|
2776
|
+
message: `No handler exposed for "${msg.name}"`
|
|
2777
|
+
});
|
|
2778
|
+
return;
|
|
2779
|
+
}
|
|
2780
|
+
try {
|
|
2781
|
+
const result = await handler(msg.args);
|
|
2782
|
+
this.send({
|
|
2783
|
+
type: '__bridge/response',
|
|
2784
|
+
requestId: msg.requestId,
|
|
2785
|
+
ok: true,
|
|
2786
|
+
result
|
|
2787
|
+
});
|
|
2788
|
+
} catch (error) {
|
|
2789
|
+
this.send({
|
|
2790
|
+
type: '__bridge/response',
|
|
2791
|
+
requestId: msg.requestId,
|
|
2792
|
+
ok: false,
|
|
2793
|
+
code: 'thrown',
|
|
2794
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2795
|
+
});
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
handleIncomingResponse(msg) {
|
|
2799
|
+
const pending = this.pendingCalls.get(msg.requestId);
|
|
2800
|
+
if (pending) {
|
|
2801
|
+
this.pendingCalls.delete(msg.requestId);
|
|
2802
|
+
clearTimeout(pending.timer);
|
|
2803
|
+
if (msg.ok) {
|
|
2804
|
+
pending.resolve(msg.result);
|
|
2805
|
+
} else {
|
|
2806
|
+
pending.reject(new HandlerError(pending.name, msg.code, msg.message));
|
|
2807
|
+
}
|
|
2808
|
+
} else {
|
|
2809
|
+
this.logger.warn('Response for unknown or already-resolved requestId', {
|
|
2810
|
+
requestId: msg.requestId
|
|
2811
|
+
});
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
handleIncomingExposed(msg) {
|
|
2815
|
+
if (msg.kind === 'action') {
|
|
2816
|
+
if (!this.remoteActions.has(msg.name)) {
|
|
2817
|
+
this.remoteActions.add(msg.name);
|
|
2818
|
+
this.notifySet(this.actionListeners, msg.name, true, 'action availability');
|
|
2819
|
+
}
|
|
2820
|
+
} else {
|
|
2821
|
+
this.remoteValues.set(msg.name, msg.currentValue);
|
|
2822
|
+
this.notifySet(this.valueListeners, msg.name, msg.currentValue, 'remote value change');
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
handleIncomingUnexposed(msg) {
|
|
2826
|
+
if (msg.kind === 'action') {
|
|
2827
|
+
if (this.remoteActions.delete(msg.name)) {
|
|
2828
|
+
this.notifySet(this.actionListeners, msg.name, false, 'action availability');
|
|
2829
|
+
}
|
|
2830
|
+
} else if (this.remoteValues.delete(msg.name)) {
|
|
2831
|
+
this.notifySet(this.valueListeners, msg.name, undefined, 'remote value change');
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
handleIncomingPublish(msg) {
|
|
2835
|
+
this.remoteValues.set(msg.name, msg.value);
|
|
2836
|
+
this.notifySet(this.valueListeners, msg.name, msg.value, 'remote value change');
|
|
2837
|
+
}
|
|
2838
|
+
notifySet(subs, name, value, label) {
|
|
2839
|
+
const set = subs.get(name);
|
|
2840
|
+
if (set) {
|
|
2841
|
+
for (const callback of set) {
|
|
2842
|
+
try {
|
|
2843
|
+
callback(value);
|
|
2844
|
+
} catch (error) {
|
|
2845
|
+
this.logger.error(`Subscriber threw during ${label} notification`, {
|
|
2846
|
+
name,
|
|
2847
|
+
error
|
|
2848
|
+
});
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
// Subset of window.__pcaBridge that bridge_web.ts needs.
|
|
2856
|
+
|
|
2857
|
+
const INBOUND_TYPES = ['__bridge/call', '__bridge/response', '__bridge/exposed', '__bridge/unexposed', '__bridge/publish'];
|
|
2858
|
+
class WebBridge extends BridgeCore {
|
|
2859
|
+
constructor(options = {}) {
|
|
2860
|
+
const bus = window.__pcaBridge;
|
|
2861
|
+
super({
|
|
2862
|
+
send: msg => {
|
|
2863
|
+
if (window.ReactNativeWebView) {
|
|
2864
|
+
try {
|
|
2865
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(msg));
|
|
2866
|
+
} catch (error) {
|
|
2867
|
+
options.logger?.error('WebBridge postMessage failed', {
|
|
2868
|
+
error
|
|
2869
|
+
});
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
},
|
|
2873
|
+
logger: options.logger,
|
|
2874
|
+
defaultTimeoutMs: options.defaultTimeoutMs
|
|
2875
|
+
});
|
|
2876
|
+
this.bus = bus;
|
|
2877
|
+
this.handlerFn = msg => {
|
|
2878
|
+
this.handleIncomingMessage(msg);
|
|
2879
|
+
};
|
|
2880
|
+
if (bus) {
|
|
2881
|
+
if (options.logger) {
|
|
2882
|
+
bus.setLogger(options.logger);
|
|
2883
|
+
}
|
|
2884
|
+
for (const type of INBOUND_TYPES) {
|
|
2885
|
+
bus.register(type, this.handlerFn);
|
|
2886
|
+
}
|
|
2887
|
+
if (window.ReactNativeWebView) {
|
|
2888
|
+
try {
|
|
2889
|
+
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
2890
|
+
type: '__bridge/busReady',
|
|
2891
|
+
bridgeVersion: BRIDGE_VERSION
|
|
2892
|
+
}));
|
|
2893
|
+
} catch (error) {
|
|
2894
|
+
options.logger?.error('WebBridge busReady post failed', {
|
|
2895
|
+
error
|
|
2896
|
+
});
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
} else {
|
|
2900
|
+
options.logger?.warn('WebBridge constructed without window.__pcaBridge — bus init script may not have run');
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
applyLogger(logger) {
|
|
2904
|
+
const applied = super.applyLogger(logger);
|
|
2905
|
+
if (applied && this.bus) {
|
|
2906
|
+
this.bus.setLogger(logger);
|
|
2907
|
+
}
|
|
2908
|
+
return applied;
|
|
2909
|
+
}
|
|
2910
|
+
dispose() {
|
|
2911
|
+
if (this.bus) {
|
|
2912
|
+
for (const type of INBOUND_TYPES) {
|
|
2913
|
+
this.bus.unregister(type, this.handlerFn);
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
super.dispose();
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
function getOrCreateWebBridge(options = {}) {
|
|
2920
|
+
if (window.__pcaWebBridge && options.logger) {
|
|
2921
|
+
window.__pcaWebBridge.applyLogger(options.logger);
|
|
2922
|
+
} else if (!window.__pcaWebBridge) {
|
|
2923
|
+
window.__pcaWebBridge = new WebBridge(options);
|
|
2924
|
+
}
|
|
2925
|
+
return window.__pcaWebBridge;
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2928
|
+
const WebBridgeContext = /*#__PURE__*/createContext(null);
|
|
2929
|
+
function useWebBridge() {
|
|
2930
|
+
const ctxBridge = useContext(WebBridgeContext);
|
|
2931
|
+
if (ctxBridge) {
|
|
2932
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
2933
|
+
return ctxBridge;
|
|
2934
|
+
} else {
|
|
2935
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
2936
|
+
return getOrCreateWebBridge();
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
function useBridgePublish(bridge, name, value) {
|
|
2940
|
+
// publish() dedups by shallow equality, so this effect is safe to re-run
|
|
2941
|
+
// on every value change without spamming wire messages.
|
|
2942
|
+
useEffect(() => {
|
|
2943
|
+
bridge.publish(name, value);
|
|
2944
|
+
}, [bridge, name, value]);
|
|
2945
|
+
useEffect(() => {
|
|
2946
|
+
return () => {
|
|
2947
|
+
bridge.unpublish(name);
|
|
2948
|
+
};
|
|
2949
|
+
}, [bridge, name]);
|
|
2950
|
+
}
|
|
2951
|
+
|
|
2227
2952
|
const useSetCurrentRouteInfo = ({
|
|
2228
2953
|
routes,
|
|
2229
2954
|
app
|
|
2230
2955
|
}) => {
|
|
2956
|
+
const bridge = useWebBridge();
|
|
2957
|
+
useBridgePublish(bridge, "routeInfo", {
|
|
2958
|
+
currentApp: app,
|
|
2959
|
+
currentRoutes: routes.map(({
|
|
2960
|
+
key,
|
|
2961
|
+
props: {
|
|
2962
|
+
to,
|
|
2963
|
+
href,
|
|
2964
|
+
active
|
|
2965
|
+
}
|
|
2966
|
+
}) => ({
|
|
2967
|
+
active,
|
|
2968
|
+
name: key,
|
|
2969
|
+
url: to !== null && to !== void 0 ? to : href
|
|
2970
|
+
}))
|
|
2971
|
+
});
|
|
2231
2972
|
useEffect(() => {
|
|
2232
2973
|
if (window.topbar) {
|
|
2233
2974
|
window.topbar.routeInfo = {
|
|
@@ -3442,12 +4183,16 @@ var styles = {"toolbar":"toolbar-KHJR5","toolbar-button":"toolbar-button-Ye4h5",
|
|
|
3442
4183
|
|
|
3443
4184
|
const pcoUrl = pcoUrl$1;
|
|
3444
4185
|
|
|
4186
|
+
// The route rides in the iframe URL fragment (`…/topbar#conversations/abc`),
|
|
4187
|
+
// not the path, so updating it is a same-document navigation that doesn't
|
|
4188
|
+
// reload the iframe, but also lets the route survive a forced reload (e.g.
|
|
4189
|
+
// Turbo re-parenting the topbar).
|
|
3445
4190
|
function buildPicobotSrc(origin, path) {
|
|
3446
4191
|
const [rawPath, queryPart] = path.replace(/^\/+/, "").split("?", 2);
|
|
3447
4192
|
const safePath = rawPath.split("/").filter(segment => segment && segment !== "." && segment !== "..").join("/");
|
|
3448
|
-
const
|
|
3449
|
-
const
|
|
3450
|
-
return
|
|
4193
|
+
const fragment = queryPart !== undefined ? `${safePath}?${queryPart}` : safePath;
|
|
4194
|
+
const base = new URL("topbar", origin).toString();
|
|
4195
|
+
return fragment ? `${base}#${fragment}` : base;
|
|
3451
4196
|
}
|
|
3452
4197
|
|
|
3453
4198
|
const handleSearchSelect = (hit, indexName) => {
|
|
@@ -3615,13 +4360,10 @@ function HelpdeskComponent({
|
|
|
3615
4360
|
onMessage
|
|
3616
4361
|
}) {
|
|
3617
4362
|
const ref = React__default.useRef(null);
|
|
3618
|
-
|
|
3619
|
-
//
|
|
3620
|
-
//
|
|
3621
|
-
|
|
3622
|
-
// no leading slash or /topbar prefix—buildPicobotSrc adds those.
|
|
3623
|
-
const lastReportedPathRef = React__default.useRef(initialPath);
|
|
3624
|
-
const [loadedPath, setLoadedPath] = React__default.useState(initialPath);
|
|
4363
|
+
// Keep the fragment in sync in both directions—external opens (deeplink /
|
|
4364
|
+
// Contact support) and picobot's own NEW_ROUTE_RENDERED reports—so it
|
|
4365
|
+
// always reflects the live conversation. See buildPicobotSrc.
|
|
4366
|
+
const [loadedPath, setLoadedPath] = React__default.useState(iframePath || "");
|
|
3625
4367
|
React__default.useEffect(() => {
|
|
3626
4368
|
if (!onIframeChangePath && !onMessage) return undefined;
|
|
3627
4369
|
const handler = e => {
|
|
@@ -3631,7 +4373,7 @@ function HelpdeskComponent({
|
|
|
3631
4373
|
if (e.source !== ref.current.contentWindow) return;
|
|
3632
4374
|
if (((_a = e.data) === null || _a === void 0 ? void 0 : _a.type) === "NEW_ROUTE_RENDERED" && onIframeChangePath) {
|
|
3633
4375
|
const reportedPath = String((_b = e.data.payload) !== null && _b !== void 0 ? _b : "");
|
|
3634
|
-
|
|
4376
|
+
setLoadedPath(reportedPath);
|
|
3635
4377
|
onIframeChangePath(reportedPath);
|
|
3636
4378
|
}
|
|
3637
4379
|
if (onMessage) onMessage(e.data);
|
|
@@ -3640,9 +4382,7 @@ function HelpdeskComponent({
|
|
|
3640
4382
|
return () => window.removeEventListener("message", handler);
|
|
3641
4383
|
}, [onIframeChangePath, onMessage]);
|
|
3642
4384
|
React__default.useEffect(() => {
|
|
3643
|
-
|
|
3644
|
-
if (lastReportedPathRef.current === incomingPath) return;
|
|
3645
|
-
setLoadedPath(incomingPath);
|
|
4385
|
+
setLoadedPath(iframePath || "");
|
|
3646
4386
|
}, [iframePath]);
|
|
3647
4387
|
const iframeSrc = buildPicobotSrc(picobotOrigin, loadedPath);
|
|
3648
4388
|
return /*#__PURE__*/React__default.createElement("iframe", {
|
|
@@ -3697,7 +4437,8 @@ const Helpdesk = {
|
|
|
3697
4437
|
staticDotPath: "",
|
|
3698
4438
|
staticPath: "M16 10C14.9391 10 13.9217 10.4214 13.1716 11.1716C12.4214 11.9217 12 12.9391 12 14H14C14 13.4696 14.2107 12.9609 14.5858 12.5858C14.9609 12.2107 15.4696 12 16 12C16.5304 12 17.0391 12.2107 17.4142 12.5858C17.7893 12.9609 18 13.4696 18 14C18 16 15 15.75 15 19H17C17 16.75 20 16.5 20 14C20 12.9391 19.5786 11.9217 18.8284 11.1716C18.0783 10.4214 17.0609 10 16 10ZM16 24C14.4178 24 12.871 23.5308 11.5554 22.6518C10.2399 21.7727 9.21447 20.5233 8.60897 19.0615C8.00347 17.5997 7.84504 15.9911 8.15372 14.4393C8.4624 12.8874 9.22433 11.462 10.3432 10.3431C11.462 9.22433 12.8874 8.4624 14.4393 8.15372C15.9911 7.84504 17.5997 8.00346 19.0615 8.60896C20.5233 9.21446 21.7727 10.2398 22.6518 11.5554C23.5308 12.871 24 14.4177 24 16C23.9974 18.1209 23.1537 20.1542 21.6539 21.6539C20.1542 23.1537 18.1209 23.9974 16 24ZM16 6C14.0222 6 12.0888 6.58649 10.4443 7.6853C8.79981 8.78412 7.51809 10.3459 6.76121 12.1732C6.00433 14.0004 5.8063 16.0111 6.19215 17.9509C6.578 19.8907 7.53041 21.6725 8.92894 23.0711C10.3275 24.4696 12.1093 25.422 14.0491 25.8079C15.9889 26.1937 17.9996 25.9957 19.8268 25.2388C21.6541 24.4819 23.2159 23.2002 24.3147 21.5557C25.4135 19.9112 26 17.9778 26 16C26 13.3478 24.9464 10.8043 23.0711 8.92893C21.1957 7.05357 18.6522 6 16 6V6ZM15 22H17V20H15V22Z"
|
|
3699
4439
|
},
|
|
3700
|
-
name: "Helpdesk"
|
|
4440
|
+
name: "Helpdesk",
|
|
4441
|
+
storageExemptPaths: ["email", "phone"]
|
|
3701
4442
|
};
|
|
3702
4443
|
|
|
3703
4444
|
const Eyebrow = ({
|
|
@@ -4628,6 +5369,13 @@ function isToolPathExemptFromStorage(toolName, path) {
|
|
|
4628
5369
|
const tool = toolsRouteMap[toolName];
|
|
4629
5370
|
return (_b = (_a = tool === null || tool === void 0 ? void 0 : tool.storageExemptPaths) === null || _a === void 0 ? void 0 : _a.includes(path)) !== null && _b !== void 0 ? _b : false;
|
|
4630
5371
|
}
|
|
5372
|
+
function scrollToolDrawerToTop(toolName) {
|
|
5373
|
+
var _a;
|
|
5374
|
+
(_a = document.getElementById(`toolbarDrawer${toolName}`)) === null || _a === void 0 ? void 0 : _a.scrollTo({
|
|
5375
|
+
behavior: "smooth",
|
|
5376
|
+
top: 0
|
|
5377
|
+
});
|
|
5378
|
+
}
|
|
4631
5379
|
const TOOLS_MAP = {
|
|
4632
5380
|
Chat,
|
|
4633
5381
|
Helpdesk,
|
|
@@ -4697,17 +5445,21 @@ function Toolbar({
|
|
|
4697
5445
|
unsubscribe();
|
|
4698
5446
|
};
|
|
4699
5447
|
}, [open, clearRoute]);
|
|
5448
|
+
const openHelpdeskAtRoot = useCallback(() => {
|
|
5449
|
+
onChangeToolbarFragmentRoute(Helpdesk.name.toLowerCase(), "");
|
|
5450
|
+
scrollToolDrawerToTop(Helpdesk.name);
|
|
5451
|
+
}, [onChangeToolbarFragmentRoute]);
|
|
4700
5452
|
React__default.useEffect(() => {
|
|
4701
5453
|
window.topbar = window.topbar || {};
|
|
4702
5454
|
window.topbar.openProfile = () => handleToolOnClick(Profile);
|
|
4703
|
-
window.topbar.openHelpdesk =
|
|
5455
|
+
window.topbar.openHelpdesk = openHelpdeskAtRoot;
|
|
4704
5456
|
window.openHelpdesk = window.topbar.openHelpdesk; /* TODO: Remove when products have migrated to window.topbar.openHelpdesk */
|
|
4705
5457
|
return () => {
|
|
4706
5458
|
delete window.topbar.openProfile;
|
|
4707
5459
|
delete window.topbar.openHelpdesk;
|
|
4708
5460
|
delete window.openHelpdesk; /* TODO: Remove when products have migrated to window.topbar.openHelpdesk */
|
|
4709
5461
|
};
|
|
4710
|
-
}, [
|
|
5462
|
+
}, [Profile, openHelpdeskAtRoot]);
|
|
4711
5463
|
const loginSettingsUrl = getLoginProfilePath({
|
|
4712
5464
|
appName: appName.replace(/[\s-]/, ""),
|
|
4713
5465
|
env: env,
|
|
@@ -4789,12 +5541,8 @@ function Toolbar({
|
|
|
4789
5541
|
});
|
|
4790
5542
|
const TOOLS = [Notifications, Tasks, displayChatIcon && Chat].filter(Boolean);
|
|
4791
5543
|
const handleToolOnClick = tool => {
|
|
4792
|
-
var _a;
|
|
4793
5544
|
toggleDrawer(tool);
|
|
4794
|
-
(
|
|
4795
|
-
behavior: "smooth",
|
|
4796
|
-
top: 0
|
|
4797
|
-
});
|
|
5545
|
+
scrollToolDrawerToTop(tool.name);
|
|
4798
5546
|
};
|
|
4799
5547
|
const toolSpecificProps = {
|
|
4800
5548
|
Profile: {
|