@seatlayer/js 0.47.2 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -1
- package/dist/{channelsMode-33ZLORVF.js → channelsMode-NCH5GYHN.js} +3 -3
- package/dist/{chunk-X2R4AQSZ.js → chunk-H4OJF6LE.js} +176 -7
- package/dist/chunk-H4OJF6LE.js.map +1 -0
- package/dist/{chunk-DMEFXZIL.js → chunk-KNMZQZXR.js} +2 -2
- package/dist/{chunk-CF5MS7UR.js → chunk-Q5QT3YLA.js} +186 -59
- package/dist/chunk-Q5QT3YLA.js.map +1 -0
- package/dist/index.cjs +555 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -4
- package/dist/index.d.ts +17 -4
- package/dist/index.js +200 -32
- package/dist/index.js.map +1 -1
- package/dist/{manager-CAIPkUYl.d.cts → manager-DEjbKiqJ.d.cts} +200 -17
- package/dist/{manager-CAIPkUYl.d.ts → manager-DEjbKiqJ.d.ts} +200 -17
- package/dist/manager.cjs +358 -62
- package/dist/manager.cjs.map +1 -1
- package/dist/manager.d.cts +1 -1
- package/dist/manager.d.ts +1 -1
- package/dist/manager.js +2 -2
- package/package.json +2 -2
- package/dist/chunk-CF5MS7UR.js.map +0 -1
- package/dist/chunk-X2R4AQSZ.js.map +0 -1
- /package/dist/{channelsMode-33ZLORVF.js.map → channelsMode-NCH5GYHN.js.map} +0 -0
- /package/dist/{chunk-DMEFXZIL.js.map → chunk-KNMZQZXR.js.map} +0 -0
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PickerSeat, RendererViewMode, SeatHoverDetails, PickerTransport, PickerMapTheme } from '@seatlayer/core';
|
|
2
2
|
export { ExpandedSeat, SeatHoverDetails } from '@seatlayer/core';
|
|
3
|
-
import { C as ChannelListResult, a as ChannelAllocationPage, b as ChannelRecord, A as AssignmentResult, c as ChannelPreviewProjection, d as ChannelAccessIntent, e as AccessLinkReveal, f as AccessLinkStatusRecord, g as ChannelSeatStatus, B as BucketRow } from './manager-
|
|
4
|
-
export { h as ACCESS_LINK_DEFAULTS, i as AccessIntentForbidsDetails, j as AccessLinkRecord, k as AccessLinkState, l as AccessLinkStatus, m as ArchiveBlockedDetails, n as AssignmentBuckets, o as AssignmentDropDetails, p as ChannelAccessSummary, q as
|
|
3
|
+
import { C as ChannelListResult, a as ChannelAllocationPage, b as ChannelRecord, A as AssignmentResult, c as ChannelPreviewProjection, d as ChannelAccessIntent, e as AccessLinkReveal, f as AccessLinkStatusRecord, g as ChannelSeatStatus, B as BucketRow } from './manager-DEjbKiqJ.cjs';
|
|
4
|
+
export { h as ACCESS_LINK_DEFAULTS, i as AccessIntentForbidsDetails, j as AccessLinkRecord, k as AccessLinkState, l as AccessLinkStatus, m as ArchiveBlockedDetails, n as AssignmentBuckets, o as AssignmentDropDetails, p as ChannelAccessSummary, q as ChannelAttribution, r as ChannelAuditEntry, s as ChannelAuditPage, t as ChannelCounts, u as ChannelReport, v as ChannelReportLinkRecord, w as ChannelReportLinkReveal, x as ChannelReportResult, y as ChannelReportRow, z as ChannelState, D as ControlRoomActivityEntry, E as ControlRoomSectionMetric, F as ControlRoomSnapshot, I as IntentSwitchBlockedDetails, G as InventoryBooking, H as InventoryBookingActivity, J as InventoryBookingDetail, K as InventoryBookingObject, L as InventoryBookingState, M as InventoryBookingsPage, N as InventoryBookingsQuery, O as LogEntry, P as LogPage, Q as ManageApi, R as ManageApiError, S as PUBLIC_CHANNEL_ID, T as PUBLIC_CHANNEL_NAME, U as ReportByStatus, V as ReportCategoryMeta, W as ReportCategoryRow, X as ReportResult, Y as SeatManager, Z as SeatManagerActionResult, _ as SeatManagerActivity, $ as SeatManagerCapability, a0 as SeatManagerConnection, a1 as SeatManagerMode, a2 as SeatManagerOptions, a3 as SeatManagerTallies, a4 as SelectionSourceRow, a5 as accessIntentDescription, a6 as accessIntentLabel, a7 as accessLine, a8 as accessLinkBadge, a9 as accessLinkErrorCopy, aa as accessLinkIsLive, ab as accessLinkPolicyLines, ac as bucketRows, ad as dropReviewRows, ae as intentForbidsCopy, af as intentSwitchBlockedCopy, ag as isPublicChannelId, ah as markerLetter, ai as markerOf, aj as mutationCount, ak as needsMoveConfirmation, al as planAssignment, am as retryAfterCopy, an as selectionSources, ao as stateBadge, ap as suggestMarker } from './manager-DEjbKiqJ.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Buyer access context — the browser half of the Sales Channels contract
|
|
@@ -369,8 +369,10 @@ interface ControllerSinkOptions {
|
|
|
369
369
|
declare function createControllerSink(controller: PickerControllerLike, options?: ControllerSinkOptions): RealtimeSink;
|
|
370
370
|
|
|
371
371
|
/**
|
|
372
|
-
* Minimal client for the
|
|
373
|
-
* routes).
|
|
372
|
+
* Minimal client for the browser embed surface of workers/api (the `/pub/*`
|
|
373
|
+
* routes). Platform events bind this client to a buyer access context; Managed
|
|
374
|
+
* public/unlisted events may still use it anonymously. Deliberately
|
|
375
|
+
* self-contained — it does NOT reuse src/lib/api.ts,
|
|
374
376
|
* which bakes in a build-time API base and dashboard session credentials. The
|
|
375
377
|
* SDK runs cross-origin on a third-party ticketing page, so:
|
|
376
378
|
* - apiBase is per-instance (constructor option), not a build constant;
|
|
@@ -1371,6 +1373,13 @@ interface SeatPickerOptions {
|
|
|
1371
1373
|
* owns hold persistence and supplies initialHoldId itself.
|
|
1372
1374
|
*/
|
|
1373
1375
|
restoreHold?: boolean;
|
|
1376
|
+
/**
|
|
1377
|
+
* Render the real chart and live seat statuses without allowing selection,
|
|
1378
|
+
* holds or checkout. This is for venue previews and pre-sale Website pages;
|
|
1379
|
+
* it is enforced by the widget even if the event later opens while mounted.
|
|
1380
|
+
* Default false.
|
|
1381
|
+
*/
|
|
1382
|
+
readOnly?: boolean;
|
|
1374
1383
|
/**
|
|
1375
1384
|
* Confirm mode: tapping a seat shows a confirmation card with section, row,
|
|
1376
1385
|
* seat, category, price and Select/Cancel before it enters the tray. Default
|
|
@@ -1522,6 +1531,8 @@ interface SeatPickerOptions {
|
|
|
1522
1531
|
declare class SeatPicker {
|
|
1523
1532
|
private readonly opts;
|
|
1524
1533
|
private readonly api;
|
|
1534
|
+
/** Authenticated view media, cached only for this picker lifetime. */
|
|
1535
|
+
private readonly buyerAssetUrls;
|
|
1525
1536
|
/** Our own public client, or null when the host injected a transport. */
|
|
1526
1537
|
private readonly pubApi;
|
|
1527
1538
|
/** Null for the ordinary public picker — the tokenless path is untouched. */
|
|
@@ -1615,6 +1626,8 @@ declare class SeatPicker {
|
|
|
1615
1626
|
private secCardEl;
|
|
1616
1627
|
private viewEl;
|
|
1617
1628
|
private viewCleanup;
|
|
1629
|
+
/** Supersedes an older authored-view byte request when another seat is opened. */
|
|
1630
|
+
private seatViewGen;
|
|
1618
1631
|
private allSeatsCache;
|
|
1619
1632
|
private miniCanvas;
|
|
1620
1633
|
private miniBase;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PickerSeat, RendererViewMode, SeatHoverDetails, PickerTransport, PickerMapTheme } from '@seatlayer/core';
|
|
2
2
|
export { ExpandedSeat, SeatHoverDetails } from '@seatlayer/core';
|
|
3
|
-
import { C as ChannelListResult, a as ChannelAllocationPage, b as ChannelRecord, A as AssignmentResult, c as ChannelPreviewProjection, d as ChannelAccessIntent, e as AccessLinkReveal, f as AccessLinkStatusRecord, g as ChannelSeatStatus, B as BucketRow } from './manager-
|
|
4
|
-
export { h as ACCESS_LINK_DEFAULTS, i as AccessIntentForbidsDetails, j as AccessLinkRecord, k as AccessLinkState, l as AccessLinkStatus, m as ArchiveBlockedDetails, n as AssignmentBuckets, o as AssignmentDropDetails, p as ChannelAccessSummary, q as
|
|
3
|
+
import { C as ChannelListResult, a as ChannelAllocationPage, b as ChannelRecord, A as AssignmentResult, c as ChannelPreviewProjection, d as ChannelAccessIntent, e as AccessLinkReveal, f as AccessLinkStatusRecord, g as ChannelSeatStatus, B as BucketRow } from './manager-DEjbKiqJ.js';
|
|
4
|
+
export { h as ACCESS_LINK_DEFAULTS, i as AccessIntentForbidsDetails, j as AccessLinkRecord, k as AccessLinkState, l as AccessLinkStatus, m as ArchiveBlockedDetails, n as AssignmentBuckets, o as AssignmentDropDetails, p as ChannelAccessSummary, q as ChannelAttribution, r as ChannelAuditEntry, s as ChannelAuditPage, t as ChannelCounts, u as ChannelReport, v as ChannelReportLinkRecord, w as ChannelReportLinkReveal, x as ChannelReportResult, y as ChannelReportRow, z as ChannelState, D as ControlRoomActivityEntry, E as ControlRoomSectionMetric, F as ControlRoomSnapshot, I as IntentSwitchBlockedDetails, G as InventoryBooking, H as InventoryBookingActivity, J as InventoryBookingDetail, K as InventoryBookingObject, L as InventoryBookingState, M as InventoryBookingsPage, N as InventoryBookingsQuery, O as LogEntry, P as LogPage, Q as ManageApi, R as ManageApiError, S as PUBLIC_CHANNEL_ID, T as PUBLIC_CHANNEL_NAME, U as ReportByStatus, V as ReportCategoryMeta, W as ReportCategoryRow, X as ReportResult, Y as SeatManager, Z as SeatManagerActionResult, _ as SeatManagerActivity, $ as SeatManagerCapability, a0 as SeatManagerConnection, a1 as SeatManagerMode, a2 as SeatManagerOptions, a3 as SeatManagerTallies, a4 as SelectionSourceRow, a5 as accessIntentDescription, a6 as accessIntentLabel, a7 as accessLine, a8 as accessLinkBadge, a9 as accessLinkErrorCopy, aa as accessLinkIsLive, ab as accessLinkPolicyLines, ac as bucketRows, ad as dropReviewRows, ae as intentForbidsCopy, af as intentSwitchBlockedCopy, ag as isPublicChannelId, ah as markerLetter, ai as markerOf, aj as mutationCount, ak as needsMoveConfirmation, al as planAssignment, am as retryAfterCopy, an as selectionSources, ao as stateBadge, ap as suggestMarker } from './manager-DEjbKiqJ.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Buyer access context — the browser half of the Sales Channels contract
|
|
@@ -369,8 +369,10 @@ interface ControllerSinkOptions {
|
|
|
369
369
|
declare function createControllerSink(controller: PickerControllerLike, options?: ControllerSinkOptions): RealtimeSink;
|
|
370
370
|
|
|
371
371
|
/**
|
|
372
|
-
* Minimal client for the
|
|
373
|
-
* routes).
|
|
372
|
+
* Minimal client for the browser embed surface of workers/api (the `/pub/*`
|
|
373
|
+
* routes). Platform events bind this client to a buyer access context; Managed
|
|
374
|
+
* public/unlisted events may still use it anonymously. Deliberately
|
|
375
|
+
* self-contained — it does NOT reuse src/lib/api.ts,
|
|
374
376
|
* which bakes in a build-time API base and dashboard session credentials. The
|
|
375
377
|
* SDK runs cross-origin on a third-party ticketing page, so:
|
|
376
378
|
* - apiBase is per-instance (constructor option), not a build constant;
|
|
@@ -1371,6 +1373,13 @@ interface SeatPickerOptions {
|
|
|
1371
1373
|
* owns hold persistence and supplies initialHoldId itself.
|
|
1372
1374
|
*/
|
|
1373
1375
|
restoreHold?: boolean;
|
|
1376
|
+
/**
|
|
1377
|
+
* Render the real chart and live seat statuses without allowing selection,
|
|
1378
|
+
* holds or checkout. This is for venue previews and pre-sale Website pages;
|
|
1379
|
+
* it is enforced by the widget even if the event later opens while mounted.
|
|
1380
|
+
* Default false.
|
|
1381
|
+
*/
|
|
1382
|
+
readOnly?: boolean;
|
|
1374
1383
|
/**
|
|
1375
1384
|
* Confirm mode: tapping a seat shows a confirmation card with section, row,
|
|
1376
1385
|
* seat, category, price and Select/Cancel before it enters the tray. Default
|
|
@@ -1522,6 +1531,8 @@ interface SeatPickerOptions {
|
|
|
1522
1531
|
declare class SeatPicker {
|
|
1523
1532
|
private readonly opts;
|
|
1524
1533
|
private readonly api;
|
|
1534
|
+
/** Authenticated view media, cached only for this picker lifetime. */
|
|
1535
|
+
private readonly buyerAssetUrls;
|
|
1525
1536
|
/** Our own public client, or null when the host injected a transport. */
|
|
1526
1537
|
private readonly pubApi;
|
|
1527
1538
|
/** Null for the ordinary public picker — the tokenless path is untouched. */
|
|
@@ -1615,6 +1626,8 @@ declare class SeatPicker {
|
|
|
1615
1626
|
private secCardEl;
|
|
1616
1627
|
private viewEl;
|
|
1617
1628
|
private viewCleanup;
|
|
1629
|
+
/** Supersedes an older authored-view byte request when another seat is opened. */
|
|
1630
|
+
private seatViewGen;
|
|
1618
1631
|
private allSeatsCache;
|
|
1619
1632
|
private miniCanvas;
|
|
1620
1633
|
private miniBase;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SeatManager
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Q5QT3YLA.js";
|
|
4
4
|
import {
|
|
5
5
|
ACCESS_LINK_DEFAULTS,
|
|
6
6
|
ChannelsMode,
|
|
@@ -28,11 +28,11 @@ import {
|
|
|
28
28
|
selectionSources,
|
|
29
29
|
stateBadge,
|
|
30
30
|
suggestMarker
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-KNMZQZXR.js";
|
|
32
32
|
import {
|
|
33
33
|
ManageApi,
|
|
34
34
|
ManageApiError
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-H4OJF6LE.js";
|
|
36
36
|
import {
|
|
37
37
|
__privateAdd,
|
|
38
38
|
__privateGet,
|
|
@@ -524,9 +524,54 @@ var PubApi = class {
|
|
|
524
524
|
}
|
|
525
525
|
return data;
|
|
526
526
|
}
|
|
527
|
+
/**
|
|
528
|
+
* Binary counterpart to `request`. Buyer media needs the same in-memory
|
|
529
|
+
* bearer/refresh rules as JSON, but returns bytes that the picker turns into
|
|
530
|
+
* a blob URL. The bearer stays in the Authorization header and is never
|
|
531
|
+
* appended to `path`.
|
|
532
|
+
*/
|
|
533
|
+
async requestBlob(path, retried = {}) {
|
|
534
|
+
const headers = {};
|
|
535
|
+
const authorization = await this.access?.authorization(retried.auth ? "unauthorized" : "initial");
|
|
536
|
+
if (authorization) headers.Authorization = authorization;
|
|
537
|
+
const res = await fetch(`${this.base}${path}`, { method: "GET", headers, credentials: "omit" });
|
|
538
|
+
if (res.ok) return res.blob();
|
|
539
|
+
const isJson = (res.headers.get("content-type") ?? "").includes("application/json");
|
|
540
|
+
const data = isJson ? await res.json().catch(() => null) : null;
|
|
541
|
+
const code = data?.code ?? data?.error;
|
|
542
|
+
if (this.access?.configured && (res.status === 401 || res.status === 403 || res.status === 422)) {
|
|
543
|
+
const refreshed = await this.access.handleFailure(res.status, code);
|
|
544
|
+
if (refreshed && !retried.auth) return this.requestBlob(path, { ...retried, auth: true });
|
|
545
|
+
}
|
|
546
|
+
let retryAfterS;
|
|
547
|
+
if (res.status === 429) {
|
|
548
|
+
retryAfterS = parseRetryAfter(res.headers.get("Retry-After"), data?.retryAfterSeconds) ?? DEFAULT_RATE_LIMIT_WAIT_S;
|
|
549
|
+
if (!retried.rateLimit && retryAfterS <= MAX_RATE_LIMIT_WAIT_S) {
|
|
550
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfterS * 1e3));
|
|
551
|
+
return this.requestBlob(path, { ...retried, rateLimit: true });
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
throw new ApiError(
|
|
555
|
+
res.status,
|
|
556
|
+
data?.error ?? `request_failed_${res.status}`,
|
|
557
|
+
code,
|
|
558
|
+
void 0,
|
|
559
|
+
void 0,
|
|
560
|
+
retryAfterS
|
|
561
|
+
);
|
|
562
|
+
}
|
|
527
563
|
chart(key) {
|
|
528
564
|
return this.request(`/pub/events/${encodeURIComponent(key)}/chart`);
|
|
529
565
|
}
|
|
566
|
+
/** Authenticated bytes for an Event-scoped authored view image. */
|
|
567
|
+
asset(key, asset) {
|
|
568
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(asset)) {
|
|
569
|
+
return Promise.reject(new ApiError(404, "not_found", "not_found"));
|
|
570
|
+
}
|
|
571
|
+
return this.requestBlob(
|
|
572
|
+
`/pub/events/${encodeURIComponent(key)}/assets/${encodeURIComponent(asset)}`
|
|
573
|
+
);
|
|
574
|
+
}
|
|
530
575
|
objects(key) {
|
|
531
576
|
return this.request(`/pub/events/${encodeURIComponent(key)}/objects?compact=1`);
|
|
532
577
|
}
|
|
@@ -629,12 +674,11 @@ var PubApi = class {
|
|
|
629
674
|
/**
|
|
630
675
|
* What PickerController opens its own socket with.
|
|
631
676
|
*
|
|
632
|
-
* Empty for an access-scoped client: a
|
|
677
|
+
* Empty for an access-scoped client: a scoped audience authenticates with a
|
|
633
678
|
* subprotocol ticket, which a URL-only constructor cannot carry, so the SDK's
|
|
634
679
|
* BuyerRealtimeClient owns that socket instead and the controller skips its
|
|
635
680
|
* own (an empty URL is its documented "no live feed" contract). A tokenless
|
|
636
|
-
* public client returns exactly the URL it always has
|
|
637
|
-
* public picker's realtime path changes.
|
|
681
|
+
* Managed public client returns exactly the URL it always has.
|
|
638
682
|
*/
|
|
639
683
|
socketUrl(key) {
|
|
640
684
|
return this.accessScoped ? "" : this.subscribeUrl(key);
|
|
@@ -1994,6 +2038,81 @@ import {
|
|
|
1994
2038
|
schedulePanoramaUpgrade
|
|
1995
2039
|
} from "@seatlayer/core/view/panoramaDelivery";
|
|
1996
2040
|
|
|
2041
|
+
// src/buyerAssets.ts
|
|
2042
|
+
var SAFE_ASSET = /^[a-zA-Z0-9._-]+$/;
|
|
2043
|
+
function buyerEventAssetReference(value) {
|
|
2044
|
+
let url;
|
|
2045
|
+
try {
|
|
2046
|
+
url = new URL(value, "https://seatlayer.invalid");
|
|
2047
|
+
} catch {
|
|
2048
|
+
return null;
|
|
2049
|
+
}
|
|
2050
|
+
if (url.search || url.hash) return null;
|
|
2051
|
+
const match = /^\/pub\/events\/([^/]+)\/assets\/([^/]+)$/.exec(url.pathname);
|
|
2052
|
+
if (!match) return null;
|
|
2053
|
+
try {
|
|
2054
|
+
const eventKey = decodeURIComponent(match[1]);
|
|
2055
|
+
const asset = decodeURIComponent(match[2]);
|
|
2056
|
+
if (!eventKey || !SAFE_ASSET.test(asset)) return null;
|
|
2057
|
+
return { eventKey, asset };
|
|
2058
|
+
} catch {
|
|
2059
|
+
return null;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
function looksLikeBuyerAsset(value) {
|
|
2063
|
+
try {
|
|
2064
|
+
return /^\/pub\/events\/[^/]+\/assets(?:\/|$)/.test(
|
|
2065
|
+
new URL(value, "https://seatlayer.invalid").pathname
|
|
2066
|
+
);
|
|
2067
|
+
} catch {
|
|
2068
|
+
return false;
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
var BuyerAssetObjectUrls = class {
|
|
2072
|
+
constructor(eventKey, load) {
|
|
2073
|
+
this.eventKey = eventKey;
|
|
2074
|
+
this.load = load;
|
|
2075
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
2076
|
+
this.created = /* @__PURE__ */ new Set();
|
|
2077
|
+
this.disposed = false;
|
|
2078
|
+
}
|
|
2079
|
+
/**
|
|
2080
|
+
* External organizer/CDN URLs pass through unchanged. SeatLayer event assets
|
|
2081
|
+
* never do: they require the transport, and a reference for another Event is
|
|
2082
|
+
* refused instead of being loaded anonymously.
|
|
2083
|
+
*/
|
|
2084
|
+
resolve(reference) {
|
|
2085
|
+
const parsed = buyerEventAssetReference(reference);
|
|
2086
|
+
if (!parsed) {
|
|
2087
|
+
return Promise.resolve(looksLikeBuyerAsset(reference) ? null : reference);
|
|
2088
|
+
}
|
|
2089
|
+
if (parsed.eventKey !== this.eventKey || !this.load || this.disposed) return Promise.resolve(null);
|
|
2090
|
+
const existing = this.pending.get(reference);
|
|
2091
|
+
if (existing) return existing;
|
|
2092
|
+
const task = this.load(this.eventKey, parsed.asset).then((blob) => {
|
|
2093
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
2094
|
+
if (this.disposed) {
|
|
2095
|
+
URL.revokeObjectURL(objectUrl);
|
|
2096
|
+
return null;
|
|
2097
|
+
}
|
|
2098
|
+
this.created.add(objectUrl);
|
|
2099
|
+
return objectUrl;
|
|
2100
|
+
}).catch((error) => {
|
|
2101
|
+
this.pending.delete(reference);
|
|
2102
|
+
throw error;
|
|
2103
|
+
});
|
|
2104
|
+
this.pending.set(reference, task);
|
|
2105
|
+
return task;
|
|
2106
|
+
}
|
|
2107
|
+
dispose() {
|
|
2108
|
+
if (this.disposed) return;
|
|
2109
|
+
this.disposed = true;
|
|
2110
|
+
for (const url of this.created) URL.revokeObjectURL(url);
|
|
2111
|
+
this.created.clear();
|
|
2112
|
+
this.pending.clear();
|
|
2113
|
+
}
|
|
2114
|
+
};
|
|
2115
|
+
|
|
1997
2116
|
// src/offerAvailability.ts
|
|
1998
2117
|
var SALE_STATES = [
|
|
1999
2118
|
"on-sale",
|
|
@@ -3157,6 +3276,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
3157
3276
|
this.secCardEl = null;
|
|
3158
3277
|
this.viewEl = null;
|
|
3159
3278
|
this.viewCleanup = null;
|
|
3279
|
+
/** Supersedes an older authored-view byte request when another seat is opened. */
|
|
3280
|
+
this.seatViewGen = 0;
|
|
3160
3281
|
this.allSeatsCache = null;
|
|
3161
3282
|
// F3 minimap
|
|
3162
3283
|
this.miniCanvas = null;
|
|
@@ -3229,6 +3350,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
3229
3350
|
onObjectUnavailable: (event) => this.opts.onSelectedObjectUnavailable?.(event)
|
|
3230
3351
|
});
|
|
3231
3352
|
this.api = options.transport ?? this.pubApi;
|
|
3353
|
+
this.buyerAssetUrls = new BuyerAssetObjectUrls(
|
|
3354
|
+
options.event,
|
|
3355
|
+
this.api.asset ? (key, asset) => this.api.asset(key, asset) : void 0
|
|
3356
|
+
);
|
|
3232
3357
|
if (options.checkout === "hosted" && !this.pubApi) {
|
|
3233
3358
|
console.warn(
|
|
3234
3359
|
'seatlayer: checkout: "hosted" needs the widget\'s own transport \u2014 a custom `transport` owns its backend, so the picker is staying on onCheckout for this mount.'
|
|
@@ -3357,7 +3482,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3357
3482
|
}
|
|
3358
3483
|
}
|
|
3359
3484
|
const sightHtml = hasStage && distance != null ? `<div class="sl-confirm-sight">${t2("picker.sightline", { m: distance })}</div>` : "";
|
|
3360
|
-
const viewBtn = realPhoto ? `<button type="button" class="sl-confirm-view sl-confirm-thumbwrap" aria-label="${t2("picker.viewFromSeat", { label: seat.label })}"><img class="sl-confirm-thumb"
|
|
3485
|
+
const viewBtn = realPhoto ? `<button type="button" class="sl-confirm-view sl-confirm-thumbwrap" aria-label="${t2("picker.viewFromSeat", { label: seat.label })}"><img class="sl-confirm-thumb" alt="" /><span class="sl-confirm-thumb-badge">\u{1F52D} ${this.tf("picker.viewFromHere", "View from here")}</span></button>` : `<button type="button" class="sl-confirm-view sl-confirm-viewbtn" aria-label="${t2("picker.viewFromSeat", { label: seat.label })}"><span aria-hidden="true">\u{1F52D}</span><span>${this.tf("picker.viewFromHere", "View from here")}</span></button>`;
|
|
3361
3486
|
return viewBtn + sightHtml;
|
|
3362
3487
|
}
|
|
3363
3488
|
/** "See it in 3D" (2D) / "View from this seat" (already in 3D) action for the
|
|
@@ -3808,7 +3933,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3808
3933
|
}
|
|
3809
3934
|
this.els.boot.remove();
|
|
3810
3935
|
this.startRealtime();
|
|
3811
|
-
this.salesClosed = !!info.salesClosed;
|
|
3936
|
+
this.salesClosed = !!info.salesClosed || !!this.opts.readOnly;
|
|
3812
3937
|
this.controller.setViewMode(this.normalizeInitialView(this.opts.initialView));
|
|
3813
3938
|
this.buildRegions();
|
|
3814
3939
|
this.regions["bottom-right"].appendChild(this.els.zoom);
|
|
@@ -4055,8 +4180,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
4055
4180
|
* idempotent DOM apply used at load and on transition.
|
|
4056
4181
|
*/
|
|
4057
4182
|
setSalesClosed(closed) {
|
|
4058
|
-
|
|
4059
|
-
this.salesClosed
|
|
4183
|
+
const next = closed || !!this.opts.readOnly;
|
|
4184
|
+
if (this.salesClosed === next) return;
|
|
4185
|
+
this.salesClosed = next;
|
|
4060
4186
|
this.applySalesClosed();
|
|
4061
4187
|
}
|
|
4062
4188
|
applySalesClosed() {
|
|
@@ -4940,6 +5066,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
4940
5066
|
el.innerHTML = `<div class="sl-confirm-grid">` + identityFields + `</div><div class="sl-confirm-cat"><span class="sl-dot" style="background:${cat?.color ?? "#6e7bff"}"></span><span class="sl-confirm-cat-name">${safe(details?.categoryLabel ?? cat?.label ?? seat.categoryKey)}</span>` + (price != null ? `<span class="sl-confirm-price">${this.money(price)}</span>` : "") + `</div><div class="sl-confirm-body">` + this.wheelchairConfirmHtml(details?.wheelchairSpaceType) + this.commercialConfirmHtml(seat.commercial) + (this.seatViewEnabled() ? this.confirmThumbHtml(seat) : "") + this.see3dConfirmHtml() + `<div class="sl-confirm-row"><button type="button" class="sl-confirm-cancel">Cancel</button><button type="button" class="sl-confirm-add"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4 4L19 7"/></svg>Select</button></div></div>`;
|
|
4941
5067
|
this.els.map.appendChild(el);
|
|
4942
5068
|
this.confirmEl = el;
|
|
5069
|
+
const thumb = el.querySelector(".sl-confirm-thumb");
|
|
5070
|
+
if (thumb && seat.viewUrl) {
|
|
5071
|
+
const thumbReference = seat.viewMeta?.previewUrl ?? seat.viewUrl;
|
|
5072
|
+
void this.buyerAssetUrls.resolve(thumbReference).then((url) => {
|
|
5073
|
+
if (url && el.isConnected && this.confirmEl === el) thumb.src = url;
|
|
5074
|
+
}).catch((error) => this.opts.onError?.(error));
|
|
5075
|
+
}
|
|
4943
5076
|
this.reanchorConfirm();
|
|
4944
5077
|
el.querySelector(".sl-confirm-view")?.addEventListener("click", () => void this.openSeatView(seat));
|
|
4945
5078
|
el.querySelector(".sl-confirm-3d")?.addEventListener("click", () => {
|
|
@@ -5020,6 +5153,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5020
5153
|
*/
|
|
5021
5154
|
async openSeatView(seat) {
|
|
5022
5155
|
if (!this.root || !this.seatViewEnabled()) return;
|
|
5156
|
+
const generation = ++this.seatViewGen;
|
|
5023
5157
|
const doc = this.controller.doc;
|
|
5024
5158
|
const activeId = this.controller.getActiveFloorId();
|
|
5025
5159
|
const focal = seat.focalPoint ?? doc?.floors?.find((f) => f.id === activeId)?.focalPoint ?? doc?.focalPoint ?? { x: 0, y: 0 };
|
|
@@ -5027,9 +5161,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
5027
5161
|
let caption;
|
|
5028
5162
|
let real = false;
|
|
5029
5163
|
if (seat.viewUrl) {
|
|
5164
|
+
let resolvedUrl;
|
|
5165
|
+
let resolvedPreviewUrl = null;
|
|
5166
|
+
try {
|
|
5167
|
+
const previewReference = seat.viewMeta?.previewUrl;
|
|
5168
|
+
if (previewReference && previewReference !== seat.viewUrl) {
|
|
5169
|
+
resolvedPreviewUrl = await this.buyerAssetUrls.resolve(previewReference);
|
|
5170
|
+
resolvedUrl = seat.viewUrl;
|
|
5171
|
+
} else {
|
|
5172
|
+
resolvedUrl = await this.buyerAssetUrls.resolve(seat.viewUrl);
|
|
5173
|
+
}
|
|
5174
|
+
} catch (error) {
|
|
5175
|
+
if (generation === this.seatViewGen) this.opts.onError?.(error);
|
|
5176
|
+
return;
|
|
5177
|
+
}
|
|
5178
|
+
if (generation !== this.seatViewGen || !resolvedUrl || seat.viewMeta?.previewUrl && seat.viewMeta.previewUrl !== seat.viewUrl && !resolvedPreviewUrl || !this.root || !this.seatViewEnabled()) return;
|
|
5030
5179
|
const view = {
|
|
5031
|
-
url:
|
|
5032
|
-
...
|
|
5180
|
+
url: resolvedUrl,
|
|
5181
|
+
...resolvedPreviewUrl ? { previewUrl: resolvedPreviewUrl } : {},
|
|
5033
5182
|
...seat.viewMeta?.sourceWidth !== void 0 ? { sourceWidth: seat.viewMeta.sourceWidth } : {},
|
|
5034
5183
|
...seat.viewMeta?.sourceHeight !== void 0 ? { sourceHeight: seat.viewMeta.sourceHeight } : {},
|
|
5035
5184
|
...seat.viewMeta?.previewWidth !== void 0 ? { previewWidth: seat.viewMeta.previewWidth } : {},
|
|
@@ -5047,14 +5196,14 @@ var SeatPicker = class _SeatPicker {
|
|
|
5047
5196
|
const { generateSeatPanorama } = await loadPanorama();
|
|
5048
5197
|
pano2 = generateSeatPanorama(seat, focal, this.allSeats());
|
|
5049
5198
|
} catch (err) {
|
|
5050
|
-
this.opts.onError?.(err);
|
|
5199
|
+
if (generation === this.seatViewGen) this.opts.onError?.(err);
|
|
5051
5200
|
return;
|
|
5052
5201
|
}
|
|
5053
|
-
if (!this.root || !this.seatViewEnabled()) return;
|
|
5202
|
+
if (generation !== this.seatViewGen || !this.root || !this.seatViewEnabled()) return;
|
|
5054
5203
|
panoSource = { url: pano2.url, generated: true };
|
|
5055
5204
|
caption = t2("picker.illustrationCaption", { m: pano2.distanceM });
|
|
5056
5205
|
}
|
|
5057
|
-
this.closeSeatView();
|
|
5206
|
+
this.closeSeatView(false);
|
|
5058
5207
|
const el = document.createElement("div");
|
|
5059
5208
|
el.className = "sl-view";
|
|
5060
5209
|
el.setAttribute("role", "dialog");
|
|
@@ -5070,9 +5219,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
5070
5219
|
};
|
|
5071
5220
|
if (delivery.upgradeUrl) {
|
|
5072
5221
|
cancelUpgrade = schedulePanoramaUpgrade(() => {
|
|
5073
|
-
void
|
|
5074
|
-
if (!
|
|
5075
|
-
|
|
5222
|
+
void this.buyerAssetUrls.resolve(delivery.upgradeUrl).then((url) => {
|
|
5223
|
+
if (!url || loadAbort.signal.aborted) return null;
|
|
5224
|
+
return loadPanoramaImage(url, loadAbort.signal).then(() => url);
|
|
5225
|
+
}).then((url) => {
|
|
5226
|
+
if (!url || !el.isConnected || loadAbort.signal.aborted) return;
|
|
5227
|
+
pano.style.backgroundImage = `url("${url}")`;
|
|
5076
5228
|
}).catch(() => {
|
|
5077
5229
|
});
|
|
5078
5230
|
});
|
|
@@ -5148,7 +5300,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
5148
5300
|
el.removeEventListener("keydown", onKey);
|
|
5149
5301
|
};
|
|
5150
5302
|
}
|
|
5151
|
-
closeSeatView() {
|
|
5303
|
+
closeSeatView(cancelPending = true) {
|
|
5304
|
+
if (cancelPending) this.seatViewGen += 1;
|
|
5152
5305
|
this.viewCleanup?.();
|
|
5153
5306
|
this.viewCleanup = null;
|
|
5154
5307
|
this.viewEl?.remove();
|
|
@@ -6231,19 +6384,33 @@ var SeatPicker = class _SeatPicker {
|
|
|
6231
6384
|
async seatViewFor3d(seatId) {
|
|
6232
6385
|
const seat = this.allSeats().find((s) => s.id === seatId);
|
|
6233
6386
|
if (!seat) return null;
|
|
6234
|
-
if (seat.viewUrl)
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6387
|
+
if (seat.viewUrl) {
|
|
6388
|
+
try {
|
|
6389
|
+
const previewReference = seat.viewMeta?.previewUrl;
|
|
6390
|
+
const progressive = !!previewReference && previewReference !== seat.viewUrl;
|
|
6391
|
+
const previewUrl = progressive ? await this.buyerAssetUrls.resolve(previewReference) : null;
|
|
6392
|
+
const url = progressive ? seat.viewUrl : await this.buyerAssetUrls.resolve(seat.viewUrl);
|
|
6393
|
+
if (!url) return null;
|
|
6394
|
+
if (progressive && !previewUrl) return null;
|
|
6395
|
+
return {
|
|
6396
|
+
url,
|
|
6397
|
+
...previewUrl ? { previewUrl } : {},
|
|
6398
|
+
...progressive ? { resolveUrl: (reference) => this.buyerAssetUrls.resolve(reference) } : {},
|
|
6399
|
+
...seat.viewMeta?.sourceWidth !== void 0 ? { sourceWidth: seat.viewMeta.sourceWidth } : {},
|
|
6400
|
+
...seat.viewMeta?.sourceHeight !== void 0 ? { sourceHeight: seat.viewMeta.sourceHeight } : {},
|
|
6401
|
+
...seat.viewMeta?.previewWidth !== void 0 ? { previewWidth: seat.viewMeta.previewWidth } : {},
|
|
6402
|
+
...seat.viewMeta?.previewHeight !== void 0 ? { previewHeight: seat.viewMeta.previewHeight } : {},
|
|
6403
|
+
...seat.viewMeta?.initialBearingDeg !== void 0 ? { initialBearingDeg: seat.viewMeta.initialBearingDeg } : {},
|
|
6404
|
+
...seat.viewMeta?.initialPitchDeg !== void 0 ? { initialPitchDeg: seat.viewMeta.initialPitchDeg } : {},
|
|
6405
|
+
...seat.viewMeta?.coverage ? { coverage: seat.viewMeta.coverage } : {},
|
|
6406
|
+
...seat.viewMeta?.capturedAt ? { capturedAt: seat.viewMeta.capturedAt } : {},
|
|
6407
|
+
...seat.viewMeta?.sourceLabel ? { sourceLabel: seat.viewMeta.sourceLabel } : {}
|
|
6408
|
+
};
|
|
6409
|
+
} catch (error) {
|
|
6410
|
+
this.opts.onError?.(error);
|
|
6411
|
+
return null;
|
|
6412
|
+
}
|
|
6413
|
+
}
|
|
6247
6414
|
const doc = this.controller.doc;
|
|
6248
6415
|
if (!doc) return null;
|
|
6249
6416
|
const activeId = this.controller.getActiveFloorId();
|
|
@@ -6826,6 +6993,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6826
6993
|
this.closeSeatView();
|
|
6827
6994
|
this.closeCheckoutPanel();
|
|
6828
6995
|
this.exit3d();
|
|
6996
|
+
this.buyerAssetUrls.dispose();
|
|
6829
6997
|
this.stopHoldTimer();
|
|
6830
6998
|
if (this.toastTimer) clearTimeout(this.toastTimer);
|
|
6831
6999
|
if (this.liveTimer) clearTimeout(this.liveTimer);
|