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:
parent
2e87b2fd22
commit
f092e8cb7b
10 changed files with 203 additions and 150 deletions
|
@ -142,7 +142,7 @@
|
|||
"imagesloaded": "^4.1.4",
|
||||
"json-loader": "^0.5.4",
|
||||
"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",
|
||||
"lint-staged": "^7.0.2",
|
||||
"localforage": "^1.7.1",
|
||||
|
|
|
@ -249,106 +249,110 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
'claim-preview__wrapper--small': type === 'small',
|
||||
})}
|
||||
>
|
||||
{!hideRepostLabel && <ClaimRepostAuthor uri={uri} />}
|
||||
<>
|
||||
{!hideRepostLabel && <ClaimRepostAuthor uri={uri} />}
|
||||
|
||||
<div
|
||||
className={classnames('claim-preview', {
|
||||
'claim-preview--small': type === 'small' || type === 'tooltip',
|
||||
'claim-preview--large': type === 'large',
|
||||
'claim-preview--inline': type === 'inline',
|
||||
'claim-preview--tooltip': type === 'tooltip',
|
||||
'claim-preview--channel': isChannelUri,
|
||||
'claim-preview--visited': !isChannelUri && !claimIsMine && hasVisitedUri,
|
||||
'claim-preview--pending': pending,
|
||||
})}
|
||||
>
|
||||
{isChannelUri && claim ? (
|
||||
<UriIndicator uri={contentUri} link>
|
||||
<ChannelThumbnail uri={contentUri} />
|
||||
</UriIndicator>
|
||||
) : (
|
||||
<>
|
||||
{!pending ? (
|
||||
<NavLink {...navLinkProps}>
|
||||
<FileThumbnail thumbnail={thumbnailUrl}>
|
||||
{/* @if TARGET='app' */}
|
||||
{claim && (
|
||||
<div className="claim-preview__hover-actions">
|
||||
<FileDownloadLink uri={canonicalUrl} hideOpenButton hideDownloadStatus />
|
||||
</div>
|
||||
)}
|
||||
{/* @endif */}
|
||||
{!isRepost && !isChannelUri && !isLivestream && (
|
||||
<div className="claim-preview__file-property-overlay">
|
||||
<FileProperties uri={contentUri} small />
|
||||
</div>
|
||||
)}
|
||||
</FileThumbnail>
|
||||
</NavLink>
|
||||
) : (
|
||||
<FileThumbnail thumbnail={thumbnailUrl} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="claim-preview__text">
|
||||
<div className="claim-preview-metadata">
|
||||
<div className="claim-preview-info">
|
||||
{pending ? (
|
||||
<ClaimPreviewTitle uri={contentUri} />
|
||||
) : (
|
||||
<div
|
||||
className={classnames('claim-preview', {
|
||||
'claim-preview--small': type === 'small' || type === 'tooltip',
|
||||
'claim-preview--large': type === 'large',
|
||||
'claim-preview--inline': type === 'inline',
|
||||
'claim-preview--tooltip': type === 'tooltip',
|
||||
'claim-preview--channel': isChannelUri,
|
||||
'claim-preview--visited': !isChannelUri && !claimIsMine && hasVisitedUri,
|
||||
'claim-preview--pending': pending,
|
||||
})}
|
||||
>
|
||||
{isChannelUri && claim ? (
|
||||
<UriIndicator uri={contentUri} link>
|
||||
<ChannelThumbnail uri={contentUri} />
|
||||
</UriIndicator>
|
||||
) : (
|
||||
<>
|
||||
{!pending ? (
|
||||
<NavLink {...navLinkProps}>
|
||||
<ClaimPreviewTitle uri={uri} />
|
||||
<FileThumbnail thumbnail={thumbnailUrl}>
|
||||
{/* @if TARGET='app' */}
|
||||
{claim && (
|
||||
<div className="claim-preview__hover-actions">
|
||||
<FileDownloadLink uri={canonicalUrl} hideOpenButton hideDownloadStatus />
|
||||
</div>
|
||||
)}
|
||||
{/* @endif */}
|
||||
{!isRepost && !isChannelUri && !isLivestream && (
|
||||
<div className="claim-preview__file-property-overlay">
|
||||
<FileProperties uri={contentUri} small />
|
||||
</div>
|
||||
)}
|
||||
</FileThumbnail>
|
||||
</NavLink>
|
||||
) : (
|
||||
<FileThumbnail thumbnail={thumbnailUrl} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* {type !== 'small' && !isChannelUri && signingChannel && SIMPLE_SITE && (
|
||||
<div className="claim-preview__text">
|
||||
<div className="claim-preview-metadata">
|
||||
<div className="claim-preview-info">
|
||||
{pending ? (
|
||||
<ClaimPreviewTitle uri={contentUri} />
|
||||
) : (
|
||||
<NavLink {...navLinkProps}>
|
||||
<ClaimPreviewTitle uri={uri} />
|
||||
</NavLink>
|
||||
)}
|
||||
|
||||
{/* {type !== 'small' && !isChannelUri && signingChannel && SIMPLE_SITE && (
|
||||
<ChannelThumbnail uri={signingChannel.permanent_url} />
|
||||
)} */}
|
||||
</div>
|
||||
<ClaimPreviewSubtitle uri={uri} type={type} />
|
||||
{(pending || !!reflectingProgress) && <PublishPending uri={uri} />}
|
||||
</div>
|
||||
<ClaimPreviewSubtitle uri={uri} type={type} />
|
||||
{(pending || !!reflectingProgress) && <PublishPending uri={uri} />}
|
||||
{type !== 'small' && (
|
||||
<div className="claim-preview__actions">
|
||||
{!pending && (
|
||||
<>
|
||||
{renderActions && claim && renderActions(claim)}
|
||||
{shouldHideActions || renderActions ? null : actions !== undefined ? (
|
||||
actions
|
||||
) : (
|
||||
<div className="claim-preview__primary-actions">
|
||||
{!isChannelUri && signingChannel && (
|
||||
<div className="claim-preview__channel-staked">
|
||||
<ChannelThumbnail uri={signingChannel.permanent_url} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isChannelUri && !channelIsBlocked && !claimIsMine && (
|
||||
<SubscribeButton
|
||||
uri={contentUri.startsWith('lbry://') ? contentUri : `lbry://${contentUri}`}
|
||||
/>
|
||||
)}
|
||||
|
||||
{includeSupportAction && <ClaimSupportButton uri={uri} />}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{claim && (
|
||||
<React.Fragment>
|
||||
{typeof properties === 'function' ? (
|
||||
properties(claim)
|
||||
) : properties !== undefined ? (
|
||||
properties
|
||||
) : (
|
||||
<ClaimTags uri={uri} type={type} />
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{type !== 'small' && (
|
||||
<div className="claim-preview__actions">
|
||||
{!pending && (
|
||||
<>
|
||||
{renderActions && claim && renderActions(claim)}
|
||||
{shouldHideActions || renderActions ? null : actions !== undefined ? (
|
||||
actions
|
||||
) : (
|
||||
<div className="claim-preview__primary-actions">
|
||||
{!isChannelUri && signingChannel && (
|
||||
<div className="claim-preview__channel-staked">
|
||||
<ChannelThumbnail uri={signingChannel.permanent_url} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isChannelUri && !channelIsBlocked && !claimIsMine && (
|
||||
<SubscribeButton uri={contentUri.startsWith('lbry://') ? contentUri : `lbry://${contentUri}`} />
|
||||
)}
|
||||
|
||||
{includeSupportAction && <ClaimSupportButton uri={uri} />}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{claim && (
|
||||
<React.Fragment>
|
||||
{typeof properties === 'function' ? (
|
||||
properties(claim)
|
||||
) : properties !== undefined ? (
|
||||
properties
|
||||
) : (
|
||||
<ClaimTags uri={uri} type={type} />
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{!hideMenu && <ClaimMenuList uri={uri} />}
|
||||
{!hideMenu && <ClaimMenuList uri={uri} />}
|
||||
</>
|
||||
</WrapperElement>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
doFileGet,
|
||||
makeSelectChannelForClaimUri,
|
||||
makeSelectClaimIsNsfw,
|
||||
makeSelectClaimHasSource,
|
||||
makeSelectClaimIsStreamPlaceholder,
|
||||
} from 'lbry-redux';
|
||||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
||||
|
@ -26,7 +26,7 @@ const select = (state, props) => ({
|
|||
blockedChannelUris: selectMutedChannels(state),
|
||||
showMature: selectShowMatureContent(state),
|
||||
isMature: makeSelectClaimIsNsfw(props.uri)(state),
|
||||
isLivestream: !makeSelectClaimHasSource(props.uri)(state),
|
||||
isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
|
||||
});
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
|
|
|
@ -21,6 +21,7 @@ type Props = {
|
|||
nag?: Node,
|
||||
smallTitle?: boolean,
|
||||
onClick?: () => void,
|
||||
children?: any, // not sure how this works
|
||||
};
|
||||
|
||||
export default function Card(props: Props) {
|
||||
|
@ -39,6 +40,7 @@ export default function Card(props: Props) {
|
|||
defaultExpand,
|
||||
nag,
|
||||
onClick,
|
||||
children,
|
||||
} = props;
|
||||
const [expanded, setExpanded] = useState(defaultExpand);
|
||||
const expandable = defaultExpand !== undefined;
|
||||
|
@ -102,6 +104,7 @@ export default function Card(props: Props) {
|
|||
</div>
|
||||
)}
|
||||
{actions && <div className="card__main-actions">{actions}</div>}
|
||||
{children && <div className="card__main-actions">{children}</div>}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
|
|
@ -64,11 +64,12 @@ export default function LivestreamLink(props: Props) {
|
|||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
className="livestream__channel-link"
|
||||
title={__('Live stream in progress')}
|
||||
actions={<ClaimPreview uri={livestreamClaim.canonical_url} livestream type="inline" />}
|
||||
/>
|
||||
// gonna pass the wrapper in so I don't have to rewrite the dmca/blocking logic in claimPreview.
|
||||
const element = (props: { children: any }) => (
|
||||
<Card className="livestream__channel-link" title={__('Live stream in progress')}>
|
||||
{props.children}
|
||||
</Card>
|
||||
);
|
||||
|
||||
return <ClaimPreview uri={livestreamClaim.canonical_url} wrapperElement={element} type="inline" />;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,6 @@ import BuyPage from 'page/buy';
|
|||
import NotificationsPage from 'page/notifications';
|
||||
import SignInWalletPasswordPage from 'page/signInWalletPassword';
|
||||
import YoutubeSyncPage from 'page/youtubeSync';
|
||||
import LiveStreamPage from 'page/livestream';
|
||||
|
||||
import { LINKED_COMMENT_QUERY_PARAM } from 'constants/comment';
|
||||
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/: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 */}
|
||||
<Route path="/:claimName" exact component={ShowPage} />
|
||||
<Route path="/:claimName/:streamName" exact component={ShowPage} />
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
SETTINGS,
|
||||
makeSelectTagInClaimOrChannelForUri,
|
||||
makeSelectClaimIsMine,
|
||||
makeSelectClaimHasSource,
|
||||
makeSelectClaimIsStreamPlaceholder,
|
||||
} from 'lbry-redux';
|
||||
import { makeSelectCostInfoForUri, doFetchCostInfoForUri } from 'lbryinc';
|
||||
import { selectShowMatureContent, makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
|
@ -35,7 +35,7 @@ const select = (state, props) => {
|
|||
videoTheaterMode: makeSelectClientSetting(SETTINGS.VIDEO_THEATER_MODE)(state),
|
||||
commentsDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state),
|
||||
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
|
||||
isLivestream: !makeSelectClaimHasSource(props.uri)(state),
|
||||
isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import React from 'react';
|
|||
import Page from 'component/page';
|
||||
import LivestreamLayout from 'component/livestreamLayout';
|
||||
import analytics from 'analytics';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
|
||||
type Props = {
|
||||
uri: string,
|
||||
|
@ -19,8 +20,41 @@ export default function LivestreamPage(props: 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);
|
||||
|
||||
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(() => {
|
||||
let interval;
|
||||
function checkIsLive() {
|
||||
// $FlowFixMe Bitwave's API can handle garbage
|
||||
fetch(`${BITWAVE_API}/${livestreamChannelId}`)
|
||||
|
@ -38,19 +72,17 @@ export default function LivestreamPage(props: Props) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
let interval;
|
||||
if (livestreamChannelId) {
|
||||
if (livestreamChannelId && hasLivestreamClaim) {
|
||||
if (!interval) checkIsLive();
|
||||
interval = setInterval(checkIsLive, 10 * 1000);
|
||||
}
|
||||
interval = setInterval(checkIsLive, STREAMING_POLL_INTERVAL_IN_MS);
|
||||
|
||||
return () => {
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
};
|
||||
}, [livestreamChannelId]);
|
||||
return () => {
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
};
|
||||
}
|
||||
}, [livestreamChannelId, hasLivestreamClaim]);
|
||||
|
||||
const stringifiedClaim = JSON.stringify(claim);
|
||||
React.useEffect(() => {
|
||||
|
|
|
@ -24,16 +24,34 @@ type Props = {
|
|||
};
|
||||
|
||||
export default function LivestreamSetupPage(props: Props) {
|
||||
const LIVESTREAM_CLAIM_POLL_IN_MS = 60000;
|
||||
const { channels, fetchingChannels, activeChannelClaim, pendingClaims } = props;
|
||||
|
||||
const [sigData, setSigData] = React.useState({ signature: undefined, signing_ts: undefined });
|
||||
const [showHelpTest, setShowHelpTest] = usePersistedState('livestream-help-seen', true);
|
||||
const [spin, setSpin] = React.useState(true);
|
||||
const [livestreamClaims, setLivestreamClaims] = React.useState([]);
|
||||
|
||||
const hasChannels = channels && channels.length > 0;
|
||||
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 = (
|
||||
<div className="section__subtitle">
|
||||
<p>
|
||||
|
@ -76,6 +94,7 @@ export default function LivestreamSetupPage(props: Props) {
|
|||
<p>{__(`Click Save and you are done!`)}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (activeChannelClaimStr) {
|
||||
const channelClaim = JSON.parse(activeChannelClaimStr);
|
||||
|
@ -96,47 +115,44 @@ export default function LivestreamSetupPage(props: Props) {
|
|||
}
|
||||
}, [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(() => {
|
||||
let checkClaimsInterval;
|
||||
if (!activeChannelClaimStr) return;
|
||||
|
||||
const channelClaim = JSON.parse(activeChannelClaimStr);
|
||||
|
||||
Lbry.claim_search({
|
||||
channel_ids: [channelClaim.claim_id],
|
||||
has_no_source: true,
|
||||
claim_type: ['stream'],
|
||||
})
|
||||
.then((res) => {
|
||||
if (res && res.items && res.items.length > 0) {
|
||||
setLivestreamClaims(res.items.reverse());
|
||||
} else {
|
||||
setLivestreamClaims([]);
|
||||
}
|
||||
setSpin(false);
|
||||
function checkLivestreamClaims() {
|
||||
Lbry.claim_search({
|
||||
channel_ids: [channelClaim.claim_id],
|
||||
has_no_source: true,
|
||||
claim_type: ['stream'],
|
||||
})
|
||||
.catch(() => {
|
||||
setLivestreamClaims([]);
|
||||
setSpin(false);
|
||||
});
|
||||
.then((res) => {
|
||||
if (res && res.items && res.items.length > 0) {
|
||||
setLivestreamClaims(res.items.reverse());
|
||||
} else {
|
||||
setLivestreamClaims([]);
|
||||
}
|
||||
setSpin(false);
|
||||
})
|
||||
.catch(() => {
|
||||
setLivestreamClaims([]);
|
||||
setSpin(false);
|
||||
});
|
||||
}
|
||||
if (!checkClaimsInterval) {
|
||||
checkLivestreamClaims();
|
||||
checkClaimsInterval = setInterval(checkLivestreamClaims, LIVESTREAM_CLAIM_POLL_IN_MS);
|
||||
}
|
||||
return () => {
|
||||
if (checkClaimsInterval) {
|
||||
clearInterval(checkClaimsInterval);
|
||||
}
|
||||
};
|
||||
}, [activeChannelClaimStr, pendingLength, setSpin]);
|
||||
|
||||
return (
|
||||
<Page>
|
||||
{fetchingChannels && (
|
||||
{(fetchingChannels || spin) && (
|
||||
<div className="main--empty">
|
||||
<Spinner delayed />
|
||||
</div>
|
||||
|
|
|
@ -6950,9 +6950,9 @@ lazy-val@^1.0.4:
|
|||
yargs "^13.2.2"
|
||||
zstd-codec "^0.1.1"
|
||||
|
||||
lbry-redux@lbryio/lbry-redux#c494c92505cd531048de20bc37dc4d192b6ace01:
|
||||
lbry-redux@lbryio/lbry-redux#db27091f5add9a6bb91c38471a369fca144fc96f:
|
||||
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:
|
||||
proxy-polyfill "0.1.6"
|
||||
reselect "^3.0.0"
|
||||
|
|
Loading…
Reference in a new issue