Refactor and fix websocket connection behavior
This commit is contained in:
parent
05ec4e5fbe
commit
129a5819d9
10 changed files with 109 additions and 52 deletions
9
flow-typed/livestream.js
vendored
9
flow-typed/livestream.js
vendored
|
@ -18,7 +18,7 @@ declare type LivestreamReplayItem = {
|
|||
uploadedAt: string, // Date?
|
||||
},
|
||||
id: string,
|
||||
}
|
||||
};
|
||||
declare type LivestreamReplayData = Array<LivestreamReplayItem>;
|
||||
|
||||
declare type LivestreamState = {
|
||||
|
@ -29,7 +29,8 @@ declare type LivestreamState = {
|
|||
activeLivestreamsLastFetchedDate: number,
|
||||
activeLivestreamsLastFetchedOptions: {},
|
||||
activeLivestreamInitialized: boolean,
|
||||
}
|
||||
socketConnectionById: { [id: string]: { connected: ?boolean, sub_category: ?string } },
|
||||
};
|
||||
|
||||
declare type LivestreamInfo = {
|
||||
[/* creatorId */ string]: {
|
||||
|
@ -38,5 +39,5 @@ declare type LivestreamInfo = {
|
|||
creatorId: string,
|
||||
claimId: string,
|
||||
claimUri: string,
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -19,11 +19,11 @@ import {
|
|||
} from 'redux/selectors/content';
|
||||
import { selectClientSetting } from 'redux/selectors/settings';
|
||||
import { selectCostInfoForUri } from 'lbryinc';
|
||||
import { doUriInitiatePlay, doSetPlayingUri } from 'redux/actions/content';
|
||||
import { doUriInitiatePlay, doSetPlayingUri, doClearPlayingUri } from 'redux/actions/content';
|
||||
import { doFetchRecommendedContent } from 'redux/actions/search';
|
||||
import { withRouter } from 'react-router';
|
||||
import { selectAppDrawerOpen } from 'redux/selectors/app';
|
||||
import { selectIsActiveLivestreamForUri, selectCommentSocketConnected } from 'redux/selectors/livestream';
|
||||
import { selectIsActiveLivestreamForUri, selectSocketConnectionForId } from 'redux/selectors/livestream';
|
||||
import { doCommentSocketConnect, doCommentSocketDisconnect } from 'redux/actions/websocket';
|
||||
import { isStreamPlaceholderClaim, getVideoClaimAspectRatio } from 'util/claim';
|
||||
import FileRenderFloating from './view';
|
||||
|
@ -57,7 +57,7 @@ const select = (state, props) => {
|
|||
collectionId,
|
||||
isCurrentClaimLive: selectIsActiveLivestreamForUri(state, uri),
|
||||
videoAspectRatio: getVideoClaimAspectRatio(claim),
|
||||
socketConnected: selectCommentSocketConnected(state),
|
||||
socketConnection: selectSocketConnectionForId(state, claimId),
|
||||
isLivestreamClaim: isStreamPlaceholderClaim(claim),
|
||||
geoRestriction: selectGeoRestrictionForUri(state, uri),
|
||||
appDrawerOpen: selectAppDrawerOpen(state),
|
||||
|
@ -70,6 +70,7 @@ const perform = {
|
|||
doSetPlayingUri,
|
||||
doCommentSocketConnect,
|
||||
doCommentSocketDisconnect,
|
||||
doClearPlayingUri,
|
||||
};
|
||||
|
||||
export default withRouter(connect(select, perform)(FileRenderFloating));
|
||||
|
|
|
@ -71,12 +71,13 @@ type Props = {
|
|||
doSetPlayingUri: ({ uri?: ?string }) => void,
|
||||
isCurrentClaimLive?: boolean,
|
||||
videoAspectRatio: number,
|
||||
socketConnected: boolean,
|
||||
socketConnection: { connected: ?boolean },
|
||||
isLivestreamClaim: boolean,
|
||||
geoRestriction: ?GeoRestriction,
|
||||
appDrawerOpen: boolean,
|
||||
doCommentSocketConnect: (string, string, string) => void,
|
||||
doCommentSocketDisconnect: (string, string) => void,
|
||||
doClearPlayingUri: () => void,
|
||||
};
|
||||
|
||||
export default function FileRenderFloating(props: Props) {
|
||||
|
@ -97,7 +98,7 @@ export default function FileRenderFloating(props: Props) {
|
|||
claimWasPurchased,
|
||||
nextListUri,
|
||||
previousListUri,
|
||||
socketConnected,
|
||||
socketConnection,
|
||||
isLivestreamClaim,
|
||||
doFetchRecommendedContent,
|
||||
doUriInitiatePlay,
|
||||
|
@ -108,6 +109,7 @@ export default function FileRenderFloating(props: Props) {
|
|||
appDrawerOpen,
|
||||
doCommentSocketConnect,
|
||||
doCommentSocketDisconnect,
|
||||
doClearPlayingUri,
|
||||
} = props;
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
@ -235,14 +237,25 @@ export default function FileRenderFloating(props: Props) {
|
|||
|
||||
// Only connect if not yet connected, so for example clicked on an embed instead of accessing
|
||||
// from the Livestream page
|
||||
if (!socketConnected) doCommentSocketConnect(uri, channelName, claimId);
|
||||
if (!socketConnection?.connected) {
|
||||
doCommentSocketConnect(uri, channelName, claimId);
|
||||
}
|
||||
|
||||
// This will be used to disconnect for every case, since this is the main player component
|
||||
return () => doCommentSocketDisconnect(claimId, channelName);
|
||||
|
||||
// only listen to socketConnected on initial mount
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [channelUrl, claimId, doCommentSocketConnect, doCommentSocketDisconnect, isCurrentClaimLive, uri]);
|
||||
return () => {
|
||||
if (socketConnection?.connected) {
|
||||
doCommentSocketDisconnect(claimId, channelName);
|
||||
}
|
||||
};
|
||||
}, [
|
||||
channelUrl,
|
||||
claimId,
|
||||
doCommentSocketConnect,
|
||||
doCommentSocketDisconnect,
|
||||
isCurrentClaimLive,
|
||||
socketConnection,
|
||||
uri,
|
||||
]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (playingPrimaryUri || playingUrl || noPlayerHeight) {
|
||||
|
@ -310,6 +323,12 @@ export default function FileRenderFloating(props: Props) {
|
|||
};
|
||||
}, [playingUrl]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!primaryUri && !floatingPlayerEnabled && playingUrl && !playingUriSource) {
|
||||
doClearPlayingUri();
|
||||
}
|
||||
}, [doClearPlayingUri, floatingPlayerEnabled, playingUriSource, playingUrl, primaryUri]);
|
||||
|
||||
if (
|
||||
geoRestriction ||
|
||||
!isPlayable ||
|
||||
|
|
|
@ -427,7 +427,7 @@ export const COMMENT_FETCH_SETTINGS_COMPLETED = 'COMMENT_FETCH_SETTINGS_COMPLETE
|
|||
export const COMMENT_FETCH_BLOCKED_WORDS_STARTED = 'COMMENT_FETCH_BLOCKED_WORDS_STARTED';
|
||||
export const COMMENT_FETCH_BLOCKED_WORDS_FAILED = 'COMMENT_FETCH_BLOCKED_WORDS_FAILED';
|
||||
export const COMMENT_FETCH_BLOCKED_WORDS_COMPLETED = 'COMMENT_FETCH_BLOCKED_WORDS_COMPLETED';
|
||||
export const COMMENT_SOCKET_CONNECTED = 'COMMENT_SOCKET_CONNECTED';
|
||||
export const SOCKET_CONNECTED_BY_ID = 'SOCKET_CONNECTED_BY_ID';
|
||||
export const COMMENT_RECEIVED = 'COMMENT_RECEIVED';
|
||||
export const COMMENT_SUPER_CHAT_LIST_STARTED = 'COMMENT_SUPER_CHAT_LIST_STARTED';
|
||||
export const COMMENT_SUPER_CHAT_LIST_COMPLETED = 'COMMENT_SUPER_CHAT_LIST_COMPLETED';
|
||||
|
|
|
@ -4,13 +4,14 @@ import { doSetPrimaryUri } from 'redux/actions/content';
|
|||
import { doUserSetReferrer } from 'redux/actions/user';
|
||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||
import { DISABLE_COMMENTS_TAG } from 'constants/tags';
|
||||
import { doCommentSocketConnect } from 'redux/actions/websocket';
|
||||
import { doCommentSocketConnect, doCommentSocketDisconnect } from 'redux/actions/websocket';
|
||||
import { getChannelIdFromClaim } from 'util/claim';
|
||||
import {
|
||||
selectActiveLivestreamForChannel,
|
||||
selectActiveLivestreamInitialized,
|
||||
selectCommentSocketConnected,
|
||||
selectSocketConnectionForId,
|
||||
} from 'redux/selectors/livestream';
|
||||
import { selectIsUriCurrentlyPlaying } from 'redux/selectors/content';
|
||||
import { doFetchChannelLiveStatus } from 'redux/actions/livestream';
|
||||
import LivestreamPage from './view';
|
||||
|
||||
|
@ -18,7 +19,7 @@ const select = (state, props) => {
|
|||
const { uri } = props;
|
||||
|
||||
const claim = selectClaimForUri(state, uri);
|
||||
const { canonical_url } = claim || {};
|
||||
const { claim_id: claimId, canonical_url } = claim || {};
|
||||
const channelClaimId = getChannelIdFromClaim(claim);
|
||||
|
||||
return {
|
||||
|
@ -28,7 +29,8 @@ const select = (state, props) => {
|
|||
chatDisabled: makeSelectTagInClaimOrChannelForUri(uri, DISABLE_COMMENTS_TAG)(state),
|
||||
activeLivestreamForChannel: selectActiveLivestreamForChannel(state, channelClaimId),
|
||||
activeLivestreamInitialized: selectActiveLivestreamInitialized(state),
|
||||
socketConnected: selectCommentSocketConnected(state),
|
||||
socketConnection: selectSocketConnectionForId(state, claimId),
|
||||
isStreamPlaying: selectIsUriCurrentlyPlaying(state, uri),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -36,6 +38,7 @@ const perform = {
|
|||
doSetPrimaryUri,
|
||||
doUserSetReferrer,
|
||||
doCommentSocketConnect,
|
||||
doCommentSocketDisconnect,
|
||||
doFetchChannelLiveStatus,
|
||||
};
|
||||
|
||||
|
|
|
@ -20,9 +20,11 @@ type Props = {
|
|||
claim: StreamClaim,
|
||||
isAuthenticated: boolean,
|
||||
uri: string,
|
||||
socketConnected: boolean,
|
||||
socketConnection: { connected: ?boolean },
|
||||
isStreamPlaying: boolean,
|
||||
doSetPrimaryUri: (uri: ?string) => void,
|
||||
doCommentSocketConnect: (string, string, string) => void,
|
||||
doCommentSocketConnect: (uri: string, channelName: string, claimId: string) => void,
|
||||
doCommentSocketDisconnect: (claimId: string, channelName: string) => void,
|
||||
doFetchChannelLiveStatus: (string) => void,
|
||||
doUserSetReferrer: (string) => void,
|
||||
};
|
||||
|
@ -38,15 +40,19 @@ export default function LivestreamPage(props: Props) {
|
|||
claim,
|
||||
isAuthenticated,
|
||||
uri,
|
||||
socketConnected,
|
||||
socketConnection,
|
||||
isStreamPlaying,
|
||||
doSetPrimaryUri,
|
||||
doCommentSocketConnect,
|
||||
doCommentSocketDisconnect,
|
||||
doFetchChannelLiveStatus,
|
||||
doUserSetReferrer,
|
||||
} = props;
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const streamPlayingRef = React.useRef();
|
||||
|
||||
const [activeStreamUri, setActiveStreamUri] = React.useState(false);
|
||||
const [showLivestream, setShowLivestream] = React.useState(false);
|
||||
const [showScheduledInfo, setShowScheduledInfo] = React.useState(false);
|
||||
|
@ -73,18 +79,33 @@ export default function LivestreamPage(props: Props) {
|
|||
// On livestream page, only connect, fileRenderFloating will handle disconnect.
|
||||
// (either by leaving page with floating player off, or by closing the player)
|
||||
React.useEffect(() => {
|
||||
if (!claim || socketConnected) return;
|
||||
|
||||
const { claim_id: claimId, signing_channel: channelClaim } = claim;
|
||||
const channelName = channelClaim && formatLbryChannelName(channelUrl);
|
||||
|
||||
if (claimId && channelName) {
|
||||
if (claimId && channelName && !socketConnection?.connected) {
|
||||
doCommentSocketConnect(uri, channelName, claimId);
|
||||
}
|
||||
|
||||
// only listen to socketConnected on initial mount
|
||||
// willAutoplay mount only
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [channelUrl, claim, doCommentSocketConnect, uri]);
|
||||
}, [channelUrl, claim, doCommentSocketConnect, doCommentSocketDisconnect, socketConnection, uri]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// use for unmount case without triggering render
|
||||
streamPlayingRef.current = isStreamPlaying;
|
||||
}, [isStreamPlaying]);
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
if (!streamPlayingRef.current) {
|
||||
const { claim_id: claimId, signing_channel: channelClaim } = claim;
|
||||
const channelName = channelClaim && formatLbryChannelName(channelUrl);
|
||||
|
||||
if (claimId && channelName) doCommentSocketDisconnect(claimId, channelName);
|
||||
}
|
||||
};
|
||||
// only on unmount -> leave page
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const claimReleaseStartingSoonStatic = () =>
|
||||
releaseTime.isBetween(moment(), moment().add(LIVESTREAM_STARTS_SOON_BUFFER, 'minutes'));
|
||||
|
|
|
@ -154,31 +154,30 @@ export const doCommentSocketConnect = (uri, channelName, claimId, subCategory) =
|
|||
dispatch(doFetchChannelLiveStatus(channel_id));
|
||||
}
|
||||
},
|
||||
'comment'
|
||||
`${subCategory || COMMENT_WS_SUBCATEGORIES.VIEWER} comment`
|
||||
);
|
||||
|
||||
dispatch(doSetSocketConnected(true));
|
||||
dispatch(doSetSocketConnection(true, claimId, subCategory || COMMENT_WS_SUBCATEGORIES.VIEWER));
|
||||
};
|
||||
|
||||
export const doCommentSocketDisconnect = (claimId, channelName) => (dispatch) => {
|
||||
const url = getCommentSocketUrl(claimId, channelName);
|
||||
export const doCommentSocketDisconnect = (claimId, channelName, subCategory) => (dispatch) => {
|
||||
const url =
|
||||
subCategory === COMMENT_WS_SUBCATEGORIES.COMMENTER
|
||||
? getCommentSocketUrlForCommenter(claimId, channelName)
|
||||
: getCommentSocketUrl(claimId, channelName);
|
||||
|
||||
dispatch(doSocketDisconnect(url));
|
||||
dispatch(doSetSocketConnected(false));
|
||||
dispatch(doSetSocketConnection(false, claimId, subCategory));
|
||||
};
|
||||
|
||||
export const doCommentSocketConnectAsCommenter = (uri, channelName, claimId) => (dispatch) => {
|
||||
export const doCommentSocketConnectAsCommenter = (uri, channelName, claimId) => (dispatch) =>
|
||||
dispatch(doCommentSocketConnect(uri, channelName, claimId, COMMENT_WS_SUBCATEGORIES.COMMENTER));
|
||||
};
|
||||
|
||||
export const doCommentSocketDisconnectAsCommenter = (claimId, channelName) => (dispatch) => {
|
||||
const url = getCommentSocketUrlForCommenter(claimId, channelName);
|
||||
export const doCommentSocketDisconnectAsCommenter = (claimId, channelName) => (dispatch) =>
|
||||
dispatch(doCommentSocketDisconnect(claimId, channelName, COMMENT_WS_SUBCATEGORIES.COMMENTER));
|
||||
|
||||
dispatch(doSocketDisconnect(url));
|
||||
};
|
||||
|
||||
export const doSetSocketConnected = (connected) => (dispatch) =>
|
||||
export const doSetSocketConnection = (connected, id, subCategory) => (dispatch) =>
|
||||
dispatch({
|
||||
type: ACTIONS.COMMENT_SOCKET_CONNECTED,
|
||||
data: { connected },
|
||||
type: ACTIONS.SOCKET_CONNECTED_BY_ID,
|
||||
data: { connected, sub_category: subCategory, id },
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ const defaultState: LivestreamState = {
|
|||
activeLivestreamsLastFetchedDate: 0,
|
||||
activeLivestreamsLastFetchedOptions: {},
|
||||
activeLivestreamInitialized: false,
|
||||
commentSocketConnected: false,
|
||||
socketConnectionById: {},
|
||||
};
|
||||
|
||||
function updateViewersById(activeLivestreams, originalState) {
|
||||
|
@ -90,10 +90,14 @@ export default handleActions(
|
|||
if (activeLivestreams) delete activeLivestreams[action.data.channelId];
|
||||
return { ...state, activeLivestreams: Object.assign({}, activeLivestreams), activeLivestreamInitialized: true };
|
||||
},
|
||||
[ACTIONS.COMMENT_SOCKET_CONNECTED]: (state: CommentsState, action: any) => ({
|
||||
...state,
|
||||
commentSocketConnected: action.data.connected,
|
||||
}),
|
||||
[ACTIONS.SOCKET_CONNECTED_BY_ID]: (state: LivestreamState, action: any) => {
|
||||
const { connected, sub_category, id: claimId } = action.data;
|
||||
|
||||
const socketConnectionById = Object.assign({}, state.socketConnectionById);
|
||||
socketConnectionById[claimId] = { connected, sub_category };
|
||||
|
||||
return { ...state, socketConnectionById };
|
||||
},
|
||||
},
|
||||
defaultState
|
||||
);
|
||||
|
|
|
@ -30,8 +30,11 @@ export const selectListShuffle = (state: State) => selectState(state).shuffleLis
|
|||
export const makeSelectIsPlaying = (uri: string) =>
|
||||
createSelector(selectPrimaryUri, (primaryUri) => primaryUri === uri);
|
||||
|
||||
export const makeSelectIsUriCurrentlyPlaying = (uri: string) =>
|
||||
createSelector(selectPlayingUri, (playingUri) => playingUri.uri === uri);
|
||||
export const selectIsUriCurrentlyPlaying = createSelector(
|
||||
(state, uri) => uri,
|
||||
selectPlayingUri,
|
||||
(uri, playingUri) => Boolean(playingUri.uri === uri)
|
||||
);
|
||||
|
||||
export const makeSelectIsPlayerFloating = (location: UrlLocation) =>
|
||||
createSelector(selectPrimaryUri, selectPlayingUri, (primaryUri, playingUri) => {
|
||||
|
|
|
@ -12,7 +12,13 @@ export const selectViewersById = (state: State) => selectState(state).viewersByI
|
|||
export const selectActiveLivestreams = (state: State) => selectState(state).activeLivestreams;
|
||||
export const selectFetchingActiveLivestreams = (state: State) => selectState(state).fetchingActiveLivestreams;
|
||||
export const selectActiveLivestreamInitialized = (state: State) => selectState(state).activeLivestreamInitialized;
|
||||
export const selectCommentSocketConnected = (state: State) => selectState(state).commentSocketConnected;
|
||||
export const selectSocketConnectionById = (state: State) => selectState(state).socketConnectionById;
|
||||
|
||||
export const selectSocketConnectionForId = createSelector(
|
||||
(state, claimId) => claimId,
|
||||
selectSocketConnectionById,
|
||||
(claimId, byId) => claimId && byId[claimId]
|
||||
);
|
||||
|
||||
// select non-pending claims without sources for given channel
|
||||
export const makeSelectLivestreamsForChannelId = (channelId: string) =>
|
||||
|
|
Loading…
Reference in a new issue