From 03e921e6df2c6e95a801eb40d20dafdfa4ffe5a5 Mon Sep 17 00:00:00 2001
From: zeppi <jessopb@gmail.com>
Date: Thu, 17 Jun 2021 14:55:23 -0400
Subject: [PATCH] use commentron for live view counts

---
 flow-typed/livestream.js               |  1 +
 ui/component/fileTitleSection/index.js | 16 +++++++++++-----
 ui/component/fileTitleSection/view.jsx |  6 +++---
 ui/component/fileViewCount/view.jsx    | 17 +++++++++--------
 ui/component/livestreamLayout/view.jsx |  5 ++---
 ui/constants/action_types.js           |  1 +
 ui/page/livestream/view.jsx            |  5 +----
 ui/redux/actions/websocket.js          |  7 +++++++
 ui/redux/reducers/livestream.js        |  7 +++++++
 ui/redux/selectors/livestream.js       |  4 ++++
 10 files changed, 46 insertions(+), 23 deletions(-)

diff --git a/flow-typed/livestream.js b/flow-typed/livestream.js
index be888611e..40ed13f19 100644
--- a/flow-typed/livestream.js
+++ b/flow-typed/livestream.js
@@ -23,4 +23,5 @@ declare type LivestreamReplayData = Array<LivestreamReplayItem>;
 
 declare type LivestreamState = {
   fetchingById: {},
+  viewersById: {},
 }
diff --git a/ui/component/fileTitleSection/index.js b/ui/component/fileTitleSection/index.js
index 21a62b415..a6a79fd5f 100644
--- a/ui/component/fileTitleSection/index.js
+++ b/ui/component/fileTitleSection/index.js
@@ -1,11 +1,17 @@
 import { connect } from 'react-redux';
-import { makeSelectTitleForUri } from 'lbry-redux';
+import { makeSelectTitleForUri, makeSelectClaimForUri } from 'lbry-redux';
 import { makeSelectInsufficientCreditsForUri } from 'redux/selectors/content';
+import { makeSelectViewersForId } from 'redux/selectors/livestream';
 import FileTitleSection from './view';
 
-const select = (state, props) => ({
-  isInsufficientCredits: makeSelectInsufficientCreditsForUri(props.uri)(state),
-  title: makeSelectTitleForUri(props.uri)(state),
-});
+const select = (state, props) => {
+  const claim = makeSelectClaimForUri(props.uri)(state);
+  const viewers = claim && makeSelectViewersForId(claim.claim_id)(state);
+  return {
+    viewers,
+    isInsufficientCredits: makeSelectInsufficientCreditsForUri(props.uri)(state),
+    title: makeSelectTitleForUri(props.uri)(state),
+  };
+};
 
 export default connect(select)(FileTitleSection);
diff --git a/ui/component/fileTitleSection/view.jsx b/ui/component/fileTitleSection/view.jsx
index 75fcfe60d..e2e06e038 100644
--- a/ui/component/fileTitleSection/view.jsx
+++ b/ui/component/fileTitleSection/view.jsx
@@ -21,11 +21,11 @@ type Props = {
   isNsfwBlocked: boolean,
   livestream?: boolean,
   isLive?: boolean,
-  activeViewers?: number,
+  viewers?: number,
 };
 
 function FileTitleSection(props: Props) {
-  const { title, uri, nsfw, isNsfwBlocked, livestream = false, isLive = false, activeViewers } = props;
+  const { title, uri, nsfw, isNsfwBlocked, livestream = false, isLive = false, viewers } = props;
   const [hasAcknowledgedSec, setHasAcknowledgedSec] = usePersistedState('sec-nag', false);
 
   return (
@@ -57,7 +57,7 @@ function FileTitleSection(props: Props) {
         body={
           <React.Fragment>
             <ClaimInsufficientCredits uri={uri} />
-            <FileSubtitle uri={uri} isLive={isLive} livestream={livestream} activeViewers={activeViewers} />
+            <FileSubtitle uri={uri} isLive={isLive} livestream={livestream} activeViewers={viewers} />
           </React.Fragment>
         }
         actions={
diff --git a/ui/component/fileViewCount/view.jsx b/ui/component/fileViewCount/view.jsx
index 41b5db9ab..9881b3f4d 100644
--- a/ui/component/fileViewCount/view.jsx
+++ b/ui/component/fileViewCount/view.jsx
@@ -6,6 +6,7 @@ import HelpLink from 'component/common/help-link';
 type Props = {
   claim: ?StreamClaim,
   fetchViewCount: (string) => void,
+  fetchingViewCount: boolean,
   uri: string,
   viewCount: string,
   livestream?: boolean,
@@ -36,14 +37,14 @@ function FileViewCount(props: Props) {
 
   return (
     <span className="media__subtitle--centered">
-      {activeViewers !== undefined
-        ? __('%viewer_count% currently %viewer_state%', {
-            viewer_count: activeViewers,
-            viewer_state: isLive ? __('watching') : __('waiting'),
-          })
-        : viewCount !== 1
-        ? __('%view_count% views', { view_count: formattedViewCount })
-        : __('1 view')}
+      {isLive &&
+        __('%viewer_count% currently %viewer_state%', {
+          viewer_count: activeViewers === undefined ? '...' : activeViewers,
+          viewer_state: isLive ? __('watching') : __('waiting'),
+        })}
+      {!isLive &&
+        activeViewers === undefined &&
+        (viewCount !== 1 ? __('%view_count% views', { view_count: formattedViewCount }) : __('1 view'))}
       {!SIMPLE_SITE && <HelpLink href="https://lbry.com/faq/views" />}
     </span>
   );
diff --git a/ui/component/livestreamLayout/view.jsx b/ui/component/livestreamLayout/view.jsx
index ff18ddd27..92c44fb71 100644
--- a/ui/component/livestreamLayout/view.jsx
+++ b/ui/component/livestreamLayout/view.jsx
@@ -9,12 +9,11 @@ type Props = {
   uri: string,
   claim: ?StreamClaim,
   isLive: boolean,
-  activeViewers: number,
   chatDisabled: boolean,
 };
 
 export default function LivestreamLayout(props: Props) {
-  const { claim, uri, isLive, activeViewers, chatDisabled } = props;
+  const { claim, uri, isLive, chatDisabled } = props;
   const isMobile = useIsMobile();
 
   if (!claim || !claim.signing_channel) {
@@ -55,7 +54,7 @@ export default function LivestreamLayout(props: Props) {
 
         {isMobile && <LivestreamComments uri={uri} />}
 
-        <FileTitleSection uri={uri} livestream isLive={isLive} activeViewers={activeViewers} />
+        <FileTitleSection uri={uri} livestream isLive={isLive} />
       </div>
     </>
   );
diff --git a/ui/constants/action_types.js b/ui/constants/action_types.js
index f405876bc..fffb684f4 100644
--- a/ui/constants/action_types.js
+++ b/ui/constants/action_types.js
@@ -339,3 +339,4 @@ export const REPORT_CONTENT_FAILED = 'REPORT_CONTENT_FAILED';
 export const FETCH_NO_SOURCE_CLAIMS_STARTED = 'FETCH_NO_SOURCE_CLAIMS_STARTED';
 export const FETCH_NO_SOURCE_CLAIMS_COMPLETED = 'FETCH_NO_SOURCE_CLAIMS_COMPLETED';
 export const FETCH_NO_SOURCE_CLAIMS_FAILED = 'FETCH_NO_SOURCE_CLAIMS_FAILED';
+export const VIEWERS_RECEIVED = 'VIEWERS_RECEIVED';
diff --git a/ui/page/livestream/view.jsx b/ui/page/livestream/view.jsx
index 3e5857295..b6845194a 100644
--- a/ui/page/livestream/view.jsx
+++ b/ui/page/livestream/view.jsx
@@ -19,7 +19,6 @@ type Props = {
 
 export default function LivestreamPage(props: Props) {
   const { uri, claim, doSetPlayingUri, isAuthenticated, doUserSetReferrer, channelClaim, chatDisabled } = props;
-  const [activeViewers, setActiveViewers] = React.useState(0);
   const [isLive, setIsLive] = React.useState(false);
   const livestreamChannelId = channelClaim && channelClaim.signing_channel && channelClaim.signing_channel.claim_id;
   const [hasLivestreamClaim, setHasLivestreamClaim] = React.useState(false);
@@ -66,8 +65,6 @@ export default function LivestreamPage(props: Props) {
             return;
           }
 
-          setActiveViewers(res.data.viewCount);
-
           if (res.data.hasOwnProperty('live')) {
             setIsLive(res.data.live);
           }
@@ -120,7 +117,7 @@ export default function LivestreamPage(props: Props) {
       chatDisabled={chatDisabled}
       rightSide={!chatDisabled && <LivestreamComments uri={uri} />}
     >
-      <LivestreamLayout uri={uri} activeViewers={activeViewers} isLive={isLive} />
+      <LivestreamLayout uri={uri} isLive={isLive} />
     </Page>
   );
 }
diff --git a/ui/redux/actions/websocket.js b/ui/redux/actions/websocket.js
index 34cf7e5f2..c4c6090b9 100644
--- a/ui/redux/actions/websocket.js
+++ b/ui/redux/actions/websocket.js
@@ -102,6 +102,13 @@ export const doCommentSocketConnect = (uri, claimId) => (dispatch) => {
         data: { comment: newComment, claimId, uri },
       });
     }
+    if (response.type === 'viewers') {
+      const connected = response.data.connected;
+      dispatch({
+        type: ACTIONS.VIEWERS_RECEIVED,
+        data: { connected, claimId },
+      });
+    }
   });
 };
 
diff --git a/ui/redux/reducers/livestream.js b/ui/redux/reducers/livestream.js
index ce339e4a3..5a74b0a39 100644
--- a/ui/redux/reducers/livestream.js
+++ b/ui/redux/reducers/livestream.js
@@ -4,6 +4,7 @@ import { handleActions } from 'util/redux-utils';
 
 const defaultState: LivestreamState = {
   fetchingById: {},
+  viewersById: {},
 };
 
 export default handleActions(
@@ -29,6 +30,12 @@ export default handleActions(
 
       return { ...state, fetchingById: newIdsFetching };
     },
+    [ACTIONS.VIEWERS_RECEIVED]: (state: LivestreamState, action: any) => {
+      const { connected, claimId } = action.data;
+      const newViewersById = Object.assign({}, state.viewersById);
+      newViewersById[claimId] = connected;
+      return { ...state, viewersById: newViewersById };
+    },
   },
   defaultState
 );
diff --git a/ui/redux/selectors/livestream.js b/ui/redux/selectors/livestream.js
index 11280b0e2..27ea16ddb 100644
--- a/ui/redux/selectors/livestream.js
+++ b/ui/redux/selectors/livestream.js
@@ -21,10 +21,14 @@ export const makeSelectLivestreamsForChannelId = (channelId: string) =>
   });
 
 export const selectFetchingLivestreams = createSelector(selectState, (state) => state.fetchingById);
+export const selectViewersById = createSelector(selectState, (state) => state.viewersById);
 
 export const makeSelectIsFetchingLivestreams = (channelId: string) =>
   createSelector(selectFetchingLivestreams, (fetchingLivestreams) => Boolean(fetchingLivestreams[channelId]));
 
+export const makeSelectViewersForId = (channelId: string) =>
+  createSelector(selectViewersById, (viewers) => viewers[channelId]);
+
 export const makeSelectPendingLivestreamsForChannelId = (channelId: string) =>
   createSelector(selectPendingClaims, (pendingClaims) => {
     return pendingClaims.filter(