@tiledev/sdk-apptile-live-selling 0.12.1 → 0.14.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.
Files changed (138) hide show
  1. package/README.md +116 -19
  2. package/dist/giveawayPrizeLines.d.ts +10 -0
  3. package/dist/giveawayPrizeLines.d.ts.map +1 -1
  4. package/dist/giveawayPrizeLines.js +11 -1
  5. package/dist/giveawayPrizeLines.js.map +1 -1
  6. package/dist/heldComments.d.ts +13 -2
  7. package/dist/heldComments.d.ts.map +1 -1
  8. package/dist/heldComments.js +16 -2
  9. package/dist/heldComments.js.map +1 -1
  10. package/dist/hiddenShows.d.ts +10 -0
  11. package/dist/hiddenShows.d.ts.map +1 -1
  12. package/dist/hiddenShows.js +11 -0
  13. package/dist/hiddenShows.js.map +1 -1
  14. package/dist/index.d.ts +9 -4
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +35 -2
  17. package/dist/index.js.map +1 -1
  18. package/dist/livePages.d.ts +79 -0
  19. package/dist/livePages.d.ts.map +1 -0
  20. package/dist/livePages.js +90 -0
  21. package/dist/livePages.js.map +1 -0
  22. package/dist/liveRoom.d.ts +21 -0
  23. package/dist/liveRoom.d.ts.map +1 -0
  24. package/dist/liveRoom.js +28 -0
  25. package/dist/liveRoom.js.map +1 -0
  26. package/dist/pageOptions.d.ts +100 -0
  27. package/dist/pageOptions.d.ts.map +1 -0
  28. package/dist/pageOptions.js +82 -0
  29. package/dist/pageOptions.js.map +1 -0
  30. package/dist/pageTypes.d.ts +127 -0
  31. package/dist/pageTypes.d.ts.map +1 -0
  32. package/dist/pageTypes.js +3 -0
  33. package/dist/pageTypes.js.map +1 -0
  34. package/dist/react/LiveSellingProvider.d.ts +23 -1
  35. package/dist/react/LiveSellingProvider.d.ts.map +1 -1
  36. package/dist/react/LiveSellingProvider.js +26 -2
  37. package/dist/react/LiveSellingProvider.js.map +1 -1
  38. package/dist/react/index.d.ts +10 -0
  39. package/dist/react/index.d.ts.map +1 -1
  40. package/dist/react/index.js +29 -1
  41. package/dist/react/index.js.map +1 -1
  42. package/dist/react/index.web.d.ts +11 -32
  43. package/dist/react/index.web.d.ts.map +1 -1
  44. package/dist/react/index.web.js +46 -139
  45. package/dist/react/index.web.js.map +1 -1
  46. package/dist/react/navigation.d.ts +13 -0
  47. package/dist/react/navigation.d.ts.map +1 -0
  48. package/dist/react/navigation.js +7 -0
  49. package/dist/react/navigation.js.map +1 -0
  50. package/dist/react/platformSession.d.ts +16 -0
  51. package/dist/react/platformSession.d.ts.map +1 -0
  52. package/dist/react/platformSession.js +28 -0
  53. package/dist/react/platformSession.js.map +1 -0
  54. package/dist/react/platformSession.web.d.ts +35 -0
  55. package/dist/react/platformSession.web.d.ts.map +1 -0
  56. package/dist/react/platformSession.web.js +148 -0
  57. package/dist/react/platformSession.web.js.map +1 -0
  58. package/dist/react/replayPlayer.d.ts +74 -0
  59. package/dist/react/replayPlayer.d.ts.map +1 -0
  60. package/dist/react/replayPlayer.js +279 -0
  61. package/dist/react/replayPlayer.js.map +1 -0
  62. package/dist/react/shopifyProviders.d.ts +68 -0
  63. package/dist/react/shopifyProviders.d.ts.map +1 -0
  64. package/dist/react/shopifyProviders.js +98 -0
  65. package/dist/react/shopifyProviders.js.map +1 -0
  66. package/dist/react/showPages.d.ts +5 -1
  67. package/dist/react/showPages.d.ts.map +1 -1
  68. package/dist/react/showPages.js +14 -5
  69. package/dist/react/showPages.js.map +1 -1
  70. package/dist/react/useKeepScreenAwake.d.ts +2 -0
  71. package/dist/react/useKeepScreenAwake.d.ts.map +1 -0
  72. package/dist/react/useKeepScreenAwake.js +34 -0
  73. package/dist/react/useKeepScreenAwake.js.map +1 -0
  74. package/dist/react/useLiveAuction.d.ts +104 -0
  75. package/dist/react/useLiveAuction.d.ts.map +1 -0
  76. package/dist/react/useLiveAuction.js +244 -0
  77. package/dist/react/useLiveAuction.js.map +1 -0
  78. package/dist/react/useLiveChat.d.ts +75 -0
  79. package/dist/react/useLiveChat.d.ts.map +1 -0
  80. package/dist/react/useLiveChat.js +131 -0
  81. package/dist/react/useLiveChat.js.map +1 -0
  82. package/dist/react/useLiveGiveawaysAndPolls.d.ts +84 -0
  83. package/dist/react/useLiveGiveawaysAndPolls.d.ts.map +1 -0
  84. package/dist/react/useLiveGiveawaysAndPolls.js +180 -0
  85. package/dist/react/useLiveGiveawaysAndPolls.js.map +1 -0
  86. package/dist/react/useLivePage.d.ts +113 -0
  87. package/dist/react/useLivePage.d.ts.map +1 -0
  88. package/dist/react/useLivePage.js +264 -0
  89. package/dist/react/useLivePage.js.map +1 -0
  90. package/dist/react/useLiveSession.d.ts +8 -0
  91. package/dist/react/useLiveSession.d.ts.map +1 -1
  92. package/dist/react/useLiveSession.js +43 -27
  93. package/dist/react/useLiveSession.js.map +1 -1
  94. package/dist/react/useLiveShop.d.ts +83 -0
  95. package/dist/react/useLiveShop.d.ts.map +1 -0
  96. package/dist/react/useLiveShop.js +165 -0
  97. package/dist/react/useLiveShop.js.map +1 -0
  98. package/dist/react/useReplayPage.d.ts +117 -0
  99. package/dist/react/useReplayPage.d.ts.map +1 -0
  100. package/dist/react/useReplayPage.js +504 -0
  101. package/dist/react/useReplayPage.js.map +1 -0
  102. package/dist/sessionTypes.d.ts +17 -9
  103. package/dist/sessionTypes.d.ts.map +1 -1
  104. package/dist/sessionTypes.js.map +1 -1
  105. package/dist/shopifyIdentity.d.ts +7 -4
  106. package/dist/shopifyIdentity.d.ts.map +1 -1
  107. package/dist/shopifyIdentity.js +11 -5
  108. package/dist/shopifyIdentity.js.map +1 -1
  109. package/dist/showCredit.d.ts +87 -0
  110. package/dist/showCredit.d.ts.map +1 -0
  111. package/dist/showCredit.js +120 -0
  112. package/dist/showCredit.js.map +1 -0
  113. package/dist/showNames.d.ts +24 -3
  114. package/dist/showNames.d.ts.map +1 -1
  115. package/dist/showNames.js +57 -10
  116. package/dist/showNames.js.map +1 -1
  117. package/dist/showPages.d.ts +12 -0
  118. package/dist/showPages.d.ts.map +1 -1
  119. package/dist/showPages.js +10 -0
  120. package/dist/showPages.js.map +1 -1
  121. package/dist/ui/BubbleFrame.d.ts +38 -0
  122. package/dist/ui/BubbleFrame.d.ts.map +1 -0
  123. package/dist/ui/BubbleFrame.js +181 -0
  124. package/dist/ui/BubbleFrame.js.map +1 -0
  125. package/dist/ui/LiveBubble.d.ts +37 -0
  126. package/dist/ui/LiveBubble.d.ts.map +1 -0
  127. package/dist/ui/LiveBubble.js +101 -0
  128. package/dist/ui/LiveBubble.js.map +1 -0
  129. package/dist/ui/ReplayBubble.d.ts +23 -0
  130. package/dist/ui/ReplayBubble.d.ts.map +1 -0
  131. package/dist/ui/ReplayBubble.js +65 -0
  132. package/dist/ui/ReplayBubble.js.map +1 -0
  133. package/dist/ui.d.ts +27 -0
  134. package/dist/ui.d.ts.map +1 -0
  135. package/dist/ui.js +34 -0
  136. package/dist/ui.js.map +1 -0
  137. package/package.json +36 -5
  138. package/ui/package.json +6 -0
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useLivePage = useLivePage;
4
+ /**
5
+ * The Live screen's page model (0.14.0, moved from amore-v2's `src/core/hooks/useLivePage.ts`:
6
+ * Freckled Poppy's Live, phase 1, watching and the in-app bubble). The routes are the app's
7
+ * (`LiveSellingProvider`'s `routes`); the route's `streamId` param is read here.
8
+ */
9
+ const sdk_analytics_core_1 = require("@tiledev/sdk-analytics-core");
10
+ const sdk_shopify_1 = require("@tiledev/sdk-shopify");
11
+ const react_1 = require("react");
12
+ const react_native_1 = require("react-native");
13
+ const client_1 = require("../client");
14
+ const hiddenShows_1 = require("../hiddenShows");
15
+ const liveSelling_1 = require("../liveSelling");
16
+ const livePages_1 = require("../livePages");
17
+ const pageOptions_1 = require("../pageOptions");
18
+ const sessionTypes_1 = require("../sessionTypes");
19
+ const LiveSellingProvider_1 = require("./LiveSellingProvider");
20
+ const platformSession_1 = require("./platformSession");
21
+ const replayPlayer_1 = require("./replayPlayer");
22
+ const useKeepScreenAwake_1 = require("./useKeepScreenAwake");
23
+ const useLiveChat_1 = require("./useLiveChat");
24
+ const useStreamInfo_1 = require("./useStreamInfo");
25
+ const navigation_1 = require("./navigation");
26
+ /**
27
+ * Shows already reported as joined (`liveJoin`) in this run of the app: once per show per run, so a
28
+ * trip through the bubble, the Cart or another screen and back isn't a second join (amore-v2's audit,
29
+ * L5: production reported on every viewer-count tick).
30
+ */
31
+ const joinedThisRun = new Set();
32
+ /**
33
+ * Everything the Live screen wires, so the screen stays plain tiles. Call it from the Live screen and
34
+ * from each of its tiles' wiring: every call reads the same session.
35
+ *
36
+ * - **The session is the app's** (`ShopifyLiveSessionProvider`), joined above the navigator; this
37
+ * screen binds to its picture (the player tile's `LiveVideoTarget`) and never owns the room.
38
+ * - **Pinned to the route's show** while watching (`holdStream(streamId)`, at every arrival), so a host
39
+ * restarting the broadcast can't swap the room underneath; released when the shopper closes the show
40
+ * (back on a show that has ended or is still joining, ✕ on the bubble, or leaving any other way
41
+ * without the bubble), and kept when the bubble takes the show.
42
+ * - **Which show** (`useLiveShow`): the session's record once it is this show's (just after the pin it
43
+ * can still hold the last show's), where it stands, and whether it is on air.
44
+ * - **Where the link lands** (`streams.destinationFor`, one read of the show's record on arrival): a
45
+ * show on air stays; an ended show with a recording is replaced by its Replay
46
+ * (`routes.replaceWithReplay`); anything else (a rehearsal this shopper can't see included) by the
47
+ * Live tab (`routes.backToLiveTab`). Not on the web, where nothing can play: the Tile preview stays on
48
+ * this page to show its tiles, with the record's poster and the ended words.
49
+ * - **Every arrival** reads the record again (`refreshStreamRecords`, which restarts the session's
50
+ * off-air wait, so a push tapped in the foreground can't be declared over on a stale record) and ends
51
+ * the bubble: this screen shows the picture again.
52
+ * - **Back, every kind** (the header's arrow, the iOS swipe, Android's back; decided 2026-10-05,
53
+ * amore-v2's Head of Engineering) shrinks a show that is playing or waiting for its host into the
54
+ * bubble as the screen leaves, and closes one that has ended. One path: the screen's `beforeRemove`,
55
+ * for the navigation's back actions (`BACK_ACTIONS`). Picture-in-picture is a back that always
56
+ * shrinks; the cart shrinks the show and opens the Cart over this screen (Items You Won on an auction
57
+ * show: `routes.openCart`). Android's back with nothing behind this screen (a link on a cold start)
58
+ * does what the arrow does: back to the Live tab.
59
+ * - **One shrunk show at a time:** arriving here stops a replay's bubble (and the Replay screen stops
60
+ * this show's bubble).
61
+ * - **The screen stays on** while it shows the show playing or waiting for its host.
62
+ * - **Under a sheet** (`routes.sheets`: the variant page, opened from the shop; the Sign In page) the
63
+ * show plays on here, with the screen on and the light status bar, as Replay's does.
64
+ * - **Put away** while the show plays here (a sheet over it included), it goes on in the phone's own
65
+ * floating window (`systemWindowWhenAway`); coming back finds it here again.
66
+ * - **`liveJoin`** (`track`) once per show per app run; never for a rehearsal, a show the config's
67
+ * `hiddenShows` names, whoever watches it.
68
+ * - **Hidden shows** (`hiddenShows`): for a shopper who isn't on the team the session never holds one,
69
+ * so it is never on air here (the link goes to the Live tab).
70
+ * - **Chat and reactions** are `useLiveChat`'s, shown only while the show is on air.
71
+ */
72
+ function useLivePage() {
73
+ var _a, _b, _c, _d;
74
+ const nav = (0, navigation_1.useNavigation)();
75
+ const route = (0, navigation_1.useRoute)();
76
+ const streamId = (_b = (_a = route.params) === null || _a === void 0 ? void 0 : _a.streamId) !== null && _b !== void 0 ? _b : '';
77
+ const isFocused = (0, navigation_1.useIsFocused)();
78
+ const { routes, track } = (0, LiveSellingProvider_1.useLivePageOptions)();
79
+ // The session changes only on the room's events, never on a clock: the header and the auction tiles keep their own.
80
+ const session = (0, platformSession_1.useLiveSession)();
81
+ const { holdStream, releaseStream, enterPip, exitPip, setMuted } = session.actions;
82
+ const { viewerEmail: email } = (0, LiveSellingProvider_1.useLiveSelling)();
83
+ const { show: sessionShow, status, onAir } = (0, platformSession_1.useLiveShow)(streamId);
84
+ const { itemCount } = (0, sdk_shopify_1.useCart)();
85
+ const { bubbleShown: replayInBubble, stop: stopReplay } = (0, replayPlayer_1.useReplayPlayer)();
86
+ // ─── Which show ────────────────────────────────────────────────────────────────────────────────
87
+ // `sessionShow` (above) is the session's record once it is this show's. The record the link's answer
88
+ // was read from stands in for its poster until then, and on the web.
89
+ const [linkedShow, setLinkedShow] = (0, react_1.useState)(null);
90
+ const show = sessionShow !== null && sessionShow !== void 0 ? sessionShow : (linkedShow && linkedShow.streamingId === streamId ? linkedShow : null);
91
+ /** The screen is leaving for good (closed, or sent on by the link's answer): going doesn't shrink the show. */
92
+ const leavingForGood = (0, react_1.useRef)(false);
93
+ // ─── Where the link lands ──────────────────────────────────────────────────────────────────────
94
+ (0, react_1.useEffect)(() => {
95
+ if (!streamId)
96
+ return;
97
+ let stillHere = true;
98
+ void liveSelling_1.liveSelling.streams.destinationFor(streamId, { viewerEmail: email }).then(({ destination, show: record, readError }) => {
99
+ if (readError)
100
+ (0, pageOptions_1.reportLiveWarning)(readError, { at: 'live.destinationFor', streamId });
101
+ if (!stillHere)
102
+ return;
103
+ setLinkedShow(record);
104
+ if (react_native_1.Platform.OS === 'web' || destination.to === 'livePlayer')
105
+ return;
106
+ leavingForGood.current = true;
107
+ if (destination.to === 'replay')
108
+ routes.replaceWithReplay(destination.feedId, nav);
109
+ // Back to the tabs already in the stack, or in this screen's place when it opened alone (a link on a cold start).
110
+ else
111
+ routes.backToLiveTab(nav);
112
+ });
113
+ return () => {
114
+ stillHere = false;
115
+ };
116
+ }, [streamId, email, nav, routes]);
117
+ // ─── Arriving ──────────────────────────────────────────────────────────────────────────────────
118
+ (0, navigation_1.useFocusEffect)((0, react_1.useCallback)(() => {
119
+ holdStream(streamId || undefined);
120
+ (0, useStreamInfo_1.refreshStreamRecords)();
121
+ exitPip();
122
+ }, [streamId, holdStream, exitPip]));
123
+ (0, react_1.useEffect)(() => {
124
+ if (isFocused && replayInBubble)
125
+ stopReplay();
126
+ }, [isFocused, replayInBubble, stopReplay]);
127
+ // ─── Leaving ───────────────────────────────────────────────────────────────────────────────────
128
+ /** The show went into the bubble on the way out: leaving keeps it playing there, pinned. */
129
+ const shrunk = (0, react_1.useRef)(false);
130
+ (0, react_1.useEffect)(() => {
131
+ if (!isFocused)
132
+ return;
133
+ shrunk.current = false;
134
+ leavingForGood.current = false;
135
+ }, [isFocused]);
136
+ const pipModeNow = (0, react_1.useRef)(session.pipMode);
137
+ pipModeNow.current = session.pipMode;
138
+ // Gone without the bubble (back on an ended show, replaced by the Replay or the Live tab): the show
139
+ // stops and the pin goes. Not when the bubble took it.
140
+ (0, react_1.useEffect)(() => () => {
141
+ if (shrunk.current || pipModeNow.current !== 'none')
142
+ return;
143
+ exitPip({ stopVideo: true });
144
+ releaseStream();
145
+ }, [exitPip, releaseStream]);
146
+ // ─── Where the show stands ─────────────────────────────────────────────────────────────────────
147
+ // `status` and `onAir` are `useLiveShow`'s: joining until the session holds this show's record. The
148
+ // controls follow the link's record until then (an auction's cart is Items You Won from the start).
149
+ const capabilities = (0, sessionTypes_1.capabilitiesFor)(show);
150
+ const canShrink = capabilities.canPip && onAir;
151
+ // On show, or covered only by a page drawn as a sheet (the variant page, from the shop): the picture
152
+ // stays here, playing behind it, and the screen stays on.
153
+ const coveredOnlyBySheet = (0, navigation_1.useNavigationState)((state) => (0, livePages_1.isCoveredOnlyBySheet)(state, route.key, routes.sheets));
154
+ const onScreen = isFocused || coveredOnlyBySheet;
155
+ const showing = onScreen && session.pipMode === 'none';
156
+ // Chat and reactions: only while this show is on air. Its own hook (`useLiveChat`).
157
+ const chat = (0, useLiveChat_1.useLiveChat)({ onAir, canComment: capabilities.canComment, canReact: capabilities.canReact });
158
+ // Every back press (`BACK_ACTIONS`) passes here as the screen goes: a show on air enters the bubble,
159
+ // then the screen is let go. Nothing is prevented.
160
+ const canShrinkNow = (0, react_1.useRef)(canShrink);
161
+ canShrinkNow.current = canShrink;
162
+ (0, react_1.useEffect)(() => nav.addListener('beforeRemove', (event) => {
163
+ if (!livePages_1.BACK_ACTIONS.has(event.data.action.type))
164
+ return;
165
+ if (leavingForGood.current || !canShrinkNow.current || pipModeNow.current !== 'none')
166
+ return;
167
+ shrunk.current = true;
168
+ enterPip();
169
+ }), [nav, enterPip]);
170
+ (0, useKeepScreenAwake_1.useKeepScreenAwake)(showing && onAir, 'live');
171
+ // ─── liveJoin, once per show per app run ───────────────────────────────────────────────────────
172
+ const viewerCount = session.viewerCount;
173
+ (0, react_1.useEffect)(() => {
174
+ if (!isFocused || !sessionShow || sessionShow.status !== 'live' || (0, hiddenShows_1.isHiddenShow)(sessionShow, (0, client_1.hiddenShowsRule)()))
175
+ return;
176
+ if (joinedThisRun.has(sessionShow.streamingId))
177
+ return;
178
+ joinedThisRun.add(sessionShow.streamingId);
179
+ track(sdk_analytics_core_1.DefaultEvents.LIVE_JOIN, (0, sdk_analytics_core_1.liveJoinParams)(sessionShow.streamingId, viewerCount));
180
+ }, [isFocused, sessionShow, viewerCount, track]);
181
+ // ─── The header's buttons ──────────────────────────────────────────────────────────────────────
182
+ /** Back: to the screen behind, or to the Live tab when this one opened alone. A show on air shrinks on the way (above). */
183
+ const goBack = (0, react_1.useCallback)(() => {
184
+ if (nav.canGoBack())
185
+ nav.goBack();
186
+ else
187
+ routes.backToLiveTab(nav);
188
+ }, [nav, routes]);
189
+ const close = (0, react_1.useCallback)(() => {
190
+ leavingForGood.current = true;
191
+ // Stopped outright: without `stopVideo` the room plays on with no surface.
192
+ exitPip({ stopVideo: true });
193
+ releaseStream();
194
+ goBack();
195
+ }, [exitPip, releaseStream, goBack]);
196
+ const back = canShrink ? goBack : close;
197
+ // Android's back with nothing behind this screen (a link on a cold start) would close the app: it does
198
+ // what the arrow does instead. With a screen behind, the navigator goes back, through `beforeRemove`.
199
+ const backNow = (0, react_1.useRef)(back);
200
+ backNow.current = back;
201
+ (0, navigation_1.useFocusEffect)((0, react_1.useCallback)(() => {
202
+ const subscription = react_native_1.BackHandler.addEventListener('hardwareBackPress', () => {
203
+ if (nav.canGoBack())
204
+ return false;
205
+ backNow.current();
206
+ return true;
207
+ });
208
+ return () => subscription.remove();
209
+ }, [nav]));
210
+ const isAuction = capabilities.canBid;
211
+ const openCart = (0, react_1.useCallback)(() => {
212
+ if (canShrink) {
213
+ shrunk.current = true;
214
+ enterPip();
215
+ }
216
+ // A bidder's cart is what they've won: their bids never land in the shopping cart (Freckled Poppy's).
217
+ routes.openCart({ itemsYouWon: isAuction }, nav);
218
+ }, [canShrink, enterPip, isAuction, nav, routes]);
219
+ // One surface at a time: not while the bubble or the phone's own window has it, or another screen is over this one.
220
+ const videoActive = showing && !!sessionShow;
221
+ return {
222
+ /**
223
+ * Android's own picture-in-picture window is up. It shrinks the whole app, and every sheet on the
224
+ * Live screen (poll, giveaway, shop, custom bid, name, reactions) is a window of its own above the
225
+ * app, so it rode the shrink and covered the video (fixed 2026-10-08; Freckled Poppy hides its
226
+ * sheets the same way). The screen then draws only the player; the sheets' open state is the
227
+ * hooks', so they come back. iOS's window shows the video alone.
228
+ */
229
+ inSystemWindow: react_native_1.Platform.OS === 'android' && session.pipMode === 'system',
230
+ /** The screen is on show (or under a sheet), over the dark video: light status bar text. */
231
+ lightStatusBar: onScreen,
232
+ player: {
233
+ status,
234
+ posterUrl: (_c = show === null || show === void 0 ? void 0 : show.thumbnailUrl) !== null && _c !== void 0 ? _c : null,
235
+ isAuction: !!(show === null || show === void 0 ? void 0 : show.isAuction),
236
+ videoActive,
237
+ // Put away while the show plays here, it goes on in the phone's own floating window. Not while
238
+ // joining, waiting for the host or ended: there is no picture.
239
+ systemWindowWhenAway: videoActive && status === 'playing' && capabilities.canPip,
240
+ },
241
+ header: {
242
+ ended: status === 'ended',
243
+ // An auction show: the header's own LIVE words for one (sparkl-v2's and k-marie-v2's LiveHeader01 `auctionLiveLabel`).
244
+ isAuction: !!(show === null || show === void 0 ? void 0 : show.isAuction),
245
+ // The header counts its clock up from it on its own (`useShowClock`), so the screen isn't drawn each second.
246
+ startedAt: (_d = sessionShow === null || sessionShow === void 0 ? void 0 : sessionShow.startedAtMs) !== null && _d !== void 0 ? _d : null,
247
+ // Known once the room has said: 0 means it hasn't yet.
248
+ viewerCount: onAir && capabilities.showViewerCount && viewerCount > 0 ? viewerCount : null,
249
+ muted: session.muted,
250
+ canMute: status !== 'ended' && status !== 'error',
251
+ canPip: canShrink,
252
+ backShrinks: canShrink,
253
+ cartCount: itemCount,
254
+ onBack: back,
255
+ onToggleMute: () => setMuted(!session.muted),
256
+ // Shown only while the show can shrink: a back press, which shrinks it on the way.
257
+ onPip: goBack,
258
+ onCart: openCart,
259
+ },
260
+ /** The chat, the comment box, reactions and the pinned comment: one set of props per tile (`useLiveChat`). */
261
+ chat,
262
+ };
263
+ }
264
+ //# sourceMappingURL=useLivePage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLivePage.js","sourceRoot":"","sources":["../../src/react/useLivePage.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,14 @@ export interface LiveSessionProviderProps {
16
16
  streamingId?: string;
17
17
  /** Shown next to this viewer's own comments and sent with them. Overrides the identity adapter. */
18
18
  viewerName?: string | null;
19
+ /**
20
+ * Names this viewer may never be shown under, as the show's team uses them (0.13.0): compared whole,
21
+ * with capitals, accents, spaces and punctuation left out (`isBlockedViewerName`). A blocked name,
22
+ * whether the account's, the identity's or one typed on this phone, counts as no name
23
+ * (`viewerName` null, so the app asks), and `chooseViewerName` refuses one. Defaults to
24
+ * `DEFAULT_BLOCKED_VIEWER_NAMES` (Host, Moderator); an empty list blocks none.
25
+ */
26
+ blockedNames?: readonly string[];
19
27
  /**
20
28
  * Called when the system PIP window gives the picture back to a full-screen player: the user expanded
21
29
  * a window that the player opened (or, on iOS, came back to the app with it still up). Navigate to
@@ -1 +1 @@
1
- {"version":3,"file":"useLiveSession.d.ts","sourceRoot":"","sources":["../../src/react/useLiveSession.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AA6CvC,OAAO,KAAK,EASV,WAAW,EAEZ,MAAM,iBAAiB,CAAC;AAwCzB;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,GAAG,OAAO,KAAK,WAAW,CAAC,IAAI,CAAC,CAIlE;AAED,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mGAAmG;IACnG,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACzD,QAAQ,EAAE,SAAS,CAAC;CACrB;AAugDD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,wBAAwB,+FA0CnF"}
1
+ {"version":3,"file":"useLiveSession.d.ts","sourceRoot":"","sources":["../../src/react/useLiveSession.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AA6CvC,OAAO,KAAK,EASV,WAAW,EAEZ,MAAM,iBAAiB,CAAC;AAwCzB;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,GAAG,OAAO,KAAK,WAAW,CAAC,IAAI,CAAC,CAIlE;AAED,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mGAAmG;IACnG,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACzD,QAAQ,EAAE,SAAS,CAAC;CACrB;AAshDD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,wBAAwB,+FA0CnF"}
@@ -89,8 +89,16 @@ function useLiveSession() {
89
89
  /* Everything but the stream hold, which belongs to the provider component: it owns which stream this
90
90
  hook is watching, so it is the only thing that can pin it. */
91
91
  function useSessionValue(props) {
92
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
92
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
93
93
  const { streamingId, viewerName: viewerNameInput, onPipRestore } = props;
94
+ const blockedNames = (_a = props.blockedNames) !== null && _a !== void 0 ? _a : showNames_1.DEFAULT_BLOCKED_VIEWER_NAMES;
95
+ const blockedNamesRef = (0, react_1.useRef)(blockedNames);
96
+ blockedNamesRef.current = blockedNames;
97
+ /** A name the room may show for this viewer: made safe (`publicName`) and not one of the team's. */
98
+ const allowedName = (name) => {
99
+ const shown = (0, showNames_1.publicName)(name);
100
+ return shown && !(0, showNames_1.isBlockedViewerName)(shown, blockedNames) ? shown : null;
101
+ };
94
102
  const [state, dispatch] = (0, react_1.useReducer)(session_1.sessionReducer, session_1.initialSessionState);
95
103
  const [joining, setJoining] = (0, react_1.useState)(false);
96
104
  const [error, setError] = (0, react_1.useState)(null);
@@ -138,8 +146,9 @@ function useSessionValue(props) {
138
146
  * name is consulted at all so a signed-in bidder is never asked to invent one.
139
147
  */
140
148
  const [chosenName, setChosenName] = (0, react_1.useState)(() => liveSelling_1.liveSelling.viewer.cachedName());
141
- // Every name here is published to the whole room, so none may be an email (`publicName`, 0.9.0).
142
- const accountName = (0, showNames_1.publicName)(viewerNameInput);
149
+ // Every name here is published to the whole room, so none may be an email (`publicName`, 0.9.0), nor
150
+ // a name the show's team goes by (`blockedNames`, 0.13.0).
151
+ const accountName = allowedName(viewerNameInput);
143
152
  (0, react_1.useEffect)(() => {
144
153
  if (accountName || chosenName)
145
154
  return;
@@ -152,22 +161,22 @@ function useSessionValue(props) {
152
161
  alive = false;
153
162
  };
154
163
  }, [accountName, chosenName]);
155
- const customerName = (0, showNames_1.publicName)((_a = identity === null || identity === void 0 ? void 0 : identity.customerName) === null || _a === void 0 ? void 0 : _a.call(identity));
156
- const namedBy = (_b = accountName !== null && accountName !== void 0 ? accountName : (0, showNames_1.publicName)(chosenName)) !== null && _b !== void 0 ? _b : customerName;
164
+ const customerName = allowedName((_b = identity === null || identity === void 0 ? void 0 : identity.customerName) === null || _b === void 0 ? void 0 : _b.call(identity));
165
+ const namedBy = (_c = accountName !== null && accountName !== void 0 ? accountName : allowedName(chosenName)) !== null && _c !== void 0 ? _c : customerName;
157
166
  const viewerName = namedBy !== null && namedBy !== void 0 ? namedBy : 'Anonymous';
158
- const bidderId = (_d = (_c = identity === null || identity === void 0 ? void 0 : identity.bidderId) === null || _c === void 0 ? void 0 : _c.call(identity)) !== null && _d !== void 0 ? _d : null;
167
+ const bidderId = (_e = (_d = identity === null || identity === void 0 ? void 0 : identity.bidderId) === null || _d === void 0 ? void 0 : _d.call(identity)) !== null && _e !== void 0 ? _e : null;
159
168
  /**
160
- * A signed-in shopper whose name has not loaded yet. They are never asked for one — the session
161
- * uses the customer's own — so a comment sent now is held until it lands (L6).
169
+ * The signed-in customer is still loading (app open, or just signed in), so their name isn't known
170
+ * yet. The room waits for it, up to `NAME_WAIT_MS`: it is entered once, and the name it is entered
171
+ * under is the one the host sees join. Before 0.9.2 an app opened during a show joined at once,
172
+ * before the sign-in was read, and every shopper joined as "Anonymous" (2026-10-08).
162
173
  */
163
- const nameResolving = !namedBy && !!bidderId;
174
+ const customerLoading = (_g = (_f = identity === null || identity === void 0 ? void 0 : identity.customerLoading) === null || _f === void 0 ? void 0 : _f.call(identity)) !== null && _g !== void 0 ? _g : false;
164
175
  /**
165
- * The signed-in customer is still loading (app open), so their name isn't known yet. The room
166
- * waits for it, up to `NAME_WAIT_MS`: it is entered once, and the name it is entered under is the
167
- * one the host sees join. Before 0.9.2 an app opened during a show joined at once, before the
168
- * sign-in was read, and every shopper joined as "Anonymous" (2026-10-08).
176
+ * A comment sent now is held until the account has named the shopper (L6), and only while it loads:
177
+ * an account read with no name on it isn't waited for (`waitsForAccountName`, 0.13.0).
169
178
  */
170
- const customerLoading = (_f = (_e = identity === null || identity === void 0 ? void 0 : identity.customerLoading) === null || _e === void 0 ? void 0 : _e.call(identity)) !== null && _f !== void 0 ? _f : false;
179
+ const nameResolving = (0, heldComments_1.waitsForAccountName)(customerLoading, accountName !== null && accountName !== void 0 ? accountName : customerName);
171
180
  const [nameWaitOver, setNameWaitOver] = (0, react_1.useState)(false);
172
181
  (0, react_1.useEffect)(() => {
173
182
  if (!customerLoading)
@@ -223,14 +232,18 @@ function useSessionValue(props) {
223
232
  // A comment held for the last show's room must not be said in this one.
224
233
  (_a = heldCommentsRef.current) === null || _a === void 0 ? void 0 : _a.drop();
225
234
  }, [watching]);
226
- /** The name arrived: say it to the engine first, then send what was waiting for it, in order. */
235
+ /**
236
+ * The account has loaded: say the name to the engine first, then send what was waiting for it, in
237
+ * order. Loaded with no name, they go under the session's name (one typed on this phone, or
238
+ * "Anonymous"), as they would after the longest wait: an app asks for a name before sending.
239
+ */
227
240
  (0, react_1.useEffect)(() => {
228
241
  var _a;
229
- if (!namedBy || !((_a = heldCommentsRef.current) === null || _a === void 0 ? void 0 : _a.size))
242
+ if (nameResolving || !((_a = heldCommentsRef.current) === null || _a === void 0 ? void 0 : _a.size))
230
243
  return;
231
244
  liveSelling_1.liveSelling.engine.setViewerName(namedBy);
232
245
  void heldCommentsRef.current.release();
233
- }, [namedBy]);
246
+ }, [namedBy, nameResolving]);
234
247
  (0, react_1.useEffect)(() => liveSelling_1.liveSelling.engine.subscribe((event) => {
235
248
  dispatch({ kind: 'event', event, at: Date.now() });
236
249
  }), []);
@@ -305,7 +318,7 @@ function useSessionValue(props) {
305
318
  * importantly an *unmute*, which is otherwise invisible until the app restarts. Keyed on the joined
306
319
  * ids because the array is a fresh object on every poll.
307
320
  */
308
- const mutedKey = ((_g = stream === null || stream === void 0 ? void 0 : stream.mutedDeviceIds) !== null && _g !== void 0 ? _g : []).join(',');
321
+ const mutedKey = ((_h = stream === null || stream === void 0 ? void 0 : stream.mutedDeviceIds) !== null && _h !== void 0 ? _h : []).join(',');
309
322
  (0, react_1.useEffect)(() => {
310
323
  if (!stream)
311
324
  return;
@@ -328,7 +341,7 @@ function useSessionValue(props) {
328
341
  */
329
342
  const nothingOnAir = (0, session_1.isNothingOnAir)({
330
343
  hasRead,
331
- streamStatus: (_h = stream === null || stream === void 0 ? void 0 : stream.status) !== null && _h !== void 0 ? _h : null,
344
+ streamStatus: (_j = stream === null || stream === void 0 ? void 0 : stream.status) !== null && _j !== void 0 ? _j : null,
332
345
  status: derivedStatus,
333
346
  });
334
347
  const graceMs = (0, client_1.offAirGraceMs)();
@@ -602,7 +615,7 @@ function useSessionValue(props) {
602
615
  * enter a giveaway they are already in.
603
616
  */
604
617
  const activeGiveaway = (0, react_1.useMemo)(() => { var _a; return (_a = stream === null || stream === void 0 ? void 0 : stream.giveaways.find((giveaway) => giveaway.status === 'active')) !== null && _a !== void 0 ? _a : null; }, [stream]);
605
- const customerRef = (_k = (_j = identity === null || identity === void 0 ? void 0 : identity.customerRef) === null || _j === void 0 ? void 0 : _j.call(identity)) !== null && _k !== void 0 ? _k : null;
618
+ const customerRef = (_l = (_k = identity === null || identity === void 0 ? void 0 : identity.customerRef) === null || _k === void 0 ? void 0 : _k.call(identity)) !== null && _l !== void 0 ? _l : null;
606
619
  /**
607
620
  * Closes the gap between the realtime giveaway start and the polled record it depends on.
608
621
  *
@@ -782,8 +795,8 @@ function useSessionValue(props) {
782
795
  * the first record after it is taken whatever it says. Joining counts as a reconnect, so arriving
783
796
  * costs one extra read.
784
797
  */
785
- const recordPin = (_l = stream === null || stream === void 0 ? void 0 : stream.pinnedComment) !== null && _l !== void 0 ? _l : null;
786
- const recordPinKey = `${watching !== null && watching !== void 0 ? watching : ''}\u0000${(_m = recordPin === null || recordPin === void 0 ? void 0 : recordPin.id) !== null && _m !== void 0 ? _m : ''}\u0000${(_o = recordPin === null || recordPin === void 0 ? void 0 : recordPin.text) !== null && _o !== void 0 ? _o : ''}`;
798
+ const recordPin = (_m = stream === null || stream === void 0 ? void 0 : stream.pinnedComment) !== null && _m !== void 0 ? _m : null;
799
+ const recordPinKey = `${watching !== null && watching !== void 0 ? watching : ''}\u0000${(_o = recordPin === null || recordPin === void 0 ? void 0 : recordPin.id) !== null && _o !== void 0 ? _o : ''}\u0000${(_p = recordPin === null || recordPin === void 0 ? void 0 : recordPin.text) !== null && _p !== void 0 ? _p : ''}`;
787
800
  const lastRecordPinKey = (0, react_1.useRef)(null);
788
801
  const takeNextRecordPin = (0, react_1.useRef)(false);
789
802
  (0, react_1.useEffect)(() => {
@@ -845,7 +858,7 @@ function useSessionValue(props) {
845
858
  if (!activeRetryRef.current) {
846
859
  activeRetryRef.current = new activeProductRetry_1.ActiveProductRetry(() => setCatalogueRetry((n) => n + 1));
847
860
  }
848
- const featuredId = (_r = (_p = state.activeStoreProductId) !== null && _p !== void 0 ? _p : (_q = stream === null || stream === void 0 ? void 0 : stream.products.find((product) => product.isCurrent)) === null || _q === void 0 ? void 0 : _q.storeProductId) !== null && _r !== void 0 ? _r : null;
861
+ const featuredId = (_s = (_q = state.activeStoreProductId) !== null && _q !== void 0 ? _q : (_r = stream === null || stream === void 0 ? void 0 : stream.products.find((product) => product.isCurrent)) === null || _r === void 0 ? void 0 : _r.storeProductId) !== null && _s !== void 0 ? _s : null;
849
862
  const featuredCatalogueId = featuredId && productKey.split(',').includes(featuredId) ? featuredId : null;
850
863
  const answerSeq = (catalogueAnswer === null || catalogueAnswer === void 0 ? void 0 : catalogueAnswer.key) === productKey ? catalogueAnswer.seq : null;
851
864
  const hasFeatured = !!featuredCatalogueId &&
@@ -918,10 +931,10 @@ function useSessionValue(props) {
918
931
  return null;
919
932
  return (_f = products[0]) !== null && _f !== void 0 ? _f : null;
920
933
  }, [products, state.activeStoreProductId, stream]);
921
- const multiplier = (_t = (_s = state.multiplierOverride) !== null && _s !== void 0 ? _s : stream === null || stream === void 0 ? void 0 : stream.viewerMultiplier) !== null && _t !== void 0 ? _t : 1;
934
+ const multiplier = (_u = (_t = state.multiplierOverride) !== null && _t !== void 0 ? _t : stream === null || stream === void 0 ? void 0 : stream.viewerMultiplier) !== null && _u !== void 0 ? _u : 1;
922
935
  /* Same precedence as the multiplier: the host's live word wins, the record stands until they
923
936
  give one. Both fall back rather than throw, so a stream row missing `meta` still renders. */
924
- const reactionStyle = (_v = (_u = state.reactionModeOverride) !== null && _u !== void 0 ? _u : stream === null || stream === void 0 ? void 0 : stream.reactionStyle) !== null && _v !== void 0 ? _v : 'single';
937
+ const reactionStyle = (_w = (_v = state.reactionModeOverride) !== null && _v !== void 0 ? _v : stream === null || stream === void 0 ? void 0 : stream.reactionStyle) !== null && _w !== void 0 ? _w : 'single';
925
938
  /**
926
939
  * Percentages come off the raw counts, so the multiplier cannot skew them — it inflates only the
927
940
  * total on display, exactly as the host's own dashboard does.
@@ -999,7 +1012,7 @@ function useSessionValue(props) {
999
1012
  alive = false;
1000
1013
  };
1001
1014
  // eslint-disable-next-line react-hooks/exhaustive-deps
1002
- }, [(_w = state.poll) === null || _w === void 0 ? void 0 : _w.pollUuid]);
1015
+ }, [(_x = state.poll) === null || _x === void 0 ? void 0 : _x.pollUuid]);
1003
1016
  const bindVideoView = (0, react_1.useCallback)((reactTag, force = false) => {
1004
1017
  // Stopped because the app is away: a claim now would start it behind the app, with sound. The
1005
1018
  // surface claims again once the app is back (`LiveVideoTarget`'s foreground re-claim).
@@ -1142,6 +1155,9 @@ function useSessionValue(props) {
1142
1155
  */
1143
1156
  const chooseViewerName = (0, react_1.useCallback)(async (raw) => {
1144
1157
  var _a;
1158
+ // A name the show's team goes by isn't saved (0.13.0): the app says why (`isBlockedViewerName`).
1159
+ if ((0, showNames_1.isBlockedViewerName)(raw, blockedNamesRef.current))
1160
+ return null;
1145
1161
  const saved = await liveSelling_1.liveSelling.viewer.saveName(raw);
1146
1162
  if (saved) {
1147
1163
  liveSelling_1.liveSelling.engine.setViewerName((_a = accountNameRef.current) !== null && _a !== void 0 ? _a : saved);
@@ -1245,7 +1261,7 @@ function useSessionValue(props) {
1245
1261
  * into a cart. It is also what comes back on the host's broadcast, so `isLeading` compares like
1246
1262
  * with like. Null when signed out, which is exactly when bidding is not offered.
1247
1263
  */
1248
- const bidderEmail = (_y = (_x = identity === null || identity === void 0 ? void 0 : identity.customerEmail) === null || _x === void 0 ? void 0 : _x.call(identity)) !== null && _y !== void 0 ? _y : null;
1264
+ const bidderEmail = (_z = (_y = identity === null || identity === void 0 ? void 0 : identity.customerEmail) === null || _y === void 0 ? void 0 : _y.call(identity)) !== null && _z !== void 0 ? _z : null;
1249
1265
  const bidder = (0, react_1.useMemo)(() => (bidderId ? { userId: bidderId, userName: viewerName, email: bidderEmail } : null), [bidderId, viewerName, bidderEmail]);
1250
1266
  /**
1251
1267
  * The hammer, with whether it is this shopper's (0.9.0): they led the round as it closed and the