fix isLivestream

channelContent - live

only poll streaming if has livestream claim

check channel for livestream claims every minute

update consts

poll livestream claims on setup page

do not poll livestream claims if live

smoother loading

unmerged redux bump
This commit is contained in:
zeppi 2021-03-25 20:46:15 -04:00
parent 2e87b2fd22
commit f092e8cb7b
10 changed files with 203 additions and 150 deletions

View file

@ -142,7 +142,7 @@
"imagesloaded": "^4.1.4", "imagesloaded": "^4.1.4",
"json-loader": "^0.5.4", "json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git", "lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#c494c92505cd531048de20bc37dc4d192b6ace01", "lbry-redux": "lbryio/lbry-redux#db27091f5add9a6bb91c38471a369fca144fc96f",
"lbryinc": "lbryio/lbryinc#7faea40d87b78ec91b901c62f501499dc4737025", "lbryinc": "lbryio/lbryinc#7faea40d87b78ec91b901c62f501499dc4737025",
"lint-staged": "^7.0.2", "lint-staged": "^7.0.2",
"localforage": "^1.7.1", "localforage": "^1.7.1",

View file

@ -249,6 +249,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
'claim-preview__wrapper--small': type === 'small', 'claim-preview__wrapper--small': type === 'small',
})} })}
> >
<>
{!hideRepostLabel && <ClaimRepostAuthor uri={uri} />} {!hideRepostLabel && <ClaimRepostAuthor uri={uri} />}
<div <div
@ -325,7 +326,9 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
)} )}
{isChannelUri && !channelIsBlocked && !claimIsMine && ( {isChannelUri && !channelIsBlocked && !claimIsMine && (
<SubscribeButton uri={contentUri.startsWith('lbry://') ? contentUri : `lbry://${contentUri}`} /> <SubscribeButton
uri={contentUri.startsWith('lbry://') ? contentUri : `lbry://${contentUri}`}
/>
)} )}
{includeSupportAction && <ClaimSupportButton uri={uri} />} {includeSupportAction && <ClaimSupportButton uri={uri} />}
@ -349,6 +352,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
</div> </div>
</div> </div>
{!hideMenu && <ClaimMenuList uri={uri} />} {!hideMenu && <ClaimMenuList uri={uri} />}
</>
</WrapperElement> </WrapperElement>
); );
}); });

View file

@ -8,7 +8,7 @@ import {
doFileGet, doFileGet,
makeSelectChannelForClaimUri, makeSelectChannelForClaimUri,
makeSelectClaimIsNsfw, makeSelectClaimIsNsfw,
makeSelectClaimHasSource, makeSelectClaimIsStreamPlaceholder,
} from 'lbry-redux'; } from 'lbry-redux';
import { selectMutedChannels } from 'redux/selectors/blocked'; import { selectMutedChannels } from 'redux/selectors/blocked';
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc'; import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
@ -26,7 +26,7 @@ const select = (state, props) => ({
blockedChannelUris: selectMutedChannels(state), blockedChannelUris: selectMutedChannels(state),
showMature: selectShowMatureContent(state), showMature: selectShowMatureContent(state),
isMature: makeSelectClaimIsNsfw(props.uri)(state), isMature: makeSelectClaimIsNsfw(props.uri)(state),
isLivestream: !makeSelectClaimHasSource(props.uri)(state), isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
}); });
const perform = (dispatch) => ({ const perform = (dispatch) => ({

View file

@ -21,6 +21,7 @@ type Props = {
nag?: Node, nag?: Node,
smallTitle?: boolean, smallTitle?: boolean,
onClick?: () => void, onClick?: () => void,
children?: any, // not sure how this works
}; };
export default function Card(props: Props) { export default function Card(props: Props) {
@ -39,6 +40,7 @@ export default function Card(props: Props) {
defaultExpand, defaultExpand,
nag, nag,
onClick, onClick,
children,
} = props; } = props;
const [expanded, setExpanded] = useState(defaultExpand); const [expanded, setExpanded] = useState(defaultExpand);
const expandable = defaultExpand !== undefined; const expandable = defaultExpand !== undefined;
@ -102,6 +104,7 @@ export default function Card(props: Props) {
</div> </div>
)} )}
{actions && <div className="card__main-actions">{actions}</div>} {actions && <div className="card__main-actions">{actions}</div>}
{children && <div className="card__main-actions">{children}</div>}
</> </>
)} )}

View file

@ -64,11 +64,12 @@ export default function LivestreamLink(props: Props) {
return null; return null;
} }
return ( // gonna pass the wrapper in so I don't have to rewrite the dmca/blocking logic in claimPreview.
<Card const element = (props: { children: any }) => (
className="livestream__channel-link" <Card className="livestream__channel-link" title={__('Live stream in progress')}>
title={__('Live stream in progress')} {props.children}
actions={<ClaimPreview uri={livestreamClaim.canonical_url} livestream type="inline" />} </Card>
/>
); );
return <ClaimPreview uri={livestreamClaim.canonical_url} wrapperElement={element} type="inline" />;
} }

View file

@ -49,7 +49,6 @@ import BuyPage from 'page/buy';
import NotificationsPage from 'page/notifications'; import NotificationsPage from 'page/notifications';
import SignInWalletPasswordPage from 'page/signInWalletPassword'; import SignInWalletPasswordPage from 'page/signInWalletPassword';
import YoutubeSyncPage from 'page/youtubeSync'; import YoutubeSyncPage from 'page/youtubeSync';
import LiveStreamPage from 'page/livestream';
import { LINKED_COMMENT_QUERY_PARAM } from 'constants/comment'; import { LINKED_COMMENT_QUERY_PARAM } from 'constants/comment';
import { parseURI, isURIValid } from 'lbry-redux'; import { parseURI, isURIValid } from 'lbry-redux';
@ -286,8 +285,6 @@ function AppRouter(props: Props) {
<Route path={`/$/${PAGES.EMBED}/:claimName`} exact component={EmbedWrapperPage} /> <Route path={`/$/${PAGES.EMBED}/:claimName`} exact component={EmbedWrapperPage} />
<Route path={`/$/${PAGES.EMBED}/:claimName/:claimId`} exact component={EmbedWrapperPage} /> <Route path={`/$/${PAGES.EMBED}/:claimName/:claimId`} exact component={EmbedWrapperPage} />
<Route path={`/$/${PAGES.LIVESTREAM}`} component={LiveStreamPage} />
{/* Below need to go at the end to make sure we don't match any of our pages first */} {/* Below need to go at the end to make sure we don't match any of our pages first */}
<Route path="/:claimName" exact component={ShowPage} /> <Route path="/:claimName" exact component={ShowPage} />
<Route path="/:claimName/:streamName" exact component={ShowPage} /> <Route path="/:claimName/:streamName" exact component={ShowPage} />

View file

@ -9,7 +9,7 @@ import {
SETTINGS, SETTINGS,
makeSelectTagInClaimOrChannelForUri, makeSelectTagInClaimOrChannelForUri,
makeSelectClaimIsMine, makeSelectClaimIsMine,
makeSelectClaimHasSource, makeSelectClaimIsStreamPlaceholder,
} from 'lbry-redux'; } from 'lbry-redux';
import { makeSelectCostInfoForUri, doFetchCostInfoForUri } from 'lbryinc'; import { makeSelectCostInfoForUri, doFetchCostInfoForUri } from 'lbryinc';
import { selectShowMatureContent, makeSelectClientSetting } from 'redux/selectors/settings'; import { selectShowMatureContent, makeSelectClientSetting } from 'redux/selectors/settings';
@ -35,7 +35,7 @@ const select = (state, props) => {
videoTheaterMode: makeSelectClientSetting(SETTINGS.VIDEO_THEATER_MODE)(state), videoTheaterMode: makeSelectClientSetting(SETTINGS.VIDEO_THEATER_MODE)(state),
commentsDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state), commentsDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state),
claimIsMine: makeSelectClaimIsMine(props.uri)(state), claimIsMine: makeSelectClaimIsMine(props.uri)(state),
isLivestream: !makeSelectClaimHasSource(props.uri)(state), isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
}; };
}; };

View file

@ -4,6 +4,7 @@ import React from 'react';
import Page from 'component/page'; import Page from 'component/page';
import LivestreamLayout from 'component/livestreamLayout'; import LivestreamLayout from 'component/livestreamLayout';
import analytics from 'analytics'; import analytics from 'analytics';
import { Lbry } from 'lbry-redux';
type Props = { type Props = {
uri: string, uri: string,
@ -19,8 +20,41 @@ export default function LivestreamPage(props: Props) {
const [activeViewers, setActiveViewers] = React.useState(0); const [activeViewers, setActiveViewers] = React.useState(0);
const [isLive, setIsLive] = React.useState(false); const [isLive, setIsLive] = React.useState(false);
const livestreamChannelId = channelClaim && channelClaim.signing_channel && channelClaim.signing_channel.claim_id; const livestreamChannelId = channelClaim && channelClaim.signing_channel && channelClaim.signing_channel.claim_id;
const [hasLivestreamClaim, setHasLivestreamClaim] = React.useState(false);
const STREAMING_POLL_INTERVAL_IN_MS = 10000;
const LIVESTREAM_CLAIM_POLL_IN_MS = 60000;
// the component needs to check if the channel has published a new livestream, so we know if it should check
React.useEffect(() => {
let checkClaimsInterval;
function checkHasLivestreamClaim() {
Lbry.claim_search({
channel_ids: [livestreamChannelId],
has_no_source: true,
claim_type: ['stream'],
})
.then((res) => {
if (res && res.items && res.items.length > 0) {
setHasLivestreamClaim(true);
}
})
.catch(() => {});
}
if (livestreamChannelId && !isLive) {
if (!checkClaimsInterval) checkHasLivestreamClaim();
checkClaimsInterval = setInterval(checkHasLivestreamClaim, LIVESTREAM_CLAIM_POLL_IN_MS);
return () => {
if (checkClaimsInterval) {
clearInterval(checkClaimsInterval);
}
};
}
}, [livestreamChannelId, isLive]);
React.useEffect(() => { React.useEffect(() => {
let interval;
function checkIsLive() { function checkIsLive() {
// $FlowFixMe Bitwave's API can handle garbage // $FlowFixMe Bitwave's API can handle garbage
fetch(`${BITWAVE_API}/${livestreamChannelId}`) fetch(`${BITWAVE_API}/${livestreamChannelId}`)
@ -38,19 +72,17 @@ export default function LivestreamPage(props: Props) {
} }
}); });
} }
if (livestreamChannelId && hasLivestreamClaim) {
let interval;
if (livestreamChannelId) {
if (!interval) checkIsLive(); if (!interval) checkIsLive();
interval = setInterval(checkIsLive, 10 * 1000); interval = setInterval(checkIsLive, STREAMING_POLL_INTERVAL_IN_MS);
}
return () => { return () => {
if (interval) { if (interval) {
clearInterval(interval); clearInterval(interval);
} }
}; };
}, [livestreamChannelId]); }
}, [livestreamChannelId, hasLivestreamClaim]);
const stringifiedClaim = JSON.stringify(claim); const stringifiedClaim = JSON.stringify(claim);
React.useEffect(() => { React.useEffect(() => {

View file

@ -24,16 +24,34 @@ type Props = {
}; };
export default function LivestreamSetupPage(props: Props) { export default function LivestreamSetupPage(props: Props) {
const LIVESTREAM_CLAIM_POLL_IN_MS = 60000;
const { channels, fetchingChannels, activeChannelClaim, pendingClaims } = props; const { channels, fetchingChannels, activeChannelClaim, pendingClaims } = props;
const [sigData, setSigData] = React.useState({ signature: undefined, signing_ts: undefined }); const [sigData, setSigData] = React.useState({ signature: undefined, signing_ts: undefined });
const [showHelpTest, setShowHelpTest] = usePersistedState('livestream-help-seen', true); const [showHelpTest, setShowHelpTest] = usePersistedState('livestream-help-seen', true);
const [spin, setSpin] = React.useState(true); const [spin, setSpin] = React.useState(true);
const [livestreamClaims, setLivestreamClaims] = React.useState([]);
const hasChannels = channels && channels.length > 0; const hasChannels = channels && channels.length > 0;
const activeChannelClaimStr = JSON.stringify(activeChannelClaim); const activeChannelClaimStr = JSON.stringify(activeChannelClaim);
const streamKey = createStreamKey();
function createStreamKey() {
if (!activeChannelClaim || !sigData.signature || !sigData.signing_ts) return null;
return `${activeChannelClaim.claim_id}?d=${toHex(activeChannelClaim.name)}&s=${sigData.signature}&t=${
sigData.signing_ts
}`;
}
const streamKey = createStreamKey();
const pendingLiveStreamClaims = pendingClaims
? pendingClaims.filter(
(claim) =>
// $FlowFixMe
claim.value_type === 'stream' && !(claim.value && claim.value.source)
)
: [];
const pendingLength = pendingLiveStreamClaims.length;
const totalLivestreamClaims = pendingLiveStreamClaims.concat(livestreamClaims);
const helpText = ( const helpText = (
<div className="section__subtitle"> <div className="section__subtitle">
<p> <p>
@ -76,6 +94,7 @@ export default function LivestreamSetupPage(props: Props) {
<p>{__(`Click Save and you are done!`)}</p> <p>{__(`Click Save and you are done!`)}</p>
</div> </div>
); );
React.useEffect(() => { React.useEffect(() => {
if (activeChannelClaimStr) { if (activeChannelClaimStr) {
const channelClaim = JSON.parse(activeChannelClaimStr); const channelClaim = JSON.parse(activeChannelClaimStr);
@ -96,25 +115,12 @@ export default function LivestreamSetupPage(props: Props) {
} }
}, [activeChannelClaimStr, setSigData]); }, [activeChannelClaimStr, setSigData]);
function createStreamKey() {
if (!activeChannelClaim || !sigData.signature || !sigData.signing_ts) return null;
return `${activeChannelClaim.claim_id}?d=${toHex(activeChannelClaim.name)}&s=${sigData.signature}&t=${
sigData.signing_ts
}`;
}
const [livestreamClaims, setLivestreamClaims] = React.useState([]);
const pendingLiveStreamClaims =
// $FlowFixMe
pendingClaims ? pendingClaims.filter((claim) => !(claim && claim.value && claim.value.source)) : [];
const pendingLength = pendingLiveStreamClaims.length;
const totalLivestreamClaims = pendingLiveStreamClaims.concat(livestreamClaims);
React.useEffect(() => { React.useEffect(() => {
let checkClaimsInterval;
if (!activeChannelClaimStr) return; if (!activeChannelClaimStr) return;
const channelClaim = JSON.parse(activeChannelClaimStr); const channelClaim = JSON.parse(activeChannelClaimStr);
function checkLivestreamClaims() {
Lbry.claim_search({ Lbry.claim_search({
channel_ids: [channelClaim.claim_id], channel_ids: [channelClaim.claim_id],
has_no_source: true, has_no_source: true,
@ -132,11 +138,21 @@ export default function LivestreamSetupPage(props: Props) {
setLivestreamClaims([]); setLivestreamClaims([]);
setSpin(false); setSpin(false);
}); });
}
if (!checkClaimsInterval) {
checkLivestreamClaims();
checkClaimsInterval = setInterval(checkLivestreamClaims, LIVESTREAM_CLAIM_POLL_IN_MS);
}
return () => {
if (checkClaimsInterval) {
clearInterval(checkClaimsInterval);
}
};
}, [activeChannelClaimStr, pendingLength, setSpin]); }, [activeChannelClaimStr, pendingLength, setSpin]);
return ( return (
<Page> <Page>
{fetchingChannels && ( {(fetchingChannels || spin) && (
<div className="main--empty"> <div className="main--empty">
<Spinner delayed /> <Spinner delayed />
</div> </div>

View file

@ -6950,9 +6950,9 @@ lazy-val@^1.0.4:
yargs "^13.2.2" yargs "^13.2.2"
zstd-codec "^0.1.1" zstd-codec "^0.1.1"
lbry-redux@lbryio/lbry-redux#c494c92505cd531048de20bc37dc4d192b6ace01: lbry-redux@lbryio/lbry-redux#db27091f5add9a6bb91c38471a369fca144fc96f:
version "0.0.1" version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/c494c92505cd531048de20bc37dc4d192b6ace01" resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/db27091f5add9a6bb91c38471a369fca144fc96f"
dependencies: dependencies:
proxy-polyfill "0.1.6" proxy-polyfill "0.1.6"
reselect "^3.0.0" reselect "^3.0.0"