diff --git a/ui/component/channelContent/index.js b/ui/component/channelContent/index.js index 76f6c715d..4c2f287e1 100644 --- a/ui/component/channelContent/index.js +++ b/ui/component/channelContent/index.js @@ -13,7 +13,7 @@ import { makeSelectChannelIsMuted } from 'redux/selectors/blocked'; 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 { doFetchChannelLiveStatus } from 'redux/actions/livestream'; import { selectActiveLivestreamForChannel, selectActiveLivestreamInitialized } from 'redux/selectors/livestream'; import { getChannelIdFromClaim } from 'util/claim'; import ChannelContent from './view'; @@ -42,7 +42,7 @@ const select = (state, props) => { const perform = (dispatch) => ({ doResolveUris: (uris, returnCachedUris) => dispatch(doResolveUris(uris, returnCachedUris)), - doFetchActiveLivestream: (channelID) => dispatch(doFetchActiveLivestream(channelID)), + doFetchChannelLiveStatus: (channelID) => dispatch(doFetchChannelLiveStatus(channelID)), }); export default withRouter(connect(select, perform)(ChannelContent)); diff --git a/ui/component/channelContent/view.jsx b/ui/component/channelContent/view.jsx index b865cebf7..be12f66b9 100644 --- a/ui/component/channelContent/view.jsx +++ b/ui/component/channelContent/view.jsx @@ -37,7 +37,7 @@ type Props = { doResolveUris: (Array, boolean) => void, claimType: string, empty?: string, - doFetchActiveLivestream: (string) => void, + doFetchChannelLiveStatus: (string) => void, activeLivestreamForChannel: any, activeLivestreamInitialized: boolean, }; @@ -59,7 +59,7 @@ function ChannelContent(props: Props) { doResolveUris, claimType, empty, - doFetchActiveLivestream, + doFetchChannelLiveStatus, activeLivestreamForChannel, activeLivestreamInitialized, } = props; @@ -126,10 +126,10 @@ function ChannelContent(props: Props) { // Find out current channels status + active live claim. React.useEffect(() => { - doFetchActiveLivestream(claimId); - const intervalId = setInterval(() => doFetchActiveLivestream(claimId), 30000); + doFetchChannelLiveStatus(claimId); + const intervalId = setInterval(() => doFetchChannelLiveStatus(claimId), 30000); return () => clearInterval(intervalId); - }, [claimId, doFetchActiveLivestream]); + }, [claimId, doFetchChannelLiveStatus]); const showScheduledLiveStreams = claimType !== 'collection'; // ie. not on the playlist page. diff --git a/ui/constants/action_types.js b/ui/constants/action_types.js index 613e6bc43..82d459f0b 100644 --- a/ui/constants/action_types.js +++ b/ui/constants/action_types.js @@ -477,9 +477,8 @@ export const FETCH_ACTIVE_LIVESTREAMS_FAILED = 'FETCH_ACTIVE_LIVESTREAMS_FAILED' export const FETCH_ACTIVE_LIVESTREAMS_SKIPPED = 'FETCH_ACTIVE_LIVESTREAMS_SKIPPED'; export const FETCH_ACTIVE_LIVESTREAMS_COMPLETED = 'FETCH_ACTIVE_LIVESTREAMS_COMPLETED'; -export const FETCH_ACTIVE_LIVESTREAM_FAILED = 'FETCH_ACTIVE_LIVESTREAMS_FAILED'; -export const FETCH_ACTIVE_LIVESTREAM_COMPLETED = 'FETCH_ACTIVE_LIVESTREAM_COMPLETED'; -export const FETCH_ACTIVE_LIVESTREAM_FINISHED = 'FETCH_ACTIVE_LIVESTREAM_FINISHED'; +export const ADD_CHANNEL_TO_ACTIVE_LIVESTREAMS = 'ADD_CHANNEL_TO_ACTIVE_LIVESTREAMS'; +export const REMOVE_CHANNEL_FROM_ACTIVE_LIVESTREAMS = 'REMOVE_CHANNEL_FROM_ACTIVE_LIVESTREAMS'; // Blacklist export const FETCH_BLACK_LISTED_CONTENT_STARTED = 'FETCH_BLACK_LISTED_CONTENT_STARTED'; diff --git a/ui/page/livestream/index.js b/ui/page/livestream/index.js index 5d3ce19b8..6bc906c0c 100644 --- a/ui/page/livestream/index.js +++ b/ui/page/livestream/index.js @@ -7,7 +7,7 @@ import { DISABLE_COMMENTS_TAG } from 'constants/tags'; import { doCommentSocketConnect, doCommentSocketDisconnect } from 'redux/actions/websocket'; import { getChannelIdFromClaim } from 'util/claim'; import { selectActiveLivestreamForChannel, selectActiveLivestreamInitialized } from 'redux/selectors/livestream'; -import { doFetchActiveLivestream } from 'redux/actions/livestream'; +import { doFetchChannelLiveStatus } from 'redux/actions/livestream'; import LivestreamPage from './view'; const select = (state, props) => { @@ -26,7 +26,7 @@ const perform = { doUserSetReferrer, doCommentSocketConnect, doCommentSocketDisconnect, - doFetchActiveLivestream, + doFetchChannelLiveStatus, }; export default connect(select, perform)(LivestreamPage); diff --git a/ui/page/livestream/view.jsx b/ui/page/livestream/view.jsx index ca5fc204d..b109aa304 100644 --- a/ui/page/livestream/view.jsx +++ b/ui/page/livestream/view.jsx @@ -19,7 +19,7 @@ type Props = { chatDisabled: boolean, doCommentSocketConnect: (string, string) => void, doCommentSocketDisconnect: (string) => void, - doFetchActiveLivestream: (string) => void, + doFetchChannelLiveStatus: (string) => void, activeLivestreamForChannel: any, activeLivestreamInitialized: boolean, }; @@ -35,7 +35,7 @@ export default function LivestreamPage(props: Props) { chatDisabled, doCommentSocketConnect, doCommentSocketDisconnect, - doFetchActiveLivestream, + doFetchChannelLiveStatus, activeLivestreamForChannel, activeLivestreamInitialized, } = props; @@ -67,10 +67,10 @@ export default function LivestreamPage(props: Props) { // Find out current channels status + active live claim. React.useEffect(() => { - doFetchActiveLivestream(livestreamChannelId); - const intervalId = setInterval(() => doFetchActiveLivestream(livestreamChannelId), 30000); + doFetchChannelLiveStatus(livestreamChannelId); + const intervalId = setInterval(() => doFetchChannelLiveStatus(livestreamChannelId), 30000); return () => clearInterval(intervalId); - }, [livestreamChannelId, doFetchActiveLivestream]); + }, [livestreamChannelId, doFetchChannelLiveStatus]); const [activeStreamUri, setActiveStreamUri] = React.useState(false); diff --git a/ui/redux/actions/livestream.js b/ui/redux/actions/livestream.js index d928d610a..fb8ebcc94 100644 --- a/ui/redux/actions/livestream.js +++ b/ui/redux/actions/livestream.js @@ -4,6 +4,16 @@ import { doClaimSearch } from 'redux/actions/claims'; import { LIVESTREAM_LIVE_API, LIVESTREAM_STARTS_SOON_BUFFER } from 'constants/livestream'; import moment from 'moment'; +const LiveStatus = Object.freeze({ + LIVE: 'LIVE', + NOT_LIVE: 'NOT_LIVE', + UNKNOWN: 'UNKNOWN', +}); + +type LiveStatusType = $Keys; + +type LiveChannelStatus = { channelStatus: LiveStatusType, channelData?: LivestreamInfo }; + export const doFetchNoSourceClaims = (channelId: string) => async (dispatch: Dispatch, getState: GetState) => { dispatch({ type: ACTIONS.FETCH_NO_SOURCE_CLAIMS_STARTED, @@ -48,23 +58,22 @@ const transformLivestreamData = (data: Array): LivestreamInfo => { }, {}); }; -const fetchLiveChannels = async () => { +const fetchLiveChannels = async (): Promise => { const response = await fetch(LIVESTREAM_LIVE_API); const json = await response.json(); if (!json.data) throw new Error(); return transformLivestreamData(json.data); }; -const fetchLiveChannel = async (channelId: string) => { - const response = await fetch(`${LIVESTREAM_LIVE_API}/${channelId}`); - let json; +const fetchLiveChannel = async (channelId: string): Promise => { try { - json = await response.json(); + const response = await fetch(`${LIVESTREAM_LIVE_API}/${channelId}`); + const json = await response.json(); + if (json.data?.live === false) return { channelStatus: LiveStatus.NOT_LIVE }; + return { channelStatus: LiveStatus.LIVE, channelData: transformLivestreamData([json.data]) }; } catch { - throw new Error('Error handling live API response'); + return { channelStatus: LiveStatus.UNKNOWN }; } - if (!(json.data && json.data.live)) throw new Error(); - return transformLivestreamData([json.data]); }; const filterUpcomingLiveStreamClaims = (upcomingClaims) => { @@ -164,25 +173,29 @@ const findActiveStreams = async (channelIDs: Array, orderBy: Array { +export const doFetchChannelLiveStatus = (channelId: string) => { return async (dispatch: Dispatch) => { try { - const liveChannel = await fetchLiveChannel(channelId); - const currentlyLiveClaims = await findActiveStreams([channelId], ['release_time'], liveChannel, dispatch); + const { channelStatus, channelData } = await fetchLiveChannel(channelId); + + if (channelStatus === LiveStatus.NOT_LIVE) { + dispatch({ type: ACTIONS.REMOVE_CHANNEL_FROM_ACTIVE_LIVESTREAMS, data: { channelId } }); + return; + } + if (channelStatus === LiveStatus.UNKNOWN) { + return; + } + + const currentlyLiveClaims = await findActiveStreams([channelId], ['release_time'], channelData, 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: { - ...liveChannel, - }, - }); + if (channelData && liveClaim) { + channelData[channelId].claimId = liveClaim.stream.claim_id; + channelData[channelId].claimUri = liveClaim.stream.canonical_url; + dispatch({ type: ACTIONS.ADD_CHANNEL_TO_ACTIVE_LIVESTREAMS, data: { ...channelData } }); + } } catch (err) { - if (err.message === 'Error handling live API response') return; - dispatch({ type: ACTIONS.FETCH_ACTIVE_LIVESTREAM_FAILED, data: { channelId } }); + dispatch({ type: ACTIONS.REMOVE_CHANNEL_FROM_ACTIVE_LIVESTREAMS, data: { channelId } }); } }; }; diff --git a/ui/redux/reducers/livestream.js b/ui/redux/reducers/livestream.js index ebd78b476..68ee53b13 100644 --- a/ui/redux/reducers/livestream.js +++ b/ui/redux/reducers/livestream.js @@ -58,11 +58,11 @@ export default handleActions( activeLivestreamsLastFetchedOptions, }; }, - [ACTIONS.FETCH_ACTIVE_LIVESTREAM_COMPLETED]: (state: LivestreamState, action: any) => { + [ACTIONS.ADD_CHANNEL_TO_ACTIVE_LIVESTREAMS]: (state: LivestreamState, action: any) => { const activeLivestreams = Object.assign({}, state.activeLivestreams || {}, action.data); return { ...state, activeLivestreams, activeLivestreamInitialized: true }; }, - [ACTIONS.FETCH_ACTIVE_LIVESTREAM_FAILED]: (state: LivestreamState, action: any) => { + [ACTIONS.REMOVE_CHANNEL_FROM_ACTIVE_LIVESTREAMS]: (state: LivestreamState, action: any) => { const activeLivestreams = state.activeLivestreams; if (activeLivestreams) delete activeLivestreams[action.data.channelId]; return { ...state, activeLivestreams: Object.assign({}, activeLivestreams), activeLivestreamInitialized: true };