@tiledev/sdk-apptile-live-selling 0.16.0 → 0.16.1
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/attribution.d.ts +2 -16
- package/dist/attribution.d.ts.map +1 -1
- package/dist/attribution.js +4 -10
- package/dist/attribution.js.map +1 -1
- package/dist/auctions.d.ts.map +1 -1
- package/dist/auctions.js +11 -42
- package/dist/auctions.js.map +1 -1
- package/dist/cartAttributes.d.ts +0 -10
- package/dist/cartAttributes.d.ts.map +1 -1
- package/dist/cartAttributes.js +1 -20
- package/dist/cartAttributes.js.map +1 -1
- package/dist/client.d.ts +2 -51
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +23 -123
- package/dist/client.js.map +1 -1
- package/dist/clips.d.ts +1 -12
- package/dist/clips.d.ts.map +1 -1
- package/dist/clips.js +6 -114
- package/dist/clips.js.map +1 -1
- package/dist/commentLinks.d.ts +0 -29
- package/dist/commentLinks.d.ts.map +1 -1
- package/dist/commentLinks.js.map +1 -1
- package/dist/engine.native.d.ts.map +1 -1
- package/dist/engine.native.js +845 -17
- package/dist/engine.native.js.map +1 -1
- package/dist/index.d.ts +4 -48
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -240
- package/dist/index.js.map +1 -1
- package/dist/polls.d.ts.map +1 -1
- package/dist/polls.js +0 -3
- package/dist/polls.js.map +1 -1
- package/dist/react/LiveSellingProvider.d.ts +1 -54
- package/dist/react/LiveSellingProvider.d.ts.map +1 -1
- package/dist/react/LiveSellingProvider.js +3 -60
- package/dist/react/LiveSellingProvider.js.map +1 -1
- package/dist/react/LiveVideoTarget.d.ts.map +1 -1
- package/dist/react/LiveVideoTarget.js +3 -41
- package/dist/react/LiveVideoTarget.js.map +1 -1
- package/dist/react/index.d.ts +3 -30
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -79
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.web.d.ts +32 -36
- package/dist/react/index.web.d.ts.map +1 -1
- package/dist/react/index.web.js +122 -102
- package/dist/react/index.web.js.map +1 -1
- package/dist/react/useAuctionBidGate.d.ts +6 -62
- package/dist/react/useAuctionBidGate.d.ts.map +1 -1
- package/dist/react/useAuctionBidGate.js +27 -228
- package/dist/react/useAuctionBidGate.js.map +1 -1
- package/dist/react/useAuctionWins.d.ts +4 -14
- package/dist/react/useAuctionWins.d.ts.map +1 -1
- package/dist/react/useAuctionWins.js +5 -16
- package/dist/react/useAuctionWins.js.map +1 -1
- package/dist/react/useClips.d.ts +1 -7
- package/dist/react/useClips.d.ts.map +1 -1
- package/dist/react/useClips.js +7 -20
- package/dist/react/useClips.js.map +1 -1
- package/dist/react/useLiveSession.d.ts +3 -15
- package/dist/react/useLiveSession.d.ts.map +1 -1
- package/dist/react/useLiveSession.js +199 -612
- package/dist/react/useLiveSession.js.map +1 -1
- package/dist/react/useLiveStreams.d.ts.map +1 -1
- package/dist/react/useLiveStreams.js +2 -6
- package/dist/react/useLiveStreams.js.map +1 -1
- package/dist/react/useOngoingLive.d.ts +0 -1
- package/dist/react/useOngoingLive.d.ts.map +1 -1
- package/dist/react/useOngoingLive.js +1 -7
- package/dist/react/useOngoingLive.js.map +1 -1
- package/dist/react/useReplays.d.ts +0 -12
- package/dist/react/useReplays.d.ts.map +1 -1
- package/dist/react/useReplays.js +24 -56
- package/dist/react/useReplays.js.map +1 -1
- package/dist/react/useStreamInfo.d.ts +2 -13
- package/dist/react/useStreamInfo.d.ts.map +1 -1
- package/dist/react/useStreamInfo.js +8 -36
- package/dist/react/useStreamInfo.js.map +1 -1
- package/dist/replays.d.ts +0 -14
- package/dist/replays.d.ts.map +1 -1
- package/dist/replays.js +22 -68
- package/dist/replays.js.map +1 -1
- package/dist/session.d.ts +6 -39
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +22 -209
- package/dist/session.js.map +1 -1
- package/dist/sessionTypes.d.ts +18 -217
- package/dist/sessionTypes.d.ts.map +1 -1
- package/dist/sessionTypes.js +1 -2
- package/dist/sessionTypes.js.map +1 -1
- package/dist/streams.d.ts +1 -27
- package/dist/streams.d.ts.map +1 -1
- package/dist/streams.js +21 -84
- package/dist/streams.js.map +1 -1
- package/dist/types.d.ts +10 -288
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -2
- package/dist/types.js.map +1 -1
- package/dist/wire.d.ts +0 -31
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +0 -25
- package/dist/wire.js.map +1 -1
- package/dist/zego.d.ts +1 -3
- package/dist/zego.d.ts.map +1 -1
- package/dist/zego.js +1 -1
- package/package.json +1 -1
- package/dist/auctionComments.d.ts +0 -25
- package/dist/auctionComments.d.ts.map +0 -1
- package/dist/auctionComments.js +0 -28
- package/dist/auctionComments.js.map +0 -1
- package/dist/auctionRules.d.ts +0 -77
- package/dist/auctionRules.d.ts.map +0 -1
- package/dist/auctionRules.js +0 -107
- package/dist/auctionRules.js.map +0 -1
- package/dist/cartExtension.d.ts +0 -39
- package/dist/cartExtension.d.ts.map +0 -1
- package/dist/cartExtension.js +0 -51
- package/dist/cartExtension.js.map +0 -1
- package/dist/checkoutRefused.d.ts +0 -9
- package/dist/checkoutRefused.d.ts.map +0 -1
- package/dist/checkoutRefused.js +0 -16
- package/dist/checkoutRefused.js.map +0 -1
- package/dist/clipsPage.d.ts +0 -29
- package/dist/clipsPage.d.ts.map +0 -1
- package/dist/clipsPage.js +0 -49
- package/dist/clipsPage.js.map +0 -1
- package/dist/commentHistory.d.ts +0 -70
- package/dist/commentHistory.d.ts.map +0 -1
- package/dist/commentHistory.js +0 -133
- package/dist/commentHistory.js.map +0 -1
- package/dist/commentLinkCards.d.ts +0 -11
- package/dist/commentLinkCards.d.ts.map +0 -1
- package/dist/commentLinkCards.js +0 -70
- package/dist/commentLinkCards.js.map +0 -1
- package/dist/giveawayPrize.d.ts +0 -13
- package/dist/giveawayPrize.d.ts.map +0 -1
- package/dist/giveawayPrize.js +0 -18
- package/dist/giveawayPrize.js.map +0 -1
- package/dist/giveawayPrizeLines.d.ts +0 -120
- package/dist/giveawayPrizeLines.d.ts.map +0 -1
- package/dist/giveawayPrizeLines.js +0 -97
- package/dist/giveawayPrizeLines.js.map +0 -1
- package/dist/giveawayPrizes.d.ts +0 -20
- package/dist/giveawayPrizes.d.ts.map +0 -1
- package/dist/giveawayPrizes.js +0 -50
- package/dist/giveawayPrizes.js.map +0 -1
- package/dist/giveawayRoster.d.ts +0 -53
- package/dist/giveawayRoster.d.ts.map +0 -1
- package/dist/giveawayRoster.js +0 -93
- package/dist/giveawayRoster.js.map +0 -1
- package/dist/heldComments.d.ts +0 -40
- package/dist/heldComments.d.ts.map +0 -1
- package/dist/heldComments.js +0 -75
- package/dist/heldComments.js.map +0 -1
- package/dist/hiddenShows.d.ts +0 -44
- package/dist/hiddenShows.d.ts.map +0 -1
- package/dist/hiddenShows.js +0 -53
- package/dist/hiddenShows.js.map +0 -1
- package/dist/linkLots.d.ts +0 -64
- package/dist/linkLots.d.ts.map +0 -1
- package/dist/linkLots.js +0 -84
- package/dist/linkLots.js.map +0 -1
- package/dist/liveHaptics.d.ts +0 -7
- package/dist/liveHaptics.d.ts.map +0 -1
- package/dist/liveHaptics.js +0 -11
- package/dist/liveHaptics.js.map +0 -1
- package/dist/liveHaptics.native.d.ts +0 -7
- package/dist/liveHaptics.native.d.ts.map +0 -1
- package/dist/liveHaptics.native.js +0 -12
- package/dist/liveHaptics.native.js.map +0 -1
- package/dist/livePages.d.ts +0 -176
- package/dist/livePages.d.ts.map +0 -1
- package/dist/livePages.js +0 -216
- package/dist/livePages.js.map +0 -1
- package/dist/liveRoom.d.ts +0 -21
- package/dist/liveRoom.d.ts.map +0 -1
- package/dist/liveRoom.js +0 -28
- package/dist/liveRoom.js.map +0 -1
- package/dist/liveShow.d.ts +0 -65
- package/dist/liveShow.d.ts.map +0 -1
- package/dist/liveShow.js +0 -65
- package/dist/liveShow.js.map +0 -1
- package/dist/missedPolls.d.ts +0 -27
- package/dist/missedPolls.d.ts.map +0 -1
- package/dist/missedPolls.js +0 -16
- package/dist/missedPolls.js.map +0 -1
- package/dist/pageOptions.d.ts +0 -149
- package/dist/pageOptions.d.ts.map +0 -1
- package/dist/pageOptions.js +0 -123
- package/dist/pageOptions.js.map +0 -1
- package/dist/pageTypes.d.ts +0 -185
- package/dist/pageTypes.d.ts.map +0 -1
- package/dist/pageTypes.js +0 -3
- package/dist/pageTypes.js.map +0 -1
- package/dist/phoneTaps.d.ts +0 -25
- package/dist/phoneTaps.d.ts.map +0 -1
- package/dist/phoneTaps.js +0 -47
- package/dist/phoneTaps.js.map +0 -1
- package/dist/pipWindow.d.ts +0 -85
- package/dist/pipWindow.d.ts.map +0 -1
- package/dist/pipWindow.js +0 -88
- package/dist/pipWindow.js.map +0 -1
- package/dist/react/clocks.d.ts +0 -29
- package/dist/react/clocks.d.ts.map +0 -1
- package/dist/react/clocks.js +0 -124
- package/dist/react/clocks.js.map +0 -1
- package/dist/react/navigation.d.ts +0 -13
- package/dist/react/navigation.d.ts.map +0 -1
- package/dist/react/navigation.js +0 -7
- package/dist/react/navigation.js.map +0 -1
- package/dist/react/platformSession.d.ts +0 -16
- package/dist/react/platformSession.d.ts.map +0 -1
- package/dist/react/platformSession.js +0 -28
- package/dist/react/platformSession.js.map +0 -1
- package/dist/react/platformSession.web.d.ts +0 -35
- package/dist/react/platformSession.web.d.ts.map +0 -1
- package/dist/react/platformSession.web.js +0 -148
- package/dist/react/platformSession.web.js.map +0 -1
- package/dist/react/replayPlayer.d.ts +0 -83
- package/dist/react/replayPlayer.d.ts.map +0 -1
- package/dist/react/replayPlayer.js +0 -315
- package/dist/react/replayPlayer.js.map +0 -1
- package/dist/react/share.d.ts +0 -22
- package/dist/react/share.d.ts.map +0 -1
- package/dist/react/share.js +0 -45
- package/dist/react/share.js.map +0 -1
- package/dist/react/shopifyProviders.d.ts +0 -82
- package/dist/react/shopifyProviders.d.ts.map +0 -1
- package/dist/react/shopifyProviders.js +0 -113
- package/dist/react/shopifyProviders.js.map +0 -1
- package/dist/react/showPages.d.ts +0 -106
- package/dist/react/showPages.d.ts.map +0 -1
- package/dist/react/showPages.js +0 -166
- package/dist/react/showPages.js.map +0 -1
- package/dist/react/showPagesOnDevice.d.ts +0 -18
- package/dist/react/showPagesOnDevice.d.ts.map +0 -1
- package/dist/react/showPagesOnDevice.js +0 -31
- package/dist/react/showPagesOnDevice.js.map +0 -1
- package/dist/react/startSignIn.d.ts +0 -3
- package/dist/react/startSignIn.d.ts.map +0 -1
- package/dist/react/startSignIn.js +0 -19
- package/dist/react/startSignIn.js.map +0 -1
- package/dist/react/systemPictureInPicture.d.ts +0 -34
- package/dist/react/systemPictureInPicture.d.ts.map +0 -1
- package/dist/react/systemPictureInPicture.js +0 -93
- package/dist/react/systemPictureInPicture.js.map +0 -1
- package/dist/react/useAccountName.d.ts +0 -8
- package/dist/react/useAccountName.d.ts.map +0 -1
- package/dist/react/useAccountName.js +0 -49
- package/dist/react/useAccountName.js.map +0 -1
- package/dist/react/useClipsPage.d.ts +0 -47
- package/dist/react/useClipsPage.d.ts.map +0 -1
- package/dist/react/useClipsPage.js +0 -130
- package/dist/react/useClipsPage.js.map +0 -1
- package/dist/react/useCommentLinkCards.d.ts +0 -6
- package/dist/react/useCommentLinkCards.d.ts.map +0 -1
- package/dist/react/useCommentLinkCards.js +0 -67
- package/dist/react/useCommentLinkCards.js.map +0 -1
- package/dist/react/useFeaturedClip.d.ts +0 -15
- package/dist/react/useFeaturedClip.d.ts.map +0 -1
- package/dist/react/useFeaturedClip.js +0 -52
- package/dist/react/useFeaturedClip.js.map +0 -1
- package/dist/react/useGiveawayEntry.d.ts +0 -59
- package/dist/react/useGiveawayEntry.d.ts.map +0 -1
- package/dist/react/useGiveawayEntry.js +0 -170
- package/dist/react/useGiveawayEntry.js.map +0 -1
- package/dist/react/useGiveawayPrize.d.ts +0 -12
- package/dist/react/useGiveawayPrize.d.ts.map +0 -1
- package/dist/react/useGiveawayPrize.js +0 -53
- package/dist/react/useGiveawayPrize.js.map +0 -1
- package/dist/react/useGiveawayPrizes.d.ts +0 -4
- package/dist/react/useGiveawayPrizes.d.ts.map +0 -1
- package/dist/react/useGiveawayPrizes.js +0 -58
- package/dist/react/useGiveawayPrizes.js.map +0 -1
- package/dist/react/useHiddenShows.d.ts +0 -10
- package/dist/react/useHiddenShows.d.ts.map +0 -1
- package/dist/react/useHiddenShows.js +0 -30
- package/dist/react/useHiddenShows.js.map +0 -1
- package/dist/react/useKeepScreenAwake.d.ts +0 -2
- package/dist/react/useKeepScreenAwake.d.ts.map +0 -1
- package/dist/react/useKeepScreenAwake.js +0 -34
- package/dist/react/useKeepScreenAwake.js.map +0 -1
- package/dist/react/useLiveAuction.d.ts +0 -106
- package/dist/react/useLiveAuction.d.ts.map +0 -1
- package/dist/react/useLiveAuction.js +0 -255
- package/dist/react/useLiveAuction.js.map +0 -1
- package/dist/react/useLiveChat.d.ts +0 -110
- package/dist/react/useLiveChat.d.ts.map +0 -1
- package/dist/react/useLiveChat.js +0 -230
- package/dist/react/useLiveChat.js.map +0 -1
- package/dist/react/useLiveGiveawaysAndPolls.d.ts +0 -95
- package/dist/react/useLiveGiveawaysAndPolls.d.ts.map +0 -1
- package/dist/react/useLiveGiveawaysAndPolls.js +0 -225
- package/dist/react/useLiveGiveawaysAndPolls.js.map +0 -1
- package/dist/react/useLivePage.d.ts +0 -136
- package/dist/react/useLivePage.d.ts.map +0 -1
- package/dist/react/useLivePage.js +0 -319
- package/dist/react/useLivePage.js.map +0 -1
- package/dist/react/useLiveShop.d.ts +0 -97
- package/dist/react/useLiveShop.d.ts.map +0 -1
- package/dist/react/useLiveShop.js +0 -197
- package/dist/react/useLiveShop.js.map +0 -1
- package/dist/react/useLiveShow.d.ts +0 -3
- package/dist/react/useLiveShow.d.ts.map +0 -1
- package/dist/react/useLiveShow.js +0 -21
- package/dist/react/useLiveShow.js.map +0 -1
- package/dist/react/useMySilentAuctionBids.d.ts +0 -29
- package/dist/react/useMySilentAuctionBids.d.ts.map +0 -1
- package/dist/react/useMySilentAuctionBids.js +0 -157
- package/dist/react/useMySilentAuctionBids.js.map +0 -1
- package/dist/react/useReplayPage.d.ts +0 -155
- package/dist/react/useReplayPage.d.ts.map +0 -1
- package/dist/react/useReplayPage.js +0 -580
- package/dist/react/useReplayPage.js.map +0 -1
- package/dist/react/useSilentAuctionLot.d.ts +0 -44
- package/dist/react/useSilentAuctionLot.d.ts.map +0 -1
- package/dist/react/useSilentAuctionLot.js +0 -168
- package/dist/react/useSilentAuctionLot.js.map +0 -1
- package/dist/react/useSilentAuctionLots.d.ts +0 -41
- package/dist/react/useSilentAuctionLots.d.ts.map +0 -1
- package/dist/react/useSilentAuctionLots.js +0 -64
- package/dist/react/useSilentAuctionLots.js.map +0 -1
- package/dist/react/useSilentAuctionRounds.d.ts +0 -15
- package/dist/react/useSilentAuctionRounds.d.ts.map +0 -1
- package/dist/react/useSilentAuctionRounds.js +0 -44
- package/dist/react/useSilentAuctionRounds.js.map +0 -1
- package/dist/replayPages.d.ts +0 -31
- package/dist/replayPages.d.ts.map +0 -1
- package/dist/replayPages.js +0 -34
- package/dist/replayPages.js.map +0 -1
- package/dist/shopifyCommerce.d.ts +0 -204
- package/dist/shopifyCommerce.d.ts.map +0 -1
- package/dist/shopifyCommerce.js +0 -217
- package/dist/shopifyCommerce.js.map +0 -1
- package/dist/shopifyIdentity.d.ts +0 -61
- package/dist/shopifyIdentity.d.ts.map +0 -1
- package/dist/shopifyIdentity.js +0 -61
- package/dist/shopifyIdentity.js.map +0 -1
- package/dist/shopifyIds.d.ts +0 -9
- package/dist/shopifyIds.d.ts.map +0 -1
- package/dist/shopifyIds.js +0 -18
- package/dist/shopifyIds.js.map +0 -1
- package/dist/showCredit.d.ts +0 -89
- package/dist/showCredit.d.ts.map +0 -1
- package/dist/showCredit.js +0 -120
- package/dist/showCredit.js.map +0 -1
- package/dist/showLinks.d.ts +0 -24
- package/dist/showLinks.d.ts.map +0 -1
- package/dist/showLinks.js +0 -23
- package/dist/showLinks.js.map +0 -1
- package/dist/showLots.d.ts +0 -44
- package/dist/showLots.d.ts.map +0 -1
- package/dist/showLots.js +0 -70
- package/dist/showLots.js.map +0 -1
- package/dist/showNames.d.ts +0 -77
- package/dist/showNames.d.ts.map +0 -1
- package/dist/showNames.js +0 -143
- package/dist/showNames.js.map +0 -1
- package/dist/showPages.d.ts +0 -56
- package/dist/showPages.d.ts.map +0 -1
- package/dist/showPages.js +0 -48
- package/dist/showPages.js.map +0 -1
- package/dist/silentAuctionFeed.d.ts +0 -49
- package/dist/silentAuctionFeed.d.ts.map +0 -1
- package/dist/silentAuctionFeed.js +0 -242
- package/dist/silentAuctionFeed.js.map +0 -1
- package/dist/silentAuctionRules.d.ts +0 -253
- package/dist/silentAuctionRules.d.ts.map +0 -1
- package/dist/silentAuctionRules.js +0 -489
- package/dist/silentAuctionRules.js.map +0 -1
- package/dist/silentAuctions.d.ts +0 -65
- package/dist/silentAuctions.d.ts.map +0 -1
- package/dist/silentAuctions.js +0 -272
- package/dist/silentAuctions.js.map +0 -1
- package/dist/storeData.d.ts +0 -16
- package/dist/storeData.d.ts.map +0 -1
- package/dist/storeData.js +0 -19
- package/dist/storeData.js.map +0 -1
- package/dist/ui/BubbleFrame.d.ts +0 -38
- package/dist/ui/BubbleFrame.d.ts.map +0 -1
- package/dist/ui/BubbleFrame.js +0 -181
- package/dist/ui/BubbleFrame.js.map +0 -1
- package/dist/ui/LiveBubble.d.ts +0 -43
- package/dist/ui/LiveBubble.d.ts.map +0 -1
- package/dist/ui/LiveBubble.js +0 -116
- package/dist/ui/LiveBubble.js.map +0 -1
- package/dist/ui/ReplayBubble.d.ts +0 -23
- package/dist/ui/ReplayBubble.d.ts.map +0 -1
- package/dist/ui/ReplayBubble.js +0 -71
- package/dist/ui/ReplayBubble.js.map +0 -1
- package/dist/ui.d.ts +0 -27
- package/dist/ui.d.ts.map +0 -1
- package/dist/ui.js +0 -34
- package/dist/ui.js.map +0 -1
- package/dist/unreadRoomMessages.d.ts +0 -26
- package/dist/unreadRoomMessages.d.ts.map +0 -1
- package/dist/unreadRoomMessages.js +0 -62
- package/dist/unreadRoomMessages.js.map +0 -1
- package/dist/viewerRules.d.ts +0 -38
- package/dist/viewerRules.d.ts.map +0 -1
- package/dist/viewerRules.js +0 -46
- package/dist/viewerRules.js.map +0 -1
- package/dist/winsAttribution.d.ts +0 -22
- package/dist/winsAttribution.d.ts.map +0 -1
- package/dist/winsAttribution.js +0 -49
- package/dist/winsAttribution.js.map +0 -1
- package/dist/zegoEngine.d.ts +0 -173
- package/dist/zegoEngine.d.ts.map +0 -1
- package/dist/zegoEngine.js +0 -1069
- package/dist/zegoEngine.js.map +0 -1
|
@@ -6,17 +6,9 @@ exports.LiveSessionProvider = LiveSessionProvider;
|
|
|
6
6
|
* `LiveSessionProvider` / `useLiveSession` — the assembled session.
|
|
7
7
|
*
|
|
8
8
|
* This is the layer that turns the engine's event stream plus the polled record into one renderable
|
|
9
|
-
* state. It owns the
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* **It never ticks.** The session changes only when something happens — a room event, a record read,
|
|
13
|
-
* an action's answer — and is the same object until one of its fields changes. It holds moments, not
|
|
14
|
-
* clocks: when the round closes (`auction.closesAt`) and when the show started (`stream.startedAtMs`).
|
|
15
|
-
* A component that shows a clock counts to one of them itself (`useAuctionCountdown`, `useShowClock`,
|
|
16
|
-
* in `clocks.ts`), so only that component is drawn as the clock moves. Before 0.9.0 the session held
|
|
17
|
-
* both clocks, and every reader — every screen in the app, since the session is app-wide — was drawn
|
|
18
|
-
* five times a second during an auction round. Its own timers are timeouts to the moment they are for:
|
|
19
|
-
* the round's fallback close, and the next card or reaction to run out.
|
|
9
|
+
* state. It owns the two clocks (the elapsed/prune tick and the faster auction tick), the
|
|
10
|
+
* picture-in-picture mode machine, product resolution through the commerce adapter, and every action
|
|
11
|
+
* a live screen fires.
|
|
20
12
|
*
|
|
21
13
|
* **Mount it above your navigator.** The Zego engine holds exactly one room, and two places usually
|
|
22
14
|
* render the same stream — a home-screen card and the full-screen player. Owning the session per
|
|
@@ -31,20 +23,18 @@ const react_native_1 = require("react-native");
|
|
|
31
23
|
const activeProductRetry_1 = require("../activeProductRetry");
|
|
32
24
|
const cartAttributes_1 = require("../cartAttributes");
|
|
33
25
|
const client_1 = require("../client");
|
|
34
|
-
const auctionRules_1 = require("../auctionRules");
|
|
35
|
-
const giveawayRoster_1 = require("../giveawayRoster");
|
|
36
|
-
const showNames_1 = require("../showNames");
|
|
37
|
-
const showLots_1 = require("../showLots");
|
|
38
|
-
const heldComments_1 = require("../heldComments");
|
|
39
26
|
const liveSelling_1 = require("../liveSelling");
|
|
40
|
-
const missedPolls_1 = require("../missedPolls");
|
|
41
|
-
const pageOptions_1 = require("../pageOptions");
|
|
42
|
-
const unreadRoomMessages_1 = require("../unreadRoomMessages");
|
|
43
27
|
const sdk_apptile_live_selling_native_1 = require("@tiledev/sdk-apptile-live-selling-native");
|
|
44
|
-
const pipWindow_1 = require("../pipWindow");
|
|
45
28
|
const session_1 = require("../session");
|
|
46
29
|
const useStreamInfo_1 = require("./useStreamInfo");
|
|
47
30
|
const videoTargets_1 = require("./videoTargets");
|
|
31
|
+
/** The elapsed timer and the transient-card pruning share one tick. */
|
|
32
|
+
const TICK_MS = 1000;
|
|
33
|
+
/**
|
|
34
|
+
* The auction clock runs faster than the session tick. Seconds are the unit a bidder acts on, and a
|
|
35
|
+
* 1s tick aliases against the deadline badly enough to show the same second twice and skip another.
|
|
36
|
+
*/
|
|
37
|
+
const AUCTION_TICK_MS = 200;
|
|
48
38
|
/**
|
|
49
39
|
* How long past the deadline to wait before closing the round ourselves.
|
|
50
40
|
*
|
|
@@ -53,8 +43,6 @@ const videoTargets_1 = require("./videoTargets");
|
|
|
53
43
|
* already given up.
|
|
54
44
|
*/
|
|
55
45
|
const AUCTION_GRACE_MS = 1000;
|
|
56
|
-
/** The longest wait a JavaScript timer can hold (2^31 − 1 ms, about 24 days); a longer one fires at once. */
|
|
57
|
-
const LONGEST_TIMER_MS = 2147483647;
|
|
58
46
|
/**
|
|
59
47
|
* How long the OS is given to actually put a picture-in-picture window up after the app goes away.
|
|
60
48
|
*
|
|
@@ -64,17 +52,6 @@ const LONGEST_TIMER_MS = 2147483647;
|
|
|
64
52
|
* means the host is talking to nobody from inside a backgrounded app.
|
|
65
53
|
*/
|
|
66
54
|
const PIP_GRANT_MS = 1200;
|
|
67
|
-
/**
|
|
68
|
-
* The longest a show's room waits for the signed-in customer to load on app open (`customerLoading`),
|
|
69
|
-
* so this viewer enters under their name. Past it the room is entered anyway: a slow sign-in must not
|
|
70
|
-
* keep the show away.
|
|
71
|
-
*/
|
|
72
|
-
const NAME_WAIT_MS = 10000;
|
|
73
|
-
/**
|
|
74
|
-
* The device said it has no picture-in-picture (L21). Module state, because it is a fact about the
|
|
75
|
-
* device rather than about one session: once heard, arming stops and the error is not reported again.
|
|
76
|
-
*/
|
|
77
|
-
let pipUnavailableOnDevice = false;
|
|
78
55
|
const LiveSessionContext = (0, react_1.createContext)(null);
|
|
79
56
|
/**
|
|
80
57
|
* Reads the shared live session.
|
|
@@ -92,19 +69,12 @@ function useLiveSession() {
|
|
|
92
69
|
/* Everything but the stream hold, which belongs to the provider component: it owns which stream this
|
|
93
70
|
hook is watching, so it is the only thing that can pin it. */
|
|
94
71
|
function useSessionValue(props) {
|
|
95
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t
|
|
72
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
96
73
|
const { streamingId, viewerName: viewerNameInput, onPipRestore } = props;
|
|
97
|
-
const blockedNames = (_a = props.blockedNames) !== null && _a !== void 0 ? _a : showNames_1.DEFAULT_BLOCKED_VIEWER_NAMES;
|
|
98
|
-
const blockedNamesRef = (0, react_1.useRef)(blockedNames);
|
|
99
|
-
blockedNamesRef.current = blockedNames;
|
|
100
|
-
/** A name the room may show for this viewer: made safe (`publicName`) and not one of the team's. */
|
|
101
|
-
const allowedName = (name) => {
|
|
102
|
-
const shown = (0, showNames_1.publicName)(name);
|
|
103
|
-
return shown && !(0, showNames_1.isBlockedViewerName)(shown, blockedNames) ? shown : null;
|
|
104
|
-
};
|
|
105
74
|
const [state, dispatch] = (0, react_1.useReducer)(session_1.sessionReducer, session_1.initialSessionState);
|
|
106
75
|
const [joining, setJoining] = (0, react_1.useState)(false);
|
|
107
76
|
const [error, setError] = (0, react_1.useState)(null);
|
|
77
|
+
const [now, setNow] = (0, react_1.useState)(() => Date.now());
|
|
108
78
|
const [muted, setMuted] = (0, react_1.useState)(false);
|
|
109
79
|
const [pipMode, setPipMode] = (0, react_1.useState)('none');
|
|
110
80
|
/**
|
|
@@ -120,23 +90,23 @@ function useSessionValue(props) {
|
|
|
120
90
|
/** Read inside the PIP listeners, which subscribe once and would otherwise close over a stale value. */
|
|
121
91
|
const handoverRef = (0, react_1.useRef)(false);
|
|
122
92
|
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
*
|
|
129
|
-
* `onPipStop` that follows is not the shopper closing it.
|
|
93
|
+
* `onPipStart` claimed the system window that is up; cleared when it closes or is expanded.
|
|
94
|
+
*
|
|
95
|
+
* `pipMode` alone cannot say whose window just closed: an app that ends picture-in-picture on
|
|
96
|
+
* `AppState` `active` (Freckled Poppy's Live screen) resets `system` to `none` while the window is
|
|
97
|
+
* up — on Android 1ms after `onPipStart`, since the window opening reads as `active`. The close was
|
|
98
|
+
* then ignored and the show played on with sound behind the app.
|
|
130
99
|
*/
|
|
131
|
-
const
|
|
100
|
+
const windowClaimedRef = (0, react_1.useRef)(false);
|
|
132
101
|
/**
|
|
133
|
-
* The show was stopped
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* show played on with sound behind an app nobody was looking at.
|
|
102
|
+
* The shopper closed the system window, so the show was stopped. Until the app is back in the
|
|
103
|
+
* foreground, a surface taking the picture does not start it again (`bindVideoView`): closing the
|
|
104
|
+
* window hands the picture back to the full-screen player, still mounted behind it, and its forced
|
|
105
|
+
* bind would otherwise restart the show behind an app nobody is looking at.
|
|
138
106
|
*/
|
|
139
107
|
const stoppedWhileAwayRef = (0, react_1.useRef)(false);
|
|
108
|
+
/** The surface that asked for the picture while `stoppedWhileAwayRef` held it back; bound on return. */
|
|
109
|
+
const heldBackTagRef = (0, react_1.useRef)(null);
|
|
140
110
|
const viewerIdRef = (0, react_1.useRef)(null);
|
|
141
111
|
const logger = (0, client_1.getLogger)();
|
|
142
112
|
const identity = (0, client_1.getIdentity)();
|
|
@@ -149,9 +119,7 @@ function useSessionValue(props) {
|
|
|
149
119
|
* name is consulted at all so a signed-in bidder is never asked to invent one.
|
|
150
120
|
*/
|
|
151
121
|
const [chosenName, setChosenName] = (0, react_1.useState)(() => liveSelling_1.liveSelling.viewer.cachedName());
|
|
152
|
-
|
|
153
|
-
// a name the show's team goes by (`blockedNames`, 0.13.0).
|
|
154
|
-
const accountName = allowedName(viewerNameInput);
|
|
122
|
+
const accountName = (viewerNameInput === null || viewerNameInput === void 0 ? void 0 : viewerNameInput.trim()) || null;
|
|
155
123
|
(0, react_1.useEffect)(() => {
|
|
156
124
|
if (accountName || chosenName)
|
|
157
125
|
return;
|
|
@@ -164,109 +132,32 @@ function useSessionValue(props) {
|
|
|
164
132
|
alive = false;
|
|
165
133
|
};
|
|
166
134
|
}, [accountName, chosenName]);
|
|
167
|
-
const customerName =
|
|
168
|
-
const namedBy = (_c = accountName !== null && accountName !== void 0 ? accountName :
|
|
135
|
+
const customerName = (_b = (_a = identity === null || identity === void 0 ? void 0 : identity.customerName) === null || _a === void 0 ? void 0 : _a.call(identity)) !== null && _b !== void 0 ? _b : null;
|
|
136
|
+
const namedBy = (_c = accountName !== null && accountName !== void 0 ? accountName : chosenName) !== null && _c !== void 0 ? _c : ((customerName === null || customerName === void 0 ? void 0 : customerName.trim()) || null);
|
|
169
137
|
const viewerName = namedBy !== null && namedBy !== void 0 ? namedBy : 'Anonymous';
|
|
170
|
-
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;
|
|
171
|
-
/**
|
|
172
|
-
* The signed-in customer is still loading (app open, or just signed in), so their name isn't known
|
|
173
|
-
* yet. The room waits for it, up to `NAME_WAIT_MS`: it is entered once, and the name it is entered
|
|
174
|
-
* under is the one the host sees join. Before 0.9.2 an app opened during a show joined at once,
|
|
175
|
-
* before the sign-in was read, and every shopper joined as "Anonymous" (2026-10-08).
|
|
176
|
-
*/
|
|
177
|
-
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;
|
|
178
|
-
/**
|
|
179
|
-
* A comment sent now is held until the account has named the shopper (L6), and only while it loads:
|
|
180
|
-
* an account read with no name on it isn't waited for (`waitsForAccountName`, 0.13.0).
|
|
181
|
-
*/
|
|
182
|
-
const nameResolving = (0, heldComments_1.waitsForAccountName)(customerLoading, accountName !== null && accountName !== void 0 ? accountName : customerName);
|
|
183
|
-
const [nameWaitOver, setNameWaitOver] = (0, react_1.useState)(false);
|
|
184
|
-
(0, react_1.useEffect)(() => {
|
|
185
|
-
if (!customerLoading)
|
|
186
|
-
return;
|
|
187
|
-
const timer = setTimeout(() => setNameWaitOver(true), NAME_WAIT_MS);
|
|
188
|
-
return () => clearTimeout(timer);
|
|
189
|
-
}, [customerLoading]);
|
|
190
|
-
const waitingForName = customerLoading && !nameWaitOver;
|
|
191
|
-
const accountNameRef = (0, react_1.useRef)(accountName);
|
|
192
|
-
accountNameRef.current = accountName;
|
|
193
|
-
const namedByRef = (0, react_1.useRef)(namedBy);
|
|
194
|
-
namedByRef.current = namedBy;
|
|
195
|
-
const nameResolvingRef = (0, react_1.useRef)(nameResolving);
|
|
196
|
-
nameResolvingRef.current = nameResolving;
|
|
197
138
|
/**
|
|
198
139
|
* Pushed to the engine rather than passed at join: the room is entered once, and a name given
|
|
199
140
|
* mid-show must not cost a rejoin — that interrupts the video for everyone watching on this device.
|
|
200
141
|
* Comments and reactions carry the author in their own payload.
|
|
201
|
-
*
|
|
202
|
-
* This effect alone is too late for the FIRST comment after a name is chosen, which is why
|
|
203
|
-
* `chooseViewerName` also sets it directly — see there.
|
|
204
142
|
*/
|
|
205
143
|
(0, react_1.useEffect)(() => {
|
|
206
144
|
liveSelling_1.liveSelling.engine.setViewerName(namedBy);
|
|
207
145
|
}, [namedBy]);
|
|
146
|
+
/**
|
|
147
|
+
* The auction clock, separate from `now` so a 200ms tick does not redraw the comment pane five
|
|
148
|
+
* times a second on an ordinary live stream. This one only runs while a round is open.
|
|
149
|
+
*/
|
|
150
|
+
const [auctionNow, setAuctionNow] = (0, react_1.useState)(() => Date.now());
|
|
208
151
|
// Poll so a stream ending (or the host swapping the product set) is noticed without a manual
|
|
209
152
|
// refresh. The engine's channels cover in-stream changes; this covers the record itself. With no
|
|
210
153
|
// `streamingId` it follows whichever stream is on air.
|
|
211
|
-
const { stream, refresh: refreshStreamInfo
|
|
154
|
+
const { stream, refresh: refreshStreamInfo } = (0, useStreamInfo_1.useStreamInfo)(streamingId);
|
|
212
155
|
const watching = streamingId !== null && streamingId !== void 0 ? streamingId : stream === null || stream === void 0 ? void 0 : stream.streamingId;
|
|
213
|
-
/** The comment pipeline; `heldComments` uses it, so it is defined before the show reset below. */
|
|
214
|
-
const deliverComment = (0, react_1.useCallback)(async (text, options) => {
|
|
215
|
-
var _a;
|
|
216
|
-
try {
|
|
217
|
-
// The host mutes by device; a per-install viewer id is the same granularity.
|
|
218
|
-
const deviceId = (_a = viewerIdRef.current) !== null && _a !== void 0 ? _a : (await liveSelling_1.liveSelling.viewer.id());
|
|
219
|
-
const own = await liveSelling_1.liveSelling.engine.sendComment(text, deviceId, options);
|
|
220
|
-
if (own)
|
|
221
|
-
dispatch({ kind: 'ownComment', comment: own });
|
|
222
|
-
}
|
|
223
|
-
catch (caught) {
|
|
224
|
-
(0, client_1.getLogger)().error(caught, { at: 'liveSession.sendComment' });
|
|
225
|
-
}
|
|
226
|
-
}, []);
|
|
227
|
-
const heldCommentsRef = (0, react_1.useRef)(null);
|
|
228
|
-
if (!heldCommentsRef.current) {
|
|
229
|
-
heldCommentsRef.current = new heldComments_1.HeldComments(({ text, options }) => deliverComment(text, options));
|
|
230
|
-
}
|
|
231
156
|
(0, react_1.useEffect)(() => {
|
|
232
|
-
var _a;
|
|
233
157
|
dispatch({ kind: 'reset' });
|
|
234
158
|
setError(null);
|
|
235
|
-
// A comment held for the last show's room must not be said in this one.
|
|
236
|
-
(_a = heldCommentsRef.current) === null || _a === void 0 ? void 0 : _a.drop();
|
|
237
|
-
}, [watching]);
|
|
238
|
-
/**
|
|
239
|
-
* The account has loaded: say the name to the engine first, then send what was waiting for it, in
|
|
240
|
-
* order. Loaded with no name, they go under the session's name (one typed on this phone, or
|
|
241
|
-
* "Anonymous"), as they would after the longest wait: an app asks for a name before sending.
|
|
242
|
-
*/
|
|
243
|
-
(0, react_1.useEffect)(() => {
|
|
244
|
-
var _a;
|
|
245
|
-
if (nameResolving || !((_a = heldCommentsRef.current) === null || _a === void 0 ? void 0 : _a.size))
|
|
246
|
-
return;
|
|
247
|
-
liveSelling_1.liveSelling.engine.setViewerName(namedBy);
|
|
248
|
-
void heldCommentsRef.current.release();
|
|
249
|
-
}, [namedBy, nameResolving]);
|
|
250
|
-
/**
|
|
251
|
-
* Room messages this version doesn't read (ENG-969, `unreadRoomMessages.ts`): the first of each kind
|
|
252
|
-
* per show goes to the app's `onWarning` (`reportLiveWarning`; the console without one), so a message
|
|
253
|
-
* the dashboard renamed or moved is seen. Not the config's `logger`, which is silent unless an app sets one.
|
|
254
|
-
*/
|
|
255
|
-
const unreadRoomMessagesRef = (0, react_1.useRef)(new unreadRoomMessages_1.UnreadRoomMessages());
|
|
256
|
-
(0, react_1.useEffect)(() => {
|
|
257
|
-
unreadRoomMessagesRef.current = new unreadRoomMessages_1.UnreadRoomMessages();
|
|
258
159
|
}, [watching]);
|
|
259
160
|
(0, react_1.useEffect)(() => liveSelling_1.liveSelling.engine.subscribe((event) => {
|
|
260
|
-
if (event.type === 'unknown') {
|
|
261
|
-
const kind = unreadRoomMessagesRef.current.firstOfItsKind(event.channel, event.payload);
|
|
262
|
-
if (kind) {
|
|
263
|
-
(0, pageOptions_1.reportLiveWarning)(new Error(`Live room: a message this version doesn't read (${kind})`), {
|
|
264
|
-
at: 'liveSession.unreadRoomMessage',
|
|
265
|
-
channel: event.channel,
|
|
266
|
-
message: (0, unreadRoomMessages_1.reportedCopyOf)(event.payload),
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
161
|
dispatch({ kind: 'event', event, at: Date.now() });
|
|
271
162
|
}), []);
|
|
272
163
|
(0, react_1.useEffect)(() => {
|
|
@@ -277,9 +168,6 @@ function useSessionValue(props) {
|
|
|
277
168
|
// effect re-runs and the viewer session takes the room back.
|
|
278
169
|
if (state.guestMode)
|
|
279
170
|
return;
|
|
280
|
-
// Entered once the shopper's name is known (`waitingForName`); this effect runs again when it is.
|
|
281
|
-
if (waitingForName)
|
|
282
|
-
return;
|
|
283
171
|
/**
|
|
284
172
|
* Off-device the engine is the no-op build, so a join would *succeed* — every stubbed call
|
|
285
173
|
* resolves — and then no room state would ever arrive, leaving the status on `connecting` for
|
|
@@ -329,10 +217,9 @@ function useSessionValue(props) {
|
|
|
329
217
|
.catch((caught) => logger.error(caught, { at: 'liveSession.leave' }));
|
|
330
218
|
};
|
|
331
219
|
// viewerName is intentionally excluded: it changes when the customer loads, and rejoining the
|
|
332
|
-
// room mid-stream to relabel comments is not worth a video interruption.
|
|
333
|
-
// ever goes from true to false, before the room is entered.
|
|
220
|
+
// room mid-stream to relabel comments is not worth a video interruption.
|
|
334
221
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
335
|
-
}, [stream === null || stream === void 0 ? void 0 : stream.streamingId, stream === null || stream === void 0 ? void 0 : stream.status, state.guestMode
|
|
222
|
+
}, [stream === null || stream === void 0 ? void 0 : stream.streamingId, stream === null || stream === void 0 ? void 0 : stream.status, state.guestMode]);
|
|
336
223
|
/**
|
|
337
224
|
* Keep the muted list in step with the polled record.
|
|
338
225
|
*
|
|
@@ -340,71 +227,14 @@ function useSessionValue(props) {
|
|
|
340
227
|
* importantly an *unmute*, which is otherwise invisible until the app restarts. Keyed on the joined
|
|
341
228
|
* ids because the array is a fresh object on every poll.
|
|
342
229
|
*/
|
|
343
|
-
const mutedKey = ((
|
|
230
|
+
const mutedKey = ((_d = stream === null || stream === void 0 ? void 0 : stream.mutedDeviceIds) !== null && _d !== void 0 ? _d : []).join(',');
|
|
344
231
|
(0, react_1.useEffect)(() => {
|
|
345
232
|
if (!stream)
|
|
346
233
|
return;
|
|
347
234
|
liveSelling_1.liveSelling.engine.setMutedDeviceIds(mutedKey ? mutedKey.split(',') : []);
|
|
348
235
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
349
236
|
}, [mutedKey]);
|
|
350
|
-
|
|
351
|
-
const derivedStatus = (0, session_1.deriveStatus)(state, !!stream, joining, error, (stream === null || stream === void 0 ? void 0 : stream.status) === 'live');
|
|
352
|
-
/**
|
|
353
|
-
* Nothing on air, settled — Freckled Poppy's `NOTHING_ON_AIR_MS`, moved here.
|
|
354
|
-
*
|
|
355
|
-
* With no record at all `deriveStatus` says `loading`, and keeps saying it: a deep link to a show
|
|
356
|
-
* that has gone, or a session with nothing live, spun for ever. After `offAirGraceMs` (4 s) with a
|
|
357
|
-
* read finished, nothing live in it, and the room not reconnecting, the status becomes `ended`.
|
|
358
|
-
*
|
|
359
|
-
* Every forced read (`refreshStreamRecords()`, which a screen calls on focus) starts the wait again.
|
|
360
|
-
* That is the guard against a stale record: a push tap lands on a screen whose last read predates
|
|
361
|
-
* the show, and without the restart the show would be declared over before the fresh read came
|
|
362
|
-
* back.
|
|
363
|
-
*/
|
|
364
|
-
const nothingOnAir = (0, session_1.isNothingOnAir)({
|
|
365
|
-
hasRead,
|
|
366
|
-
streamStatus: (_j = stream === null || stream === void 0 ? void 0 : stream.status) !== null && _j !== void 0 ? _j : null,
|
|
367
|
-
status: derivedStatus,
|
|
368
|
-
});
|
|
369
|
-
const graceMs = (0, client_1.offAirGraceMs)();
|
|
370
|
-
const [settledOffAir, setSettledOffAir] = (0, react_1.useState)(false);
|
|
371
|
-
(0, react_1.useEffect)(() => {
|
|
372
|
-
setSettledOffAir(false);
|
|
373
|
-
if (!nothingOnAir)
|
|
374
|
-
return;
|
|
375
|
-
const timer = setTimeout(() => setSettledOffAir(true), graceMs);
|
|
376
|
-
return () => clearTimeout(timer);
|
|
377
|
-
}, [nothingOnAir, forcedReadAt, graceMs]);
|
|
378
|
-
const status = (0, session_1.withOffAirSettled)(derivedStatus, settledOffAir);
|
|
379
|
-
/**
|
|
380
|
-
* A poll the room never told this phone about (ENG-969, `missedPolls.ts`): while a show is on air
|
|
381
|
-
* with no poll up, its record is read for one every `MISSED_POLL_READ_MS`, so a poll opened
|
|
382
|
-
* meanwhile shows within that time rather than after the shopper leaves and comes back.
|
|
383
|
-
*
|
|
384
|
-
* Only the poll is taken from the read, through `pollSynced`, which keeps the state as it is unless
|
|
385
|
-
* the poll is one this phone has never seen. It doesn't go through `useStreamInfo`: a record set every
|
|
386
|
-
* 20 s would re-render everything that reads the session and restart the off-air wait, and that file
|
|
387
|
-
* keeps no timer by design (read its header). This one is the exception, for as long as the room's
|
|
388
|
-
* own `START_POLL` can't be relied on: one record by id, per watching phone, every 20 s, only while
|
|
389
|
-
* no poll is up.
|
|
390
|
-
*/
|
|
391
|
-
const readingForMissedPoll = (0, missedPolls_1.readsForMissedPoll)({ status, connected: state.connected, pollShowing: !!state.poll });
|
|
392
|
-
(0, react_1.useEffect)(() => {
|
|
393
|
-
if (!readingForMissedPoll || !watching)
|
|
394
|
-
return;
|
|
395
|
-
const timer = setInterval(() => {
|
|
396
|
-
liveSelling_1.liveSelling.streams
|
|
397
|
-
.byId(watching)
|
|
398
|
-
.then((record) => {
|
|
399
|
-
var _a;
|
|
400
|
-
if (record === null || record === void 0 ? void 0 : record.poll)
|
|
401
|
-
dispatch({ kind: 'pollSynced', poll: record.poll, results: (_a = record.pollResults) !== null && _a !== void 0 ? _a : null });
|
|
402
|
-
})
|
|
403
|
-
.catch((caught) => logger.error(caught, { at: 'liveSession.missedPollRead', streamingId: watching }));
|
|
404
|
-
}, missedPolls_1.MISSED_POLL_READ_MS);
|
|
405
|
-
return () => clearInterval(timer);
|
|
406
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
407
|
-
}, [readingForMissedPoll, watching]);
|
|
237
|
+
const status = (0, session_1.deriveStatus)(state, !!stream, joining, error, (stream === null || stream === void 0 ? void 0 : stream.status) === 'live');
|
|
408
238
|
// A PIP surface outlives the screen that opened it, so nothing else would take it down when the
|
|
409
239
|
// host ends the stream — it would sit there over the app showing a frozen last frame.
|
|
410
240
|
(0, react_1.useEffect)(() => {
|
|
@@ -416,19 +246,15 @@ function useSessionValue(props) {
|
|
|
416
246
|
// The host's end broadcast reaches us long before the polled record catches up. Stop the picture,
|
|
417
247
|
// then force a refetch so every poll-driven surface drops the stream instead of waiting out the
|
|
418
248
|
// interval.
|
|
419
|
-
//
|
|
420
|
-
// Keyed on the status BEFORE the off-air wait: an end the wait declared has no record to catch up
|
|
421
|
-
// with, and its forced read would restart the wait — which would end the show again four seconds
|
|
422
|
-
// later, for ever.
|
|
423
249
|
(0, react_1.useEffect)(() => {
|
|
424
|
-
if (
|
|
250
|
+
if (status !== 'ended')
|
|
425
251
|
return;
|
|
426
252
|
liveSelling_1.liveSelling.engine
|
|
427
253
|
.stopPlayback()
|
|
428
254
|
.catch((caught) => logger.error(caught, { at: 'liveSession.stopOnEnd' }));
|
|
429
255
|
(0, useStreamInfo_1.refreshStreamRecords)();
|
|
430
256
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
431
|
-
}, [
|
|
257
|
+
}, [status]);
|
|
432
258
|
/**
|
|
433
259
|
* Whether a mounted surface has asked to hand over to a system window.
|
|
434
260
|
*
|
|
@@ -446,29 +272,16 @@ function useSessionValue(props) {
|
|
|
446
272
|
// `pipMode !== 'none'` covers the in-app bubble; `handoverWanted` is what lets the FULL-SCREEN
|
|
447
273
|
// player hand over too. Without it, minimising from the player armed nothing, no window opened,
|
|
448
274
|
// and the audio played on behind the app because the plugin declares the `audio` background mode.
|
|
449
|
-
//
|
|
450
|
-
// What was last armed, and when it was last disarmed, are what decide on Android whether a window
|
|
451
|
-
// that opens is ours — see `ownsSystemPipWindow`.
|
|
452
|
-
//
|
|
453
|
-
// Only while the show is `playing` (`shouldArmSystemWindow`): joining, a host off camera and an ended
|
|
454
|
-
// show have no picture to put in a window. A window already up stays armed whatever the status does.
|
|
455
|
-
const armedRef = (0, react_1.useRef)(false);
|
|
456
|
-
const disarmedAtRef = (0, react_1.useRef)(0);
|
|
457
275
|
(0, react_1.useEffect)(() => {
|
|
458
276
|
if (!sdk_apptile_live_selling_native_1.isLivePipSupported)
|
|
459
277
|
return;
|
|
460
|
-
const armed =
|
|
461
|
-
|
|
462
|
-
disarmedAtRef.current = Date.now();
|
|
463
|
-
armedRef.current = armed;
|
|
464
|
-
// A device without picture-in-picture answers every arming with an error (L21); stop asking.
|
|
465
|
-
if (!(armed && pipUnavailableOnDevice))
|
|
466
|
-
void sdk_apptile_live_selling_native_1.livePip.setArmed(armed);
|
|
278
|
+
const armed = pipMode !== 'none' || handoverWanted;
|
|
279
|
+
void sdk_apptile_live_selling_native_1.livePip.setArmed(armed);
|
|
467
280
|
liveSelling_1.liveSelling.engine
|
|
468
281
|
.setBackgroundVideoEnabled(armed)
|
|
469
282
|
.catch((caught) => logger.error(caught, { at: 'liveSession.backgroundVideo' }));
|
|
470
283
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
471
|
-
}, [pipMode, handoverWanted
|
|
284
|
+
}, [pipMode, handoverWanted]);
|
|
472
285
|
/**
|
|
473
286
|
* The sound must not outlive the app when no window follows.
|
|
474
287
|
*
|
|
@@ -477,16 +290,11 @@ function useSessionValue(props) {
|
|
|
477
290
|
* declared. So a backgrounded session gives the OS `PIP_GRANT_MS` to produce a window and stops
|
|
478
291
|
* playback if none arrived. `pipModeRef` rather than `pipMode`, because this subscribes once and
|
|
479
292
|
* the answer is only knowable when the timer fires.
|
|
480
|
-
*
|
|
481
|
-
* A show stopped here stays stopped until the app is back (`stoppedWhileAwayRef`): a surface that
|
|
482
|
-
* re-claims meanwhile — its status changed, a window was closed — would otherwise start it again
|
|
483
|
-
* behind the app. Back in the foreground, `LiveVideoTarget` takes the picture again by itself.
|
|
484
293
|
*/
|
|
485
294
|
(0, react_1.useEffect)(() => {
|
|
486
295
|
let grant = null;
|
|
487
296
|
const subscription = react_native_1.AppState.addEventListener('change', (next) => {
|
|
488
297
|
if (next === 'active') {
|
|
489
|
-
stoppedWhileAwayRef.current = false;
|
|
490
298
|
if (grant)
|
|
491
299
|
clearTimeout(grant);
|
|
492
300
|
grant = null;
|
|
@@ -500,7 +308,6 @@ function useSessionValue(props) {
|
|
|
500
308
|
grant = null;
|
|
501
309
|
if (pipModeRef.current === 'system')
|
|
502
310
|
return;
|
|
503
|
-
stoppedWhileAwayRef.current = true;
|
|
504
311
|
liveSelling_1.liveSelling.engine
|
|
505
312
|
.stopPlayback()
|
|
506
313
|
.catch((caught) => logger.error(caught, { at: 'liveSession.noPipWindow' }));
|
|
@@ -518,50 +325,25 @@ function useSessionValue(props) {
|
|
|
518
325
|
return;
|
|
519
326
|
/**
|
|
520
327
|
* Only ours if this session had a surface up first — either the in-app bubble, or the full-screen
|
|
521
|
-
* player having asked for the handover
|
|
522
|
-
*
|
|
523
|
-
*
|
|
524
|
-
* for why Android cannot go by the handover alone.
|
|
328
|
+
* player having asked for the handover. Anything else minimising into a system window raises the
|
|
329
|
+
* same event, and taking it means the live session believes it is in a PIP window it never
|
|
330
|
+
* opened.
|
|
525
331
|
*/
|
|
526
332
|
const offStart = sdk_apptile_live_selling_native_1.livePip.addListener('onPipStart', () => {
|
|
527
|
-
|
|
528
|
-
platform: react_native_1.Platform.OS,
|
|
529
|
-
pipMode: pipModeRef.current,
|
|
530
|
-
handoverWanted: handoverRef.current,
|
|
531
|
-
armed: armedRef.current,
|
|
532
|
-
disarmedAt: disarmedAtRef.current,
|
|
533
|
-
now: Date.now(),
|
|
534
|
-
});
|
|
535
|
-
if (!ours)
|
|
333
|
+
if (pipModeRef.current !== 'inapp' && !handoverRef.current)
|
|
536
334
|
return;
|
|
537
|
-
|
|
538
|
-
takingWindowDownRef.current = false;
|
|
335
|
+
windowClaimedRef.current = true;
|
|
539
336
|
setPipMode('system');
|
|
540
337
|
});
|
|
541
|
-
/**
|
|
542
|
-
* Coming back: the picture goes back to where the window took it from. The in-app bubble is the
|
|
543
|
-
* app's own surface and is still mounted, so `'inapp'` is all it takes; a full-screen player is a
|
|
544
|
-
* route only the app can navigate to, so it hears `onPipRestore`. Set in the same event as the
|
|
545
|
-
* window's end, so no render ever sees `'none'` on the way back to the bubble: a card that claims
|
|
546
|
-
* on `'none'` would take the picture for one frame and leave a black one behind.
|
|
547
|
-
*/
|
|
548
|
-
const giveThePictureBack = () => {
|
|
549
|
-
const place = windowCameFromRef.current;
|
|
550
|
-
setPipMode(place);
|
|
551
|
-
if (place === 'none')
|
|
552
|
-
onPipRestore === null || onPipRestore === void 0 ? void 0 : onPipRestore(watching);
|
|
553
|
-
};
|
|
554
338
|
// Expanding and closing both end in `onPipStop`; `restored` is the only thing that tells them
|
|
555
|
-
// apart, and the expand path is handled by `onPipRestore` instead.
|
|
556
|
-
//
|
|
339
|
+
// apart, and the expand path is handled by `onPipRestore` instead. A window this session claimed
|
|
340
|
+
// stops the show on close whatever `pipMode` says now (`windowClaimedRef`).
|
|
557
341
|
const offStop = sdk_apptile_live_selling_native_1.livePip.addListener('onPipStop', ({ restored }) => {
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
342
|
+
const claimedAtStart = windowClaimedRef.current;
|
|
343
|
+
windowClaimedRef.current = false;
|
|
562
344
|
if (restored)
|
|
563
345
|
return;
|
|
564
|
-
if (pipModeRef.current !== 'system')
|
|
346
|
+
if (pipModeRef.current !== 'system' && !claimedAtStart)
|
|
565
347
|
return;
|
|
566
348
|
stoppedWhileAwayRef.current = true;
|
|
567
349
|
setPipMode('none');
|
|
@@ -570,37 +352,17 @@ function useSessionValue(props) {
|
|
|
570
352
|
.catch((caught) => logger.error(caught, { at: 'liveSession.pipDismissed' }));
|
|
571
353
|
});
|
|
572
354
|
const offRestore = sdk_apptile_live_selling_native_1.livePip.addListener('onPipRestore', () => {
|
|
355
|
+
windowClaimedRef.current = false;
|
|
573
356
|
if (pipModeRef.current !== 'system')
|
|
574
357
|
return;
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
/* iOS: back in the app with the window still up (see `isWindowStillUpOnReturn`). The picture goes
|
|
578
|
-
back where it was, then the window is taken down; its `onPipStop` is ours, not a close. */
|
|
579
|
-
const offReturn = react_native_1.AppState.addEventListener('change', (next) => {
|
|
580
|
-
if (next !== 'active')
|
|
581
|
-
return;
|
|
582
|
-
if (!(0, pipWindow_1.isWindowStillUpOnReturn)(react_native_1.Platform.OS, pipModeRef.current))
|
|
583
|
-
return;
|
|
584
|
-
takingWindowDownRef.current = true;
|
|
585
|
-
giveThePictureBack();
|
|
586
|
-
void sdk_apptile_live_selling_native_1.livePip.stop();
|
|
587
|
-
});
|
|
588
|
-
/* "Not available on this device" is a fact about the device, reported once as a warning — not an
|
|
589
|
-
error filed every time the bubble opens (L21). */
|
|
590
|
-
const offError = sdk_apptile_live_selling_native_1.livePip.addListener('onPipError', ({ message }) => {
|
|
591
|
-
if ((0, pipWindow_1.isPipUnavailableMessage)(message)) {
|
|
592
|
-
if (!pipUnavailableOnDevice)
|
|
593
|
-
(0, client_1.logWarning)(new Error(message), { at: 'liveSession.pipUnavailable' });
|
|
594
|
-
pipUnavailableOnDevice = true;
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
logger.error(new Error(message), { at: 'liveSession.pipError' });
|
|
358
|
+
setPipMode('none');
|
|
359
|
+
onPipRestore === null || onPipRestore === void 0 ? void 0 : onPipRestore(watching);
|
|
598
360
|
});
|
|
361
|
+
const offError = sdk_apptile_live_selling_native_1.livePip.addListener('onPipError', ({ message }) => logger.error(new Error(message), { at: 'liveSession.pipError' }));
|
|
599
362
|
return () => {
|
|
600
363
|
offStart === null || offStart === void 0 ? void 0 : offStart();
|
|
601
364
|
offStop === null || offStop === void 0 ? void 0 : offStop();
|
|
602
365
|
offRestore === null || offRestore === void 0 ? void 0 : offRestore();
|
|
603
|
-
offReturn.remove();
|
|
604
366
|
offError === null || offError === void 0 ? void 0 : offError();
|
|
605
367
|
};
|
|
606
368
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -611,6 +373,19 @@ function useSessionValue(props) {
|
|
|
611
373
|
const subscription = react_native_1.AppState.addEventListener('change', (next) => {
|
|
612
374
|
if (next !== 'active')
|
|
613
375
|
return;
|
|
376
|
+
if (stoppedWhileAwayRef.current) {
|
|
377
|
+
// The shopper closed the window and came back: `stopPlayback` dropped the bound view, so
|
|
378
|
+
// there is nothing to refresh — bind the surface that asked for the picture while away.
|
|
379
|
+
stoppedWhileAwayRef.current = false;
|
|
380
|
+
const tag = heldBackTagRef.current;
|
|
381
|
+
heldBackTagRef.current = null;
|
|
382
|
+
if (tag == null)
|
|
383
|
+
return;
|
|
384
|
+
liveSelling_1.liveSelling.engine
|
|
385
|
+
.bindView(tag, true)
|
|
386
|
+
.catch((caught) => logger.error(caught, { at: 'liveSession.bindViewOnReturn' }));
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
614
389
|
liveSelling_1.liveSelling.engine
|
|
615
390
|
.refreshPlayback()
|
|
616
391
|
.catch((caught) => logger.error(caught, { at: 'liveSession.refreshPlayback' }));
|
|
@@ -618,45 +393,39 @@ function useSessionValue(props) {
|
|
|
618
393
|
return () => subscription.remove();
|
|
619
394
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
620
395
|
}, []);
|
|
621
|
-
/**
|
|
622
|
-
* Sold cards, cart cards and reactions leave when their time is up (`SOLD_CARD_MS`, `CART_CARD_MS`,
|
|
623
|
-
* `REACTION_MS`): one timer, to the moment the next one runs out (`nextExpiryAt`), set again whenever
|
|
624
|
-
* that moment changes. Nothing runs with none on screen.
|
|
625
|
-
*
|
|
626
|
-
* The prune is told it is that moment even if the timer fires a millisecond early, so the card it
|
|
627
|
-
* was set for is always the one that goes — otherwise nothing would change, the moment would stay
|
|
628
|
-
* the same, and no timer would be set again.
|
|
629
|
-
*/
|
|
630
|
-
const nextExpiry = (0, session_1.nextExpiryAt)(state);
|
|
631
396
|
(0, react_1.useEffect)(() => {
|
|
632
|
-
if (
|
|
397
|
+
if (status !== 'playing' &&
|
|
398
|
+
state.sold.length === 0 &&
|
|
399
|
+
state.cartAnnouncements.length === 0 &&
|
|
400
|
+
state.reactions.length === 0) {
|
|
633
401
|
return;
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
402
|
+
}
|
|
403
|
+
const timer = setInterval(() => {
|
|
404
|
+
const at = Date.now();
|
|
405
|
+
setNow(at);
|
|
406
|
+
dispatch({ kind: 'prune', now: at });
|
|
407
|
+
}, TICK_MS);
|
|
408
|
+
return () => clearInterval(timer);
|
|
409
|
+
}, [status, state.sold.length, state.cartAnnouncements.length, state.reactions.length]);
|
|
639
410
|
/**
|
|
640
|
-
* The
|
|
641
|
-
*
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
* Destructured rather than read off `state`, so the timer is keyed on exactly the three things that
|
|
645
|
-
* move the moment. An open round with no deadline yet (a bid heard before the opening, carrying
|
|
646
|
-
* none) closes at once, as it did on the old 200 ms clock: there is nothing to wait for.
|
|
411
|
+
* The auction clock. Only runs while a round is open, so it costs nothing on a normal live stream.
|
|
412
|
+
* It also owns the fallback close: past the deadline plus the grace period with no word from the
|
|
413
|
+
* host, the round is over as far as this device is concerned.
|
|
647
414
|
*/
|
|
648
415
|
const { biddingOpen, endsAtMs, serverOffsetMs } = state;
|
|
649
416
|
(0, react_1.useEffect)(() => {
|
|
650
417
|
if (!biddingOpen)
|
|
651
418
|
return;
|
|
652
|
-
const
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
419
|
+
const timer = setInterval(() => {
|
|
420
|
+
const at = Date.now();
|
|
421
|
+
setAuctionNow(at);
|
|
422
|
+
// Destructured rather than read off `state`, so the closure cannot go stale against a deadline
|
|
423
|
+
// a late bid has already moved — the deps re-arm it when one does.
|
|
424
|
+
if (endsAtMs - (at + serverOffsetMs) < -AUCTION_GRACE_MS) {
|
|
425
|
+
dispatch({ kind: 'auctionExpired' });
|
|
426
|
+
}
|
|
427
|
+
}, AUCTION_TICK_MS);
|
|
428
|
+
return () => clearInterval(timer);
|
|
660
429
|
}, [biddingOpen, endsAtMs, serverOffsetMs]);
|
|
661
430
|
/**
|
|
662
431
|
* Giveaway state off the polled record.
|
|
@@ -666,7 +435,8 @@ function useSessionValue(props) {
|
|
|
666
435
|
* enter a giveaway they are already in.
|
|
667
436
|
*/
|
|
668
437
|
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]);
|
|
669
|
-
const
|
|
438
|
+
const bidderId = (_f = (_e = identity === null || identity === void 0 ? void 0 : identity.bidderId) === null || _e === void 0 ? void 0 : _e.call(identity)) !== null && _f !== void 0 ? _f : null;
|
|
439
|
+
const customerRef = (_h = (_g = identity === null || identity === void 0 ? void 0 : identity.customerRef) === null || _g === void 0 ? void 0 : _g.call(identity)) !== null && _h !== void 0 ? _h : null;
|
|
670
440
|
/**
|
|
671
441
|
* Closes the gap between the realtime giveaway start and the polled record it depends on.
|
|
672
442
|
*
|
|
@@ -682,7 +452,6 @@ function useSessionValue(props) {
|
|
|
682
452
|
void refreshStreamInfo();
|
|
683
453
|
}, [giveawayKnown, refreshStreamInfo]);
|
|
684
454
|
(0, react_1.useEffect)(() => {
|
|
685
|
-
var _a;
|
|
686
455
|
if (!activeGiveaway)
|
|
687
456
|
return;
|
|
688
457
|
const entries = activeGiveaway.entries.filter((entry) => entry.active);
|
|
@@ -692,37 +461,8 @@ function useSessionValue(props) {
|
|
|
692
461
|
entryCount: entries.length,
|
|
693
462
|
registered: !!bidderId && entries.some((entry) => entry.userId === bidderId),
|
|
694
463
|
bidderId,
|
|
695
|
-
entrantNames: activeGiveaway.entrantNames,
|
|
696
|
-
startedAtMs: (_a = activeGiveaway.startedAtMs) !== null && _a !== void 0 ? _a : null,
|
|
697
464
|
});
|
|
698
465
|
}, [activeGiveaway, bidderId]);
|
|
699
|
-
/**
|
|
700
|
-
* The draw's roster — ENG-705, moved here from Freckled Poppy's Live screen.
|
|
701
|
-
*
|
|
702
|
-
* `giveaway_end` names the winner and nobody else, and reaches the room before the gateway has
|
|
703
|
-
* written the entrants onto the record. When the roster latched while the lot was open is empty
|
|
704
|
-
* too, the record is asked again every 700 ms for up to 5 s; the first read with names is taken and
|
|
705
|
-
* kept (`giveawayRoster`), and running out of time lets the reveal go ahead without one.
|
|
706
|
-
*/
|
|
707
|
-
const rosterWait = state.giveawayResult && !state.giveawayResult.entrantNamesReady
|
|
708
|
-
? `${state.giveawayResult.storeProductId}\u0000${state.giveawayResult.winnerName}`
|
|
709
|
-
: null;
|
|
710
|
-
(0, react_1.useEffect)(() => {
|
|
711
|
-
if (!rosterWait)
|
|
712
|
-
return;
|
|
713
|
-
return (0, giveawayRoster_1.waitForGiveawayRoster)({
|
|
714
|
-
readRecord: () => void refreshStreamInfo(),
|
|
715
|
-
onGiveUp: () => dispatch({ kind: 'giveawayRosterTimedOut' }),
|
|
716
|
-
});
|
|
717
|
-
}, [rosterWait, refreshStreamInfo]);
|
|
718
|
-
(0, react_1.useEffect)(() => {
|
|
719
|
-
const result = state.giveawayResult;
|
|
720
|
-
if (!result || result.entrantNamesReady || !stream)
|
|
721
|
-
return;
|
|
722
|
-
const names = (0, giveawayRoster_1.rosterFromRecord)(stream.giveaways, result.storeProductId);
|
|
723
|
-
if (names.length)
|
|
724
|
-
dispatch({ kind: 'giveawayRoster', storeProductId: result.storeProductId, names });
|
|
725
|
-
}, [state.giveawayResult, stream]);
|
|
726
466
|
/**
|
|
727
467
|
* The poll off the RECORD, for one this device never heard open — DEL-2769.
|
|
728
468
|
*
|
|
@@ -781,93 +521,29 @@ function useSessionValue(props) {
|
|
|
781
521
|
}));
|
|
782
522
|
}, [stream, state.removedStoreProductIds]);
|
|
783
523
|
/**
|
|
784
|
-
*
|
|
785
|
-
*
|
|
786
|
-
* Two things live only on the record and change at exactly this moment:
|
|
524
|
+
* A lot the host featured that this record has never heard of.
|
|
787
525
|
*
|
|
788
|
-
*
|
|
789
|
-
*
|
|
790
|
-
*
|
|
791
|
-
*
|
|
792
|
-
*
|
|
793
|
-
* read it resolved to nothing and `activeProduct` fell back to a different product.
|
|
526
|
+
* `productChange` adds the id to the extras, so a CATALOGUE lot survives: the id goes to
|
|
527
|
+
* `productsByIds` and the storefront answers for it immediately. A LINK lot cannot — everything
|
|
528
|
+
* it has to show (name, thumbnail, URL) lives only on the stream record, which is polled on a
|
|
529
|
+
* five-minute timer. So a link lot added and featured mid-stream resolved to nothing, and
|
|
530
|
+
* `activeProduct` fell back to `products[0]` — showing the room a different product entirely.
|
|
794
531
|
*
|
|
795
|
-
*
|
|
532
|
+
* Re-reading the record is what makes it appear. Once per id: an id that is genuinely absent
|
|
533
|
+
* must not turn into a poll every render.
|
|
796
534
|
*/
|
|
535
|
+
const chasedRecordFor = (0, react_1.useRef)(null);
|
|
797
536
|
(0, react_1.useEffect)(() => {
|
|
798
|
-
var _a;
|
|
799
537
|
const active = state.activeStoreProductId;
|
|
800
|
-
if (!active)
|
|
801
|
-
return;
|
|
802
|
-
const row = (_a = streamRef.current) === null || _a === void 0 ? void 0 : _a.products.find((product) => product.storeProductId === active);
|
|
803
|
-
if (row === null || row === void 0 ? void 0 : row.sellId)
|
|
804
|
-
return;
|
|
805
|
-
void refreshStreamInfo();
|
|
806
|
-
}, [state.activeStoreProductId, refreshStreamInfo]);
|
|
807
|
-
/**
|
|
808
|
-
* What the room said before this device arrived, put in front of the comments — once per show.
|
|
809
|
-
*
|
|
810
|
-
* Read when the show is known to be live, and dropped if the show changed while it was in flight.
|
|
811
|
-
* `mergeCommentHistory` (in the reducer) leaves out anything the room has already delivered and
|
|
812
|
-
* renames the host's stored "You" to the host.
|
|
813
|
-
*/
|
|
814
|
-
const liveStreamingId = (stream === null || stream === void 0 ? void 0 : stream.status) === 'live' ? stream.streamingId : null;
|
|
815
|
-
(0, react_1.useEffect)(() => {
|
|
816
|
-
if (!liveStreamingId || react_native_1.Platform.OS === 'web')
|
|
538
|
+
if (!active || !stream)
|
|
817
539
|
return;
|
|
818
|
-
|
|
819
|
-
liveSelling_1.liveSelling.streams
|
|
820
|
-
.commentHistory(liveStreamingId)
|
|
821
|
-
.then((history) => {
|
|
822
|
-
var _a, _b;
|
|
823
|
-
if (cancelled || !history.length)
|
|
824
|
-
return;
|
|
825
|
-
dispatch({
|
|
826
|
-
kind: 'commentHistory',
|
|
827
|
-
history,
|
|
828
|
-
hostName: (_b = (_a = streamRef.current) === null || _a === void 0 ? void 0 : _a.hostName) !== null && _b !== void 0 ? _b : null,
|
|
829
|
-
ownName: namedByRef.current,
|
|
830
|
-
});
|
|
831
|
-
})
|
|
832
|
-
.catch((caught) => logger.error(caught, { at: 'liveSession.commentHistory', streamingId: liveStreamingId }));
|
|
833
|
-
return () => {
|
|
834
|
-
cancelled = true;
|
|
835
|
-
};
|
|
836
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
837
|
-
}, [liveStreamingId]);
|
|
838
|
-
/**
|
|
839
|
-
* The pinned comment, off the RECORD — for arrival, and for every reconnect.
|
|
840
|
-
*
|
|
841
|
-
* The room's `PINNED_COMMENT_CHANGED` reaches the reducer directly and wins until the record's own
|
|
842
|
-
* pin changes. Dispatched only when the record's pin CHANGES (keyed with the show), so a read
|
|
843
|
-
* taken before an unpin cannot bring the old pin back — the same precedence the record's poll has.
|
|
844
|
-
*
|
|
845
|
-
* A broadcast sent while the room was down never arrives, so a reconnect re-reads the record and
|
|
846
|
-
* the first record after it is taken whatever it says. Joining counts as a reconnect, so arriving
|
|
847
|
-
* costs one extra read.
|
|
848
|
-
*/
|
|
849
|
-
const recordPin = (_m = stream === null || stream === void 0 ? void 0 : stream.pinnedComment) !== null && _m !== void 0 ? _m : null;
|
|
850
|
-
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 : ''}`;
|
|
851
|
-
const lastRecordPinKey = (0, react_1.useRef)(null);
|
|
852
|
-
const takeNextRecordPin = (0, react_1.useRef)(false);
|
|
853
|
-
(0, react_1.useEffect)(() => {
|
|
854
|
-
if (!stream)
|
|
540
|
+
if (stream.products.some((product) => product.storeProductId === active))
|
|
855
541
|
return;
|
|
856
|
-
if (
|
|
542
|
+
if (chasedRecordFor.current === active)
|
|
857
543
|
return;
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
862
|
-
}, [stream, recordPinKey]);
|
|
863
|
-
const wasConnected = (0, react_1.useRef)(false);
|
|
864
|
-
(0, react_1.useEffect)(() => {
|
|
865
|
-
if (state.connected && !wasConnected.current) {
|
|
866
|
-
takeNextRecordPin.current = true;
|
|
867
|
-
void refreshStreamInfo();
|
|
868
|
-
}
|
|
869
|
-
wasConnected.current = state.connected;
|
|
870
|
-
}, [state.connected, refreshStreamInfo]);
|
|
544
|
+
chasedRecordFor.current = active;
|
|
545
|
+
refreshStreamInfo();
|
|
546
|
+
}, [state.activeStoreProductId, stream, refreshStreamInfo]);
|
|
871
547
|
/**
|
|
872
548
|
* Resolved through the commerce adapter, keyed on the joined ids so a mid-stream product add
|
|
873
549
|
* refetches and an unchanged set does not. Link lots are held out: they have no id the catalogue
|
|
@@ -909,7 +585,7 @@ function useSessionValue(props) {
|
|
|
909
585
|
if (!activeRetryRef.current) {
|
|
910
586
|
activeRetryRef.current = new activeProductRetry_1.ActiveProductRetry(() => setCatalogueRetry((n) => n + 1));
|
|
911
587
|
}
|
|
912
|
-
const featuredId = (
|
|
588
|
+
const featuredId = (_l = (_j = state.activeStoreProductId) !== null && _j !== void 0 ? _j : (_k = stream === null || stream === void 0 ? void 0 : stream.products.find((product) => product.isCurrent)) === null || _k === void 0 ? void 0 : _k.storeProductId) !== null && _l !== void 0 ? _l : null;
|
|
913
589
|
const featuredCatalogueId = featuredId && productKey.split(',').includes(featuredId) ? featuredId : null;
|
|
914
590
|
const answerSeq = (catalogueAnswer === null || catalogueAnswer === void 0 ? void 0 : catalogueAnswer.key) === productKey ? catalogueAnswer.seq : null;
|
|
915
591
|
const hasFeatured = !!featuredCatalogueId &&
|
|
@@ -946,15 +622,27 @@ function useSessionValue(props) {
|
|
|
946
622
|
return ordered;
|
|
947
623
|
};
|
|
948
624
|
/*
|
|
949
|
-
* Sell codes
|
|
950
|
-
*
|
|
951
|
-
*
|
|
952
|
-
* matching them itself (`withLotFieldsFromRecord`; the opening bid since 0.9.0).
|
|
625
|
+
* Sell codes live on the stream record, not in the catalogue — the commerce adapter is asked for
|
|
626
|
+
* products by id and has no idea a lot carries one. Joined on here so a call site reads
|
|
627
|
+
* `product.sellId` instead of holding both lists and matching them itself.
|
|
953
628
|
*
|
|
954
|
-
* A new array only when
|
|
955
|
-
*
|
|
629
|
+
* A new array only when at least one code exists, so an app on a gateway that issues none keeps
|
|
630
|
+
* the adapter's own objects and the identity the memo already promised.
|
|
956
631
|
*/
|
|
957
|
-
|
|
632
|
+
const base = merge();
|
|
633
|
+
const codes = new Map();
|
|
634
|
+
for (const row of (_a = stream === null || stream === void 0 ? void 0 : stream.products) !== null && _a !== void 0 ? _a : []) {
|
|
635
|
+
if (row.sellId)
|
|
636
|
+
codes.set(row.storeProductId, row.sellId);
|
|
637
|
+
}
|
|
638
|
+
if (!codes.size)
|
|
639
|
+
return base;
|
|
640
|
+
return base.map((product) => {
|
|
641
|
+
var _a;
|
|
642
|
+
return codes.has(product.storeProductId)
|
|
643
|
+
? { ...product, sellId: (_a = codes.get(product.storeProductId)) !== null && _a !== void 0 ? _a : null }
|
|
644
|
+
: product;
|
|
645
|
+
});
|
|
958
646
|
}, [catalogueProducts, externalProducts, storeProductIds, stream]);
|
|
959
647
|
const activeProduct = (0, react_1.useMemo)(() => {
|
|
960
648
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -982,10 +670,11 @@ function useSessionValue(props) {
|
|
|
982
670
|
return null;
|
|
983
671
|
return (_f = products[0]) !== null && _f !== void 0 ? _f : null;
|
|
984
672
|
}, [products, state.activeStoreProductId, stream]);
|
|
985
|
-
const multiplier = (
|
|
673
|
+
const multiplier = (_o = (_m = state.multiplierOverride) !== null && _m !== void 0 ? _m : stream === null || stream === void 0 ? void 0 : stream.viewerMultiplier) !== null && _o !== void 0 ? _o : 1;
|
|
986
674
|
/* Same precedence as the multiplier: the host's live word wins, the record stands until they
|
|
987
675
|
give one. Both fall back rather than throw, so a stream row missing `meta` still renders. */
|
|
988
|
-
const reactionStyle = (
|
|
676
|
+
const reactionStyle = (_q = (_p = state.reactionModeOverride) !== null && _p !== void 0 ? _p : stream === null || stream === void 0 ? void 0 : stream.reactionStyle) !== null && _q !== void 0 ? _q : 'single';
|
|
677
|
+
const elapsedMs = (stream === null || stream === void 0 ? void 0 : stream.startedAtMs) ? Math.max(0, now - stream.startedAtMs) : 0;
|
|
989
678
|
/**
|
|
990
679
|
* Percentages come off the raw counts, so the multiplier cannot skew them — it inflates only the
|
|
991
680
|
* total on display, exactly as the host's own dashboard does.
|
|
@@ -1063,28 +752,34 @@ function useSessionValue(props) {
|
|
|
1063
752
|
alive = false;
|
|
1064
753
|
};
|
|
1065
754
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1066
|
-
}, [(
|
|
755
|
+
}, [(_r = state.poll) === null || _r === void 0 ? void 0 : _r.pollUuid]);
|
|
1067
756
|
const bindVideoView = (0, react_1.useCallback)((reactTag, force = false) => {
|
|
1068
|
-
// Stopped because the
|
|
1069
|
-
//
|
|
1070
|
-
|
|
757
|
+
// Stopped because the shopper closed the window: a claim now would start the show behind the app,
|
|
758
|
+
// with sound. Not `AppState.currentState` — on Android the window itself reads as `active`. The
|
|
759
|
+
// surface is bound once the app comes back (the foreground effect).
|
|
760
|
+
if (stoppedWhileAwayRef.current) {
|
|
761
|
+
heldBackTagRef.current = reactTag;
|
|
1071
762
|
return;
|
|
763
|
+
}
|
|
1072
764
|
liveSelling_1.liveSelling.engine
|
|
1073
765
|
.bindView(reactTag, force)
|
|
1074
766
|
.catch((caught) => (0, client_1.getLogger)().error(caught, { at: 'liveSession.bindView' }));
|
|
1075
767
|
}, []);
|
|
1076
|
-
/**
|
|
1077
|
-
* Sends now — or, for a signed-in shopper whose name has not loaded, holds it until the name lands
|
|
1078
|
-
* (L6) and resolves once it has gone out. See `HeldComments`.
|
|
1079
|
-
*/
|
|
1080
768
|
const sendComment = (0, react_1.useCallback)(async (text, options) => {
|
|
769
|
+
var _a;
|
|
1081
770
|
if (!text.trim())
|
|
1082
771
|
return;
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
772
|
+
try {
|
|
773
|
+
// The host mutes by device; a per-install viewer id is the same granularity.
|
|
774
|
+
const deviceId = (_a = viewerIdRef.current) !== null && _a !== void 0 ? _a : (await liveSelling_1.liveSelling.viewer.id());
|
|
775
|
+
const own = await liveSelling_1.liveSelling.engine.sendComment(text, deviceId, options);
|
|
776
|
+
if (own)
|
|
777
|
+
dispatch({ kind: 'ownComment', comment: own });
|
|
778
|
+
}
|
|
779
|
+
catch (caught) {
|
|
780
|
+
(0, client_1.getLogger)().error(caught, { at: 'liveSession.sendComment' });
|
|
781
|
+
}
|
|
782
|
+
}, []);
|
|
1088
783
|
const sendReaction = (0, react_1.useCallback)(async (reactionUrl) => {
|
|
1089
784
|
try {
|
|
1090
785
|
await liveSelling_1.liveSelling.engine.sendReaction(reactionUrl);
|
|
@@ -1195,25 +890,10 @@ function useSessionValue(props) {
|
|
|
1195
890
|
.bindGuestHostView(reactTag)
|
|
1196
891
|
.catch((caught) => (0, client_1.getLogger)().error(caught, { at: 'liveSession.bindGuestHostView' }));
|
|
1197
892
|
}, []);
|
|
1198
|
-
/**
|
|
1199
|
-
* Saves the name and gives it to the engine NOW, not in the effect (L6).
|
|
1200
|
-
*
|
|
1201
|
-
* The caller's next line is the comment that prompted for the name. The effect that pushes the name
|
|
1202
|
-
* belongs to this provider, a parent of that caller, and React runs parents' effects after
|
|
1203
|
-
* children's — so the comment went out under the join-time name, usually "Anonymous", into the
|
|
1204
|
-
* room and the stored history. Freckled Poppy set the engine's name itself to get round it. A name
|
|
1205
|
-
* passed as the `viewerName` prop still wins, as it does everywhere else.
|
|
1206
|
-
*/
|
|
1207
893
|
const chooseViewerName = (0, react_1.useCallback)(async (raw) => {
|
|
1208
|
-
var _a;
|
|
1209
|
-
// A name the show's team goes by isn't saved (0.13.0): the app says why (`isBlockedViewerName`).
|
|
1210
|
-
if ((0, showNames_1.isBlockedViewerName)(raw, blockedNamesRef.current))
|
|
1211
|
-
return null;
|
|
1212
894
|
const saved = await liveSelling_1.liveSelling.viewer.saveName(raw);
|
|
1213
|
-
if (saved)
|
|
1214
|
-
liveSelling_1.liveSelling.engine.setViewerName((_a = accountNameRef.current) !== null && _a !== void 0 ? _a : saved);
|
|
895
|
+
if (saved)
|
|
1215
896
|
setChosenName(saved);
|
|
1216
|
-
}
|
|
1217
897
|
return saved;
|
|
1218
898
|
}, []);
|
|
1219
899
|
/** The speaker button, and nothing else. See `setOutputMuted` below for the other half. */
|
|
@@ -1312,51 +992,38 @@ function useSessionValue(props) {
|
|
|
1312
992
|
* into a cart. It is also what comes back on the host's broadcast, so `isLeading` compares like
|
|
1313
993
|
* with like. Null when signed out, which is exactly when bidding is not offered.
|
|
1314
994
|
*/
|
|
1315
|
-
const bidderEmail = (
|
|
995
|
+
const bidderEmail = (_t = (_s = identity === null || identity === void 0 ? void 0 : identity.customerEmail) === null || _s === void 0 ? void 0 : _s.call(identity)) !== null && _t !== void 0 ? _t : null;
|
|
1316
996
|
const bidder = (0, react_1.useMemo)(() => (bidderId ? { userId: bidderId, userName: viewerName, email: bidderEmail } : null), [bidderId, viewerName, bidderEmail]);
|
|
1317
|
-
/**
|
|
1318
|
-
* The hammer, with whether it is this shopper's (0.9.0): they led the round as it closed and the
|
|
1319
|
-
* winning bid is theirs (`isYourWin`). Its own memo, so it is a new object only when the win, the
|
|
1320
|
-
* round's last leader or the bidder change, never on a comment.
|
|
1321
|
-
*/
|
|
1322
|
-
const lastWin = (0, react_1.useMemo)(() => state.lastWin
|
|
1323
|
-
? { ...state.lastWin, isYou: (0, auctionRules_1.isYourWin)(state.lastWin, state.leaderWhenRoundClosed, bidderId) }
|
|
1324
|
-
: null, [state.lastWin, state.leaderWhenRoundClosed, bidderId]);
|
|
1325
997
|
const auction = (0, react_1.useMemo)(() => {
|
|
1326
998
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
1327
|
-
// Falls back to the lot's opening
|
|
1328
|
-
//
|
|
1329
|
-
const nextBid = state.nextBid || (0
|
|
999
|
+
// Falls back to the lot's opening price: until the first bid lands the host sends no next bid,
|
|
1000
|
+
// and a slide-to-bid showing zero is worse than useless.
|
|
1001
|
+
const nextBid = state.nextBid || (biddingLot === null || biddingLot === void 0 ? void 0 : biddingLot.baseBidPrice) || 0;
|
|
1330
1002
|
return {
|
|
1331
1003
|
biddingOpen: state.biddingOpen,
|
|
1332
1004
|
highestBid: state.highestBid,
|
|
1333
1005
|
nextBid,
|
|
1334
|
-
|
|
1335
|
-
// never ticks. A component counts down to it itself (`useAuctionCountdown`).
|
|
1336
|
-
closesAt: (0, session_1.roundClosesAt)(state),
|
|
1006
|
+
timeLeftMs: (0, session_1.timeLeftMs)(state, auctionNow),
|
|
1337
1007
|
isLeading: !!bidder && ((_a = state.bidLeader) === null || _a === void 0 ? void 0 : _a.userId) === bidder.userId,
|
|
1338
1008
|
leaderName: (_c = (_b = state.bidLeader) === null || _b === void 0 ? void 0 : _b.userName) !== null && _c !== void 0 ? _c : null,
|
|
1339
1009
|
extended: state.auctionExtended,
|
|
1340
1010
|
storeProductId: (_e = (_d = state.biddingStoreProductId) !== null && _d !== void 0 ? _d : biddingLot === null || biddingLot === void 0 ? void 0 : biddingLot.storeProductId) !== null && _e !== void 0 ? _e : null,
|
|
1341
|
-
lastWin,
|
|
1011
|
+
lastWin: state.lastWin,
|
|
1342
1012
|
bidCap: (_f = stream === null || stream === void 0 ? void 0 : stream.bidCap) !== null && _f !== void 0 ? _f : null,
|
|
1343
1013
|
extendedBiddingMs: (_g = stream === null || stream === void 0 ? void 0 : stream.extendedBiddingMs) !== null && _g !== void 0 ? _g : 0,
|
|
1344
|
-
wonLotIds: state.wonLotIds,
|
|
1345
1014
|
};
|
|
1346
|
-
}, [state, biddingLot, bidder, stream
|
|
1015
|
+
}, [state, auctionNow, biddingLot, bidder, stream]);
|
|
1347
1016
|
const giveaway = (0, react_1.useMemo)(() => {
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
const winnerId = drawn && stream ? (0, giveawayRoster_1.winnerIdFromRecord)(stream.giveaways, drawn.storeProductId, drawn.startedAtMs) : null;
|
|
1017
|
+
var _a, _b;
|
|
1018
|
+
const result = state.giveawayResult;
|
|
1019
|
+
// Entrant names come off the record's roster rather than the event, which carries only the
|
|
1020
|
+
// winner. A draw the record has not caught up with still celebrates, with fewer names in it.
|
|
1021
|
+
const settled = result
|
|
1022
|
+
? stream === null || stream === void 0 ? void 0 : stream.giveaways.find((item) => item.storeProductId === result.storeProductId && item.status === 'ended')
|
|
1023
|
+
: undefined;
|
|
1356
1024
|
return {
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
? (0, giveawayRoster_1.giveawayForLot)(stream.giveaways, state.giveawayStoreProductId)
|
|
1025
|
+
active: state.giveawayStoreProductId
|
|
1026
|
+
? ((_a = stream === null || stream === void 0 ? void 0 : stream.giveaways.find((item) => item.storeProductId === state.giveawayStoreProductId)) !== null && _a !== void 0 ? _a : null)
|
|
1360
1027
|
: null,
|
|
1361
1028
|
// Inflated by the host's multiplier, exactly as `viewerCount` and poll totals are. State keeps
|
|
1362
1029
|
// the raw count; only the read out of the session multiplies.
|
|
@@ -1367,21 +1034,8 @@ function useSessionValue(props) {
|
|
|
1367
1034
|
* still active and a re-render to fire.
|
|
1368
1035
|
*/
|
|
1369
1036
|
registered: !!bidderId && state.giveawayRegisteredBy === bidderId,
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
? {
|
|
1373
|
-
winnerName: drawn.winnerName,
|
|
1374
|
-
entrantNames: drawn.entrantNames,
|
|
1375
|
-
entrantNamesReady: drawn.entrantNamesReady,
|
|
1376
|
-
youEntered,
|
|
1377
|
-
isYou: (0, giveawayRoster_1.isYourGiveawayWin)({
|
|
1378
|
-
winnerId,
|
|
1379
|
-
winnerName: drawn.winnerName,
|
|
1380
|
-
youEntered,
|
|
1381
|
-
bidderId,
|
|
1382
|
-
viewerName: namedBy,
|
|
1383
|
-
}),
|
|
1384
|
-
}
|
|
1037
|
+
result: result
|
|
1038
|
+
? { winnerName: result.winnerName, entrantNames: (_b = settled === null || settled === void 0 ? void 0 : settled.entrantNames) !== null && _b !== void 0 ? _b : [] }
|
|
1385
1039
|
: null,
|
|
1386
1040
|
};
|
|
1387
1041
|
}, [
|
|
@@ -1392,7 +1046,6 @@ function useSessionValue(props) {
|
|
|
1392
1046
|
state.giveawayResult,
|
|
1393
1047
|
stream,
|
|
1394
1048
|
multiplier,
|
|
1395
|
-
namedBy,
|
|
1396
1049
|
]);
|
|
1397
1050
|
/**
|
|
1398
1051
|
* The round, the stream and the bidder, for the actions to read when they fire.
|
|
@@ -1429,13 +1082,11 @@ function useSessionValue(props) {
|
|
|
1429
1082
|
const round = roundRef.current;
|
|
1430
1083
|
const current = streamRef.current;
|
|
1431
1084
|
const who = bidderRef.current;
|
|
1432
|
-
if (!current)
|
|
1433
|
-
return
|
|
1434
|
-
if (!who)
|
|
1435
|
-
return { sent: false, reason: 'signedOut' };
|
|
1085
|
+
if (!current || !who)
|
|
1086
|
+
return;
|
|
1436
1087
|
const amount = (_a = options === null || options === void 0 ? void 0 : options.amount) !== null && _a !== void 0 ? _a : round.auction.nextBid;
|
|
1437
1088
|
if (!amount || amount <= 0)
|
|
1438
|
-
return
|
|
1089
|
+
return;
|
|
1439
1090
|
/**
|
|
1440
1091
|
* The lot's only variant, for a bid that names none.
|
|
1441
1092
|
*
|
|
@@ -1452,20 +1103,6 @@ function useSessionValue(props) {
|
|
|
1452
1103
|
*/
|
|
1453
1104
|
const lot = productsRef.current.find((product) => product.storeProductId === round.auction.storeProductId);
|
|
1454
1105
|
const soleVariantId = (lot === null || lot === void 0 ? void 0 : lot.variants.length) === 1 ? ((_c = (_b = lot.variants[0]) === null || _b === void 0 ? void 0 : _b.id) !== null && _c !== void 0 ? _c : null) : null;
|
|
1455
|
-
/**
|
|
1456
|
-
* No moderator, no bid (L3). Bids go to the moderator in charge and to nobody else, and a viewer
|
|
1457
|
-
* who missed the round's opening may not know one — see `lastModeratorId`. Said rather than
|
|
1458
|
-
* swallowed, and the ledger is left alone: before 0.9.0 the REST record still went out, for a bid
|
|
1459
|
-
* that never entered the round.
|
|
1460
|
-
*/
|
|
1461
|
-
if (!round.moderatorId) {
|
|
1462
|
-
(0, client_1.logWarning)(new Error('No moderator is known for this round, so the bid was not sent'), {
|
|
1463
|
-
at: 'liveSession.placeBid',
|
|
1464
|
-
amount,
|
|
1465
|
-
});
|
|
1466
|
-
return { sent: false, reason: 'noModerator' };
|
|
1467
|
-
}
|
|
1468
|
-
let result = { sent: true };
|
|
1469
1106
|
try {
|
|
1470
1107
|
await liveSelling_1.liveSelling.engine.sendBid({
|
|
1471
1108
|
bidAmount: amount,
|
|
@@ -1479,7 +1116,6 @@ function useSessionValue(props) {
|
|
|
1479
1116
|
}
|
|
1480
1117
|
catch (caught) {
|
|
1481
1118
|
(0, client_1.getLogger)().error(caught, { at: 'liveSession.placeBid', amount });
|
|
1482
|
-
result = { sent: false, reason: 'sendFailed' };
|
|
1483
1119
|
}
|
|
1484
1120
|
try {
|
|
1485
1121
|
await liveSelling_1.liveSelling.auctions.recordBid({
|
|
@@ -1493,17 +1129,14 @@ function useSessionValue(props) {
|
|
|
1493
1129
|
catch (caught) {
|
|
1494
1130
|
(0, client_1.getLogger)().error(caught, { at: 'liveSession.recordBid', amount });
|
|
1495
1131
|
}
|
|
1496
|
-
return result;
|
|
1497
1132
|
}, []);
|
|
1498
1133
|
const enterGiveaway = (0, react_1.useCallback)(async (cartId) => {
|
|
1499
1134
|
var _a, _b, _c;
|
|
1500
1135
|
const lot = giveawayLotRef.current;
|
|
1501
1136
|
const current = streamRef.current;
|
|
1502
1137
|
const customer = customerRefRef.current;
|
|
1503
|
-
if (!current || !lot)
|
|
1504
|
-
return
|
|
1505
|
-
if (!customer)
|
|
1506
|
-
return { entered: false, reason: 'signedOut' };
|
|
1138
|
+
if (!current || !lot || !customer)
|
|
1139
|
+
return;
|
|
1507
1140
|
try {
|
|
1508
1141
|
const viewerId = (_a = viewerIdRef.current) !== null && _a !== void 0 ? _a : (await liveSelling_1.liveSelling.viewer.id());
|
|
1509
1142
|
const entered = await liveSelling_1.liveSelling.auctions.enterGiveaway({
|
|
@@ -1516,14 +1149,11 @@ function useSessionValue(props) {
|
|
|
1516
1149
|
});
|
|
1517
1150
|
// Only on the host's confirmation. The entry count follows from its own push, so nothing here
|
|
1518
1151
|
// guesses at it.
|
|
1519
|
-
if (
|
|
1520
|
-
|
|
1521
|
-
dispatch({ kind: 'giveawayRegistered', bidderId: (_c = (_b = bidderRef.current) === null || _b === void 0 ? void 0 : _b.userId) !== null && _c !== void 0 ? _c : null });
|
|
1522
|
-
return { entered: true };
|
|
1152
|
+
if (entered)
|
|
1153
|
+
dispatch({ kind: 'giveawayRegistered', bidderId: (_c = (_b = bidderRef.current) === null || _b === void 0 ? void 0 : _b.userId) !== null && _c !== void 0 ? _c : null });
|
|
1523
1154
|
}
|
|
1524
1155
|
catch (caught) {
|
|
1525
1156
|
(0, client_1.getLogger)().error(caught, { at: 'liveSession.enterGiveaway' });
|
|
1526
|
-
return { entered: false, reason: 'failed' };
|
|
1527
1157
|
}
|
|
1528
1158
|
}, []);
|
|
1529
1159
|
const dismissAuctionWin = (0, react_1.useCallback)(() => dispatch({ kind: 'dismissWin' }), []);
|
|
@@ -1580,35 +1210,10 @@ function useSessionValue(props) {
|
|
|
1580
1210
|
dismissAuctionWin,
|
|
1581
1211
|
dismissGiveawayResult,
|
|
1582
1212
|
]);
|
|
1583
|
-
|
|
1584
|
-
mode: state.guestMode,
|
|
1585
|
-
status: state.guestStatus,
|
|
1586
|
-
slot: state.guestSlot,
|
|
1587
|
-
mutedByHost: state.guestMutedByHost,
|
|
1588
|
-
cameraOffByHost: state.guestCameraOffByHost,
|
|
1589
|
-
mixerLayout: state.mixerLayout,
|
|
1590
|
-
}), [
|
|
1591
|
-
state.guestMode,
|
|
1592
|
-
state.guestStatus,
|
|
1593
|
-
state.guestSlot,
|
|
1594
|
-
state.guestMutedByHost,
|
|
1595
|
-
state.guestCameraOffByHost,
|
|
1596
|
-
state.mixerLayout,
|
|
1597
|
-
]);
|
|
1598
|
-
const viewerCount = Math.round(state.rawViewerCount * multiplier);
|
|
1599
|
-
const pinnedComment = status === 'ended' ? null : state.pinnedComment;
|
|
1600
|
-
/**
|
|
1601
|
-
* The same object until one of its fields changes.
|
|
1602
|
-
*
|
|
1603
|
-
* Every component that reads the session is drawn again when this object changes, and the session is
|
|
1604
|
-
* app-wide. Built fresh on every render, it changed whenever the provider was drawn for any reason —
|
|
1605
|
-
* its parent passing the same props again, a record read that changed nothing a reader sees — and
|
|
1606
|
-
* drew every reader with it. Field by field, so a new object means something in it is new.
|
|
1607
|
-
*/
|
|
1608
|
-
return (0, react_1.useMemo)(() => ({
|
|
1213
|
+
return {
|
|
1609
1214
|
status,
|
|
1610
1215
|
stream: stream !== null && stream !== void 0 ? stream : null,
|
|
1611
|
-
viewerCount,
|
|
1216
|
+
viewerCount: Math.round(state.rawViewerCount * multiplier),
|
|
1612
1217
|
rawViewerCount: state.rawViewerCount,
|
|
1613
1218
|
reactionStyle,
|
|
1614
1219
|
comments: state.comments,
|
|
@@ -1619,39 +1224,23 @@ function useSessionValue(props) {
|
|
|
1619
1224
|
reactions: state.reactions,
|
|
1620
1225
|
poll,
|
|
1621
1226
|
viewerName: namedBy,
|
|
1622
|
-
guest
|
|
1227
|
+
guest: {
|
|
1228
|
+
mode: state.guestMode,
|
|
1229
|
+
status: state.guestStatus,
|
|
1230
|
+
slot: state.guestSlot,
|
|
1231
|
+
mutedByHost: state.guestMutedByHost,
|
|
1232
|
+
cameraOffByHost: state.guestCameraOffByHost,
|
|
1233
|
+
mixerLayout: state.mixerLayout,
|
|
1234
|
+
},
|
|
1623
1235
|
muted,
|
|
1624
1236
|
videoTag: state.videoTag,
|
|
1237
|
+
elapsedMs,
|
|
1625
1238
|
pipMode,
|
|
1626
1239
|
auction,
|
|
1627
1240
|
giveaway,
|
|
1628
|
-
pinnedComment,
|
|
1629
|
-
error,
|
|
1630
|
-
actions,
|
|
1631
|
-
}), [
|
|
1632
|
-
status,
|
|
1633
|
-
stream,
|
|
1634
|
-
viewerCount,
|
|
1635
|
-
state.rawViewerCount,
|
|
1636
|
-
reactionStyle,
|
|
1637
|
-
state.comments,
|
|
1638
|
-
activeProduct,
|
|
1639
|
-
products,
|
|
1640
|
-
state.sold,
|
|
1641
|
-
state.cartAnnouncements,
|
|
1642
|
-
state.reactions,
|
|
1643
|
-
poll,
|
|
1644
|
-
namedBy,
|
|
1645
|
-
guest,
|
|
1646
|
-
muted,
|
|
1647
|
-
state.videoTag,
|
|
1648
|
-
pipMode,
|
|
1649
|
-
auction,
|
|
1650
|
-
giveaway,
|
|
1651
|
-
pinnedComment,
|
|
1652
1241
|
error,
|
|
1653
1242
|
actions,
|
|
1654
|
-
|
|
1243
|
+
};
|
|
1655
1244
|
}
|
|
1656
1245
|
/**
|
|
1657
1246
|
* Holds one live session for the whole app. Nothing is joined while no stream is live, so the engine
|
|
@@ -1679,20 +1268,18 @@ function LiveSessionProvider({ children, ...props }) {
|
|
|
1679
1268
|
the video — see the note on `actions` in `useSessionValue`. */
|
|
1680
1269
|
const watchedRef = (0, react_1.useRef)(undefined);
|
|
1681
1270
|
watchedRef.current = (_d = (_c = props.streamingId) !== null && _c !== void 0 ? _c : heldId) !== null && _d !== void 0 ? _d : (_e = session.stream) === null || _e === void 0 ? void 0 : _e.streamingId;
|
|
1682
|
-
const holdStream = (0, react_1.useCallback)((
|
|
1683
|
-
/*
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
const id = streamingId || watchedRef.current;
|
|
1271
|
+
const holdStream = (0, react_1.useCallback)(() => {
|
|
1272
|
+
/* Nothing to pin yet — the caller retries when a stream id arrives, so a player opened before
|
|
1273
|
+
the read resolved is not left following whatever turns up next. */
|
|
1274
|
+
const id = watchedRef.current;
|
|
1687
1275
|
if (id)
|
|
1688
1276
|
setHeldId(id);
|
|
1689
1277
|
}, []);
|
|
1690
1278
|
const releaseStream = (0, react_1.useCallback)(() => setHeldId(null), []);
|
|
1691
|
-
/* Two memos, and the split matters: `session`
|
|
1692
|
-
the same one would rebuild `actions`
|
|
1279
|
+
/* Two memos, and the split matters: `session` is a fresh object every render, so folding the
|
|
1280
|
+
actions into the same one would rebuild `actions` every render too — the exact thing the note in
|
|
1693
1281
|
`useSessionValue` warns about, and it live-locks a player that calls `holdStream` from an effect
|
|
1694
|
-
keyed on `actions`. `session.actions` is stable, so keyed on that this one is as well.
|
|
1695
|
-
is the same object while nothing in it changed, so `value` is too: no reader is drawn for nothing. */
|
|
1282
|
+
keyed on `actions`. `session.actions` is stable, so keyed on that this one is as well. */
|
|
1696
1283
|
const actions = (0, react_1.useMemo)(() => ({ ...session.actions, holdStream, releaseStream }), [session.actions, holdStream, releaseStream]);
|
|
1697
1284
|
const value = (0, react_1.useMemo)(() => ({ ...session, actions }), [session, actions]);
|
|
1698
1285
|
return (0, react_1.createElement)(LiveSessionContext.Provider, { value }, children);
|