diff --git a/flow-typed/livestream.js b/flow-typed/livestream.js index 936339428..0eaf2757e 100644 --- a/flow-typed/livestream.js +++ b/flow-typed/livestream.js @@ -21,17 +21,6 @@ declare type LivestreamReplayItem = { } declare type LivestreamReplayData = Array; -declare type CurrentLiveClaim = { - claimId: string | null, - claimUri: string | null, -} - -declare type LivestreamChannelStatus = { - channelId: null | string, - isBroadcasting: boolean, - liveClaim: CurrentLiveClaim, -} - declare type LivestreamState = { fetchingById: {}, viewersById: {}, @@ -39,8 +28,7 @@ declare type LivestreamState = { activeLivestreams: ?LivestreamInfo, activeLivestreamsLastFetchedDate: number, activeLivestreamsLastFetchedOptions: {}, - - currentChannelStatus: LivestreamChannelStatus, + activeLivestreamInitialized: boolean, } declare type LivestreamInfo = { diff --git a/ui/component/channelContent/index.js b/ui/component/channelContent/index.js index 61f3a546f..76f6c715d 100644 --- a/ui/component/channelContent/index.js +++ b/ui/component/channelContent/index.js @@ -14,8 +14,8 @@ import { withRouter } from 'react-router'; import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectClientSetting, selectShowMatureContent } from 'redux/selectors/settings'; import { doFetchActiveLivestream } from 'redux/actions/livestream'; -import { selectCurrentChannelStatus } from 'redux/selectors/livestream'; - +import { selectActiveLivestreamForChannel, selectActiveLivestreamInitialized } from 'redux/selectors/livestream'; +import { getChannelIdFromClaim } from 'util/claim'; import ChannelContent from './view'; const select = (state, props) => { @@ -23,6 +23,7 @@ const select = (state, props) => { const urlParams = new URLSearchParams(search); const page = urlParams.get('page') || 0; const claim = props.uri && selectClaimForUri(state, props.uri); + const channelClaimId = getChannelIdFromClaim(claim); return { pageOfClaimsInChannel: makeSelectClaimsInChannelForPage(props.uri, page)(state), @@ -34,7 +35,8 @@ const select = (state, props) => { isAuthenticated: selectUserVerifiedEmail(state), showMature: selectShowMatureContent(state), tileLayout: selectClientSetting(state, SETTINGS.TILE_LAYOUT), - currentChannelStatus: selectCurrentChannelStatus(state), + activeLivestreamForChannel: selectActiveLivestreamForChannel(state, channelClaimId), + activeLivestreamInitialized: selectActiveLivestreamInitialized(state), }; }; diff --git a/ui/component/channelContent/view.jsx b/ui/component/channelContent/view.jsx index 49c1afa47..52f692d2c 100644 --- a/ui/component/channelContent/view.jsx +++ b/ui/component/channelContent/view.jsx @@ -38,7 +38,8 @@ type Props = { claimType: string, empty?: string, doFetchActiveLivestream: (string) => void, - currentChannelStatus: LivestreamChannelStatus, + activeLivestreamForChannel: any, + activeLivestreamInitialized: boolean, }; function ChannelContent(props: Props) { @@ -59,7 +60,8 @@ function ChannelContent(props: Props) { claimType, empty, doFetchActiveLivestream, - currentChannelStatus, + activeLivestreamForChannel, + activeLivestreamInitialized, } = props; // const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0; const claimsInChannel = 9999; @@ -252,8 +254,8 @@ function ChannelContent(props: Props) { setSearchResults(null); }, [url]); - const [isInitialized, setIsInitialized] = React.useState(false); - const [isChannelBroadcasting, setIsChannelBroadcasting] = React.useState(false); + const isInitialized = Boolean(activeLivestreamForChannel) || activeLivestreamInitialized; + const isChannelBroadcasting = Boolean(activeLivestreamForChannel); // Find out current channels status + active live claim. React.useEffect(() => { @@ -262,14 +264,6 @@ function ChannelContent(props: Props) { return () => clearInterval(intervalId); }, [claimId, doFetchActiveLivestream]); - React.useEffect(() => { - const initialized = currentChannelStatus.channelId === claimId; - setIsInitialized(initialized); - if (initialized) { - setIsChannelBroadcasting(currentChannelStatus.isBroadcasting); - } - }, [currentChannelStatus, claimId]); - const showScheduledLiveStreams = claimType !== 'collection'; // ie. not on the playlist page. return ( @@ -279,7 +273,7 @@ function ChannelContent(props: Props) { )} {!fetching && isInitialized && isChannelBroadcasting && !isChannelEmpty && ( - + )} {!fetching && showScheduledLiveStreams && ( @@ -287,7 +281,7 @@ function ChannelContent(props: Props) { channelIds={[claimId]} tileLayout={tileLayout} liveUris={ - isChannelBroadcasting && currentChannelStatus.liveClaim ? [currentChannelStatus.liveClaim.claimUri] : [] + isChannelBroadcasting && activeLivestreamForChannel.claimUri ? [activeLivestreamForChannel.claimUri] : [] } /> )} diff --git a/ui/component/claimListDiscover/context.js b/ui/component/claimListDiscover/context.js deleted file mode 100644 index 22eaf5354..000000000 --- a/ui/component/claimListDiscover/context.js +++ /dev/null @@ -1,5 +0,0 @@ -import { createContext } from 'react'; - -const ClaimListDiscoverContext = createContext(); - -export default ClaimListDiscoverContext; diff --git a/ui/component/claimPreviewSubtitle/index.js b/ui/component/claimPreviewSubtitle/index.js index 513f8d444..fcf3c65b6 100644 --- a/ui/component/claimPreviewSubtitle/index.js +++ b/ui/component/claimPreviewSubtitle/index.js @@ -5,7 +5,6 @@ import { doClearPublish, doPrepareEdit } from 'redux/actions/publish'; import { push } from 'connected-react-router'; import ClaimPreviewSubtitle from './view'; import { doFetchSubCount, selectSubCountForUri } from 'lbryinc'; -import { selectIsActiveLivestreamForUri } from 'redux/selectors/livestream'; const select = (state, props) => { const claim = selectClaimForUri(state, props.uri); @@ -16,7 +15,6 @@ const select = (state, props) => { pending: makeSelectClaimIsPending(props.uri)(state), isLivestream, subCount: isChannel ? selectSubCountForUri(state, props.uri) : 0, - isLivestreamActive: isLivestream && selectIsActiveLivestreamForUri(state, props.uri), }; }; diff --git a/ui/component/claimPreviewSubtitle/view.jsx b/ui/component/claimPreviewSubtitle/view.jsx index 5c2f2e5eb..c12fca6a0 100644 --- a/ui/component/claimPreviewSubtitle/view.jsx +++ b/ui/component/claimPreviewSubtitle/view.jsx @@ -1,13 +1,12 @@ // @flow import { ENABLE_NO_SOURCE_CLAIMS } from 'config'; -import React, { useContext } from 'react'; +import React from 'react'; import UriIndicator from 'component/uriIndicator'; import DateTime from 'component/dateTime'; +import LivestreamDateTime from 'component/livestreamDateTime'; import Button from 'component/button'; import FileViewCountInline from 'component/fileViewCountInline'; import { parseURI } from 'util/lbryURI'; -import ClaimListDiscoverContext from 'component/claimListDiscover/context'; -import moment from 'moment'; type Props = { uri: string, @@ -18,12 +17,11 @@ type Props = { isLivestream: boolean, fetchSubCount: (string) => void, subCount: number, - isLivestreamActive: boolean, }; // previews used in channel overview and homepage (and other places?) function ClaimPreviewSubtitle(props: Props) { - const { pending, uri, claim, type, beginPublish, isLivestream, isLivestreamActive, fetchSubCount, subCount } = props; + const { pending, uri, claim, type, beginPublish, isLivestream, fetchSubCount, subCount } = props; const isChannel = claim && claim.value_type === 'channel'; const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0; @@ -41,29 +39,6 @@ function ClaimPreviewSubtitle(props: Props) { ({ streamName: name } = parseURI(uri)); } catch (e) {} - const { listingType } = useContext(ClaimListDiscoverContext) || {}; - - const LivestreamDateTimeLabel = () => { - // If showing in upcoming and in the past. (we allow x time in past to show here if not live yet) - if (listingType === 'UPCOMING') { - // $FlowFixMe - if (moment.unix(claim.value.release_time).isBefore()) { - return __('Starting Soon'); - } - } else { - // If not in upcoming + live and in the future (started streaming a bit early) - // $FlowFixMe - if (isLivestreamActive && moment.unix(claim.value.release_time).isAfter()) { - return __('Streaming Now'); - } - } - return ( - <> - {__('Livestream')} - - ); - }; - return (
{claim ? ( @@ -82,7 +57,7 @@ function ClaimPreviewSubtitle(props: Props) { {!isChannel && (isLivestream && ENABLE_NO_SOURCE_CLAIMS ? ( - + ) : ( <> diff --git a/ui/component/claimPreviewTile/view.jsx b/ui/component/claimPreviewTile/view.jsx index a89d6449e..5244ba03e 100644 --- a/ui/component/claimPreviewTile/view.jsx +++ b/ui/component/claimPreviewTile/view.jsx @@ -1,11 +1,12 @@ // @flow -import React, { useContext } from 'react'; +import React from 'react'; import classnames from 'classnames'; import { NavLink, withRouter } from 'react-router-dom'; import FileThumbnail from 'component/fileThumbnail'; import UriIndicator from 'component/uriIndicator'; import TruncatedText from 'component/common/truncated-text'; import DateTime from 'component/dateTime'; +import LivestreamDateTime from 'component/livestreamDateTime'; import ChannelThumbnail from 'component/channelThumbnail'; import FileViewCountInline from 'component/fileViewCountInline'; import SubscribeButton from 'component/subscribeButton'; @@ -19,8 +20,6 @@ import FileWatchLaterLink from 'component/fileWatchLaterLink'; import ClaimRepostAuthor from 'component/claimRepostAuthor'; import ClaimMenuList from 'component/claimMenuList'; import CollectionPreviewOverlay from 'component/collectionPreviewOverlay'; -import ClaimListDiscoverContext from 'component/claimListDiscover/context'; -import moment from 'moment'; // $FlowFixMe cannot resolve ... import PlaceholderTx from 'static/img/placeholderTx.gif'; @@ -110,8 +109,6 @@ function ClaimPreviewTile(props: Props) { } } - const { listingType } = useContext(ClaimListDiscoverContext) || {}; - const signingChannel = claim && claim.signing_channel; const isChannel = claim && claim.value_type === 'channel'; const channelUri = !isChannel ? signingChannel && signingChannel.permanent_url : claim && claim.permanent_url; @@ -175,23 +172,6 @@ function ClaimPreviewTile(props: Props) { liveProperty = (claim) => <>LIVE; } - const LivestreamDateTimeLabel = () => { - // If showing in upcoming and in the past. (we allow x time in past to show here if not live yet) - if (listingType === 'UPCOMING') { - // $FlowFixMe - if (moment.unix(claim.value.release_time).isBefore()) { - return __('Starting Soon'); - } - } else { - // If not in upcoming + live and in the future (started streaming a bit early) - // $FlowFixMe - if (isLivestreamActive && moment.unix(claim.value.release_time).isAfter()) { - return __('Streaming Now'); - } - } - return ; - }; - return (
  • - {isLivestream && } + {isLivestream && } {!isLivestream && }
  • diff --git a/ui/component/fileSubtitle/view.jsx b/ui/component/fileSubtitle/view.jsx index 54acdabdc..ab10ac7cd 100644 --- a/ui/component/fileSubtitle/view.jsx +++ b/ui/component/fileSubtitle/view.jsx @@ -4,6 +4,7 @@ import DateTime from 'component/dateTime'; import FileViewCount from 'component/fileViewCount'; import FileActions from 'component/fileActions'; import ClaimPreviewReset from 'component/claimPreviewReset'; +import LivestreamDateTime from 'component/livestreamDateTime'; type Props = { uri: string, @@ -13,13 +14,12 @@ type Props = { function FileSubtitle(props: Props) { const { uri, livestream = false, isLive = false } = props; - const showDateTime = !livestream || (livestream && !isLive); return ( <>
    - {showDateTime && } - + {livestream && isLive && } + {!livestream && }
    diff --git a/ui/component/livestreamDateTime/index.js b/ui/component/livestreamDateTime/index.js new file mode 100644 index 000000000..b64475699 --- /dev/null +++ b/ui/component/livestreamDateTime/index.js @@ -0,0 +1,14 @@ +import { connect } from 'react-redux'; +import { makeSelectClaimForUri } from 'redux/selectors/claims'; +import LivestreamDateTime from './view'; +import { selectActiveLivestreamForUri } from 'redux/selectors/livestream'; + +const select = (state, props) => { + const claim = props.uri && makeSelectClaimForUri(props.uri)(state); + return { + claim, + activeLivestream: selectActiveLivestreamForUri(state, props.uri), + }; +}; + +export default connect(select)(LivestreamDateTime); diff --git a/ui/component/livestreamDateTime/view.jsx b/ui/component/livestreamDateTime/view.jsx new file mode 100644 index 000000000..c06d4071f --- /dev/null +++ b/ui/component/livestreamDateTime/view.jsx @@ -0,0 +1,37 @@ +// @flow +import React from 'react'; +import DateTime from 'component/dateTime'; +import { LIVESTREAM_STARTED_RECENTLY_BUFFER } from 'constants/livestream'; +import moment from 'moment'; + +type Props = { + uri: string, + claim: any, + activeLivestream: any, +}; + +const LivestreamDateTime = (props: Props) => { + const { uri, claim, activeLivestream } = props; + + if (activeLivestream) { + return ( + + {__('Started')} + + ); + } + if ( + moment + .unix(claim.value.release_time) + .isBetween(moment().subtract(LIVESTREAM_STARTED_RECENTLY_BUFFER, 'minutes'), moment()) + ) { + return __('Starting Soon'); + } + return ( + + {__('Live')} + + ); +}; + +export default LivestreamDateTime; diff --git a/ui/component/livestreamLayout/view.jsx b/ui/component/livestreamLayout/view.jsx index ce8b7dce7..c01719cc6 100644 --- a/ui/component/livestreamLayout/view.jsx +++ b/ui/component/livestreamLayout/view.jsx @@ -80,7 +80,12 @@ export default function LivestreamLayout(props: Props) {
    )} - {activeStreamUri && } + {activeStreamUri && ( + + )} {isMobile && !hideComments && } diff --git a/ui/component/livestreamLink/view.jsx b/ui/component/livestreamLink/view.jsx index f111112db..75e08a955 100644 --- a/ui/component/livestreamLink/view.jsx +++ b/ui/component/livestreamLink/view.jsx @@ -7,17 +7,18 @@ import { useHistory } from 'react-router'; import { formatLbryUrlForWeb } from 'util/url'; type Props = { + title?: string, claimUri: string, }; export default function LivestreamLink(props: Props) { - const { claimUri } = props; + const { claimUri, title = null } = props; const { push } = useHistory(); const element = (props: { children: any }) => ( { push(formatLbryUrlForWeb(claimUri)); }} diff --git a/ui/component/scheduledStreams/view.jsx b/ui/component/scheduledStreams/view.jsx index 88e1f1408..231c7decb 100644 --- a/ui/component/scheduledStreams/view.jsx +++ b/ui/component/scheduledStreams/view.jsx @@ -8,7 +8,6 @@ import { useIsMediumScreen, useIsLargeScreen } from 'effects/use-screensize'; import ClaimListDiscover from 'component/claimListDiscover'; import Button from 'component/button'; import { LIVESTREAM_UPCOMING_BUFFER } from 'constants/livestream'; -import ClaimListDiscoverContext from 'component/claimListDiscover/context'; type Props = { channelIds: Array, @@ -39,56 +38,54 @@ const ScheduledStreams = (props: Props) => { }; return ( - -
    - ${moment().subtract(LIVESTREAM_UPCOMING_BUFFER, 'minutes').startOf('minute').unix()}`} - hideAdvancedFilter - hideFilters - infiniteScroll={false} - showNoSourceClaims - hideLayoutButton - header={__('Upcoming Livestreams')} - maxClaimRender={upcomingMax} - excludeUris={liveUris} - loadedCallback={loadedCallback} - /> - {totalUpcomingLivestreams > upcomingMax && !showAllUpcoming && ( -
    -
    - )} - {showAllUpcoming && ( -
    -
    - )} -
    -
    +
    + ${moment().subtract(LIVESTREAM_UPCOMING_BUFFER, 'minutes').startOf('minute').unix()}`} + hideAdvancedFilter + hideFilters + infiniteScroll={false} + showNoSourceClaims + hideLayoutButton + header={__('Upcoming Livestreams')} + maxClaimRender={upcomingMax} + excludeUris={liveUris} + loadedCallback={loadedCallback} + /> + {totalUpcomingLivestreams > upcomingMax && !showAllUpcoming && ( +
    +
    + )} + {showAllUpcoming && ( +
    +
    + )} +
    ); }; diff --git a/ui/page/livestream/index.js b/ui/page/livestream/index.js index dd6dcc70a..5d3ce19b8 100644 --- a/ui/page/livestream/index.js +++ b/ui/page/livestream/index.js @@ -6,16 +6,20 @@ import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { DISABLE_COMMENTS_TAG } from 'constants/tags'; import { doCommentSocketConnect, doCommentSocketDisconnect } from 'redux/actions/websocket'; import { getChannelIdFromClaim } from 'util/claim'; -import { selectCurrentChannelStatus } from 'redux/selectors/livestream'; +import { selectActiveLivestreamForChannel, selectActiveLivestreamInitialized } from 'redux/selectors/livestream'; import { doFetchActiveLivestream } from 'redux/actions/livestream'; import LivestreamPage from './view'; -const select = (state, props) => ({ - isAuthenticated: selectUserVerifiedEmail(state), - channelClaimId: getChannelIdFromClaim(selectClaimForUri(state, props.uri)), - chatDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state), - currentChannelStatus: selectCurrentChannelStatus(state), -}); +const select = (state, props) => { + const channelClaimId = getChannelIdFromClaim(selectClaimForUri(state, props.uri)); + return { + isAuthenticated: selectUserVerifiedEmail(state), + channelClaimId, + chatDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state), + activeLivestreamForChannel: selectActiveLivestreamForChannel(state, channelClaimId), + activeLivestreamInitialized: selectActiveLivestreamInitialized(state), + }; +}; const perform = { doSetPlayingUri, diff --git a/ui/page/livestream/view.jsx b/ui/page/livestream/view.jsx index 3c19fd7d3..ca5fc204d 100644 --- a/ui/page/livestream/view.jsx +++ b/ui/page/livestream/view.jsx @@ -20,7 +20,8 @@ type Props = { doCommentSocketConnect: (string, string) => void, doCommentSocketDisconnect: (string) => void, doFetchActiveLivestream: (string) => void, - currentChannelStatus: LivestreamChannelStatus, + activeLivestreamForChannel: any, + activeLivestreamInitialized: boolean, }; export default function LivestreamPage(props: Props) { @@ -35,7 +36,8 @@ export default function LivestreamPage(props: Props) { doCommentSocketConnect, doCommentSocketDisconnect, doFetchActiveLivestream, - currentChannelStatus, + activeLivestreamForChannel, + activeLivestreamInitialized, } = props; React.useEffect(() => { @@ -58,10 +60,9 @@ export default function LivestreamPage(props: Props) { }; }, [claimId, uri, doCommentSocketConnect, doCommentSocketDisconnect]); - const [isInitialized, setIsInitialized] = React.useState(false); - const [isChannelBroadcasting, setIsChannelBroadcasting] = React.useState(false); - const [isCurrentClaimLive, setIsCurrentClaimLive] = React.useState(false); - + const isInitialized = Boolean(activeLivestreamForChannel) || activeLivestreamInitialized; + const isChannelBroadcasting = Boolean(activeLivestreamForChannel); + const isCurrentClaimLive = isChannelBroadcasting && activeLivestreamForChannel.claimId === claimId; const livestreamChannelId = channelClaimId || ''; // Find out current channels status + active live claim. @@ -71,19 +72,10 @@ export default function LivestreamPage(props: Props) { return () => clearInterval(intervalId); }, [livestreamChannelId, doFetchActiveLivestream]); - React.useEffect(() => { - const initialized = currentChannelStatus.channelId === livestreamChannelId; - setIsInitialized(initialized); - if (initialized) { - setIsChannelBroadcasting(currentChannelStatus.isBroadcasting); - setIsCurrentClaimLive(currentChannelStatus.liveClaim.claimId === claimId); - } - }, [currentChannelStatus, livestreamChannelId, claimId]); - const [activeStreamUri, setActiveStreamUri] = React.useState(false); React.useEffect(() => { - setActiveStreamUri(!isCurrentClaimLive && isChannelBroadcasting ? currentChannelStatus.liveClaim.claimUri : false); + setActiveStreamUri(!isCurrentClaimLive && isChannelBroadcasting ? activeLivestreamForChannel.claimUri : false); }, [isCurrentClaimLive, isChannelBroadcasting]); // eslint-disable-line react-hooks/exhaustive-deps // $FlowFixMe diff --git a/ui/redux/actions/livestream.js b/ui/redux/actions/livestream.js index 7338a313b..7c43bf220 100644 --- a/ui/redux/actions/livestream.js +++ b/ui/redux/actions/livestream.js @@ -165,14 +165,18 @@ export const doFetchActiveLivestream = (channelId: string) => { const liveChannel = await fetchLiveChannel(channelId); const currentlyLiveClaims = await findActiveStreams([channelId], ['release_time'], liveChannel, dispatch); const liveClaim = currentlyLiveClaims[channelId]; + + liveChannel[channelId].claimId = liveClaim.stream.claim_id; + liveChannel[channelId].claimUri = liveClaim.stream.canonical_url; + dispatch({ type: ACTIONS.FETCH_ACTIVE_LIVESTREAM_COMPLETED, - data: { liveClaim: { claimId: liveClaim.stream.claim_id, claimUri: liveClaim.stream.canonical_url } }, + data: { + ...liveChannel, + }, }); } catch (err) { - dispatch({ type: ACTIONS.FETCH_ACTIVE_LIVESTREAM_FAILED }); - } finally { - dispatch({ type: ACTIONS.FETCH_ACTIVE_LIVESTREAM_FINISHED, data: { channelId } }); + dispatch({ type: ACTIONS.FETCH_ACTIVE_LIVESTREAM_FAILED, data: { channelId } }); } }; }; diff --git a/ui/redux/reducers/livestream.js b/ui/redux/reducers/livestream.js index 6ffeecc91..ebd78b476 100644 --- a/ui/redux/reducers/livestream.js +++ b/ui/redux/reducers/livestream.js @@ -3,15 +3,6 @@ import * as ACTIONS from 'constants/action_types'; import { handleActions } from 'util/redux-utils'; -const currentChannelStatus: LivestreamChannelStatus = { - channelId: null, - isBroadcasting: false, - liveClaim: { - claimId: null, - claimUri: null, - }, -}; - const defaultState: LivestreamState = { fetchingById: {}, viewersById: {}, @@ -19,10 +10,7 @@ const defaultState: LivestreamState = { activeLivestreams: null, activeLivestreamsLastFetchedDate: 0, activeLivestreamsLastFetchedOptions: {}, - - currentChannelStatus: { - ...currentChannelStatus, - }, + activeLivestreamInitialized: false, }; export default handleActions( @@ -70,24 +58,14 @@ export default handleActions( activeLivestreamsLastFetchedOptions, }; }, - [ACTIONS.FETCH_ACTIVE_LIVESTREAM_COMPLETED]: (state: LivestreamState, action: any) => { - const currentChannelStatus = Object.assign({}, state.currentChannelStatus, { - isBroadcasting: true, - liveClaim: action.data.liveClaim, - }); - return { ...state, currentChannelStatus }; + const activeLivestreams = Object.assign({}, state.activeLivestreams || {}, action.data); + return { ...state, activeLivestreams, activeLivestreamInitialized: true }; }, - [ACTIONS.FETCH_ACTIVE_LIVESTREAM_FAILED]: (state: LivestreamState) => { - const currentChannelStatus = Object.assign({}, state.currentChannelStatus, { - isBroadcasting: false, - liveClaim: { claimId: null, claimUri: null }, - }); - return { ...state, currentChannelStatus }; - }, - [ACTIONS.FETCH_ACTIVE_LIVESTREAM_FINISHED]: (state: LivestreamState, action: any) => { - const currentChannelStatus = Object.assign({}, state.currentChannelStatus, { channelId: action.data.channelId }); - return { ...state, currentChannelStatus }; + [ACTIONS.FETCH_ACTIVE_LIVESTREAM_FAILED]: (state: LivestreamState, action: any) => { + const activeLivestreams = state.activeLivestreams; + if (activeLivestreams) delete activeLivestreams[action.data.channelId]; + return { ...state, activeLivestreams: Object.assign({}, activeLivestreams), activeLivestreamInitialized: true }; }, }, defaultState diff --git a/ui/redux/selectors/livestream.js b/ui/redux/selectors/livestream.js index e22775c97..f8774134b 100644 --- a/ui/redux/selectors/livestream.js +++ b/ui/redux/selectors/livestream.js @@ -62,6 +62,31 @@ export const selectIsActiveLivestreamForUri = createCachedSelector( } )((state, uri) => String(uri)); +export const selectActiveLivestreamForUri = createCachedSelector( + (state, uri) => uri, + selectActiveLivestreams, + (uri, activeLivestreams) => { + if (!uri || !activeLivestreams) { + return null; + } + + const activeLivestreamValues = Object.values(activeLivestreams); + // $FlowFixMe - unable to resolve claimUri + return activeLivestreamValues.find((v) => v.claimUri === uri) || null; + } +)((state, uri) => String(uri)); + +export const selectActiveLivestreamForChannel = createCachedSelector( + (state, channelId) => channelId, + selectActiveLivestreams, + (channelId, activeLivestreams) => { + if (!channelId || !activeLivestreams) { + return null; + } + return activeLivestreams[channelId] || null; + } +)((state, channelId) => String(channelId)); + export const selectFetchingActiveLivestreams = (state: State) => selectState(state).fetchingActiveLivestreams; -export const selectCurrentChannelStatus = (state: State) => selectState(state).currentChannelStatus; +export const selectActiveLivestreamInitialized = (state: State) => selectState(state).activeLivestreamInitialized;