Also add support for embeds

This commit is contained in:
Rafael 2022-05-09 11:47:17 -03:00 committed by Thomas Zarebczan
parent 6b2427768c
commit 239bde0752
6 changed files with 93 additions and 17 deletions

View file

@ -55,6 +55,7 @@ const oneTrustScriptSrc = 'https://cdn.cookielaw.org/scripttemplates/otSDKStub.j
const LATEST_PATH = `/$/${PAGES.LATEST}/`; const LATEST_PATH = `/$/${PAGES.LATEST}/`;
const LIVE_PATH = `/$/${PAGES.LIVE_NOW}/`; const LIVE_PATH = `/$/${PAGES.LIVE_NOW}/`;
const EMBED_PATH = `/$/${PAGES.EMBED}/`;
type Props = { type Props = {
language: string, language: string,
@ -143,7 +144,7 @@ function App(props: Props) {
const rawReferrerParam = urlParams.get('r'); const rawReferrerParam = urlParams.get('r');
const fromLbrytvParam = urlParams.get('sunset'); const fromLbrytvParam = urlParams.get('sunset');
const sanitizedReferrerParam = rawReferrerParam && rawReferrerParam.replace(':', '#'); const sanitizedReferrerParam = rawReferrerParam && rawReferrerParam.replace(':', '#');
const embedPath = pathname.startsWith(`/$/${PAGES.EMBED}`); const embedPath = pathname.startsWith(EMBED_PATH);
const shouldHideNag = embedPath || pathname.startsWith(`/$/${PAGES.AUTH_VERIFY}`); const shouldHideNag = embedPath || pathname.startsWith(`/$/${PAGES.AUTH_VERIFY}`);
const userId = user && user.id; const userId = user && user.id;
const hasMyChannels = myChannelClaimIds && myChannelClaimIds.length > 0; const hasMyChannels = myChannelClaimIds && myChannelClaimIds.length > 0;
@ -156,11 +157,13 @@ function App(props: Props) {
const urlPath = pathname + hash; const urlPath = pathname + hash;
const latestContentPath = urlPath.startsWith(LATEST_PATH); const latestContentPath = urlPath.startsWith(LATEST_PATH);
const liveContentPath = urlPath.startsWith(LIVE_PATH); const liveContentPath = urlPath.startsWith(LIVE_PATH);
const isNewestPath = latestContentPath || liveContentPath; const featureParam = urlParams.get('feature');
const embedLatestPath = embedPath && (featureParam === PAGES.LATEST || featureParam === PAGES.LIVE_NOW);
const isNewestPath = latestContentPath || liveContentPath || embedLatestPath;
let path; let path;
if (isNewestPath) { if (isNewestPath) {
path = urlPath.replace(latestContentPath ? LATEST_PATH : LIVE_PATH, ''); path = urlPath.replace(embedLatestPath ? EMBED_PATH : latestContentPath ? LATEST_PATH : LIVE_PATH, '');
} else { } else {
// Remove the leading "/" added by the browser // Remove the leading "/" added by the browser
path = urlPath.slice(1); path = urlPath.slice(1);
@ -515,7 +518,7 @@ function App(props: Props) {
/> />
) : ( ) : (
<React.Fragment> <React.Fragment>
<Router uri={uri} /> <Router uri={uri} embedLatestPath={embedLatestPath} />
<ModalRouter /> <ModalRouter />
<React.Suspense fallback={null}>{renderFiledrop && <FileDrop />}</React.Suspense> <React.Suspense fallback={null}>{renderFiledrop && <FileDrop />}</React.Suspense>

View file

@ -136,6 +136,7 @@ type Props = {
hideTitleNotificationCount: boolean, hideTitleNotificationCount: boolean,
hasDefaultChannel: boolean, hasDefaultChannel: boolean,
doSetActiveChannel: (claimId: ?string, override?: boolean) => void, doSetActiveChannel: (claimId: ?string, override?: boolean) => void,
embedLatestPath: ?boolean,
}; };
type PrivateRouteProps = Props & { type PrivateRouteProps = Props & {
@ -179,6 +180,7 @@ function AppRouter(props: Props) {
hideTitleNotificationCount, hideTitleNotificationCount,
hasDefaultChannel, hasDefaultChannel,
doSetActiveChannel, doSetActiveChannel,
embedLatestPath,
} = props; } = props;
const defaultChannelRef = React.useRef(hasDefaultChannel); const defaultChannelRef = React.useRef(hasDefaultChannel);
@ -397,7 +399,11 @@ function AppRouter(props: Props) {
<Route path={`/$/${PAGES.POPOUT}/:channelName/:streamName`} component={PopoutChatPage} /> <Route path={`/$/${PAGES.POPOUT}/:channelName/:streamName`} component={PopoutChatPage} />
<Route path={`/$/${PAGES.EMBED}/:claimName`} exact component={EmbedWrapperPage} /> <Route
path={`/$/${PAGES.EMBED}/:claimName`}
exact
component={embedLatestPath ? () => <EmbedWrapperPage uri={uri} /> : EmbedWrapperPage}
/>
<Route path={`/$/${PAGES.EMBED}/:claimName/:claimId`} exact component={EmbedWrapperPage} /> <Route path={`/$/${PAGES.EMBED}/:claimName/:claimId`} exact component={EmbedWrapperPage} />
{/* 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 */}

View file

@ -1,31 +1,60 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import EmbedWrapperPage from './view'; import EmbedWrapperPage from './view';
import { selectClaimForUri, selectIsUriResolving, selectGeoRestrictionForUri } from 'redux/selectors/claims'; import * as PAGES from 'constants/pages';
import {
selectClaimForUri,
selectIsUriResolving,
selectGeoRestrictionForUri,
selectLatestClaimByUri,
} from 'redux/selectors/claims';
import { makeSelectStreamingUrlForUri } from 'redux/selectors/file_info'; import { makeSelectStreamingUrlForUri } from 'redux/selectors/file_info';
import { doResolveUri } from 'redux/actions/claims'; import { doResolveUri, doFetchLatestClaimForChannel } from 'redux/actions/claims';
import { buildURI } from 'util/lbryURI'; import { buildURI } from 'util/lbryURI';
import { doPlayUri } from 'redux/actions/content'; import { doPlayUri } from 'redux/actions/content';
import { selectShouldObscurePreviewForUri } from 'redux/selectors/content'; import { selectShouldObscurePreviewForUri } from 'redux/selectors/content';
import { selectCostInfoForUri, doFetchCostInfoForUri, selectBlackListedOutpoints } from 'lbryinc'; import { selectCostInfoForUri, doFetchCostInfoForUri, selectBlackListedOutpoints } from 'lbryinc';
import { doCommentSocketConnect, doCommentSocketDisconnect } from 'redux/actions/websocket'; import { doCommentSocketConnect, doCommentSocketDisconnect } from 'redux/actions/websocket';
import { doFetchActiveLivestreams, doFetchChannelLiveStatus } from 'redux/actions/livestream'; import { doFetchActiveLivestreams, doFetchChannelLiveStatus } from 'redux/actions/livestream';
import { selectIsActiveLivestreamForUri, selectActiveLivestreamInitialized } from 'redux/selectors/livestream'; import {
selectIsActiveLivestreamForUri,
selectActiveLivestreamInitialized,
selectActiveLiveClaimForChannel,
} from 'redux/selectors/livestream';
import { getThumbnailFromClaim, isStreamPlaceholderClaim } from 'util/claim'; import { getThumbnailFromClaim, isStreamPlaceholderClaim } from 'util/claim';
import { doUserSetReferrerWithUri } from 'redux/actions/user'; import { doUserSetReferrerWithUri } from 'redux/actions/user';
const select = (state, props) => { const select = (state, props) => {
const { match } = props; const { search } = state.router.location;
const { match } = props || {};
let uri = props.uri;
let claimId;
if (match) {
const { params } = match; const { params } = match;
const { claimName, claimId } = params; const { claimName } = params;
const uri = claimName ? buildURI({ claimName, claimId }) : ''; claimId = params.claimId;
uri = claimName ? buildURI({ claimName, claimId }) : '';
}
const urlParams = new URLSearchParams(search);
const featureParam = urlParams.get('feature');
const isNewestPath = featureParam === PAGES.LIVE_NOW || featureParam === PAGES.LATEST;
const claim = selectClaimForUri(state, uri); const claim = selectClaimForUri(state, uri);
const { canonical_url: canonicalUrl, signing_channel: channelClaim, txid, nout } = claim || {}; const { canonical_url: canonicalUrl, signing_channel: channelClaim, txid, nout } = claim || {};
if (isNewestPath) claimId = claim?.claim_id;
const { claim_id: channelClaimId, canonical_url: channelUri, txid: channelTxid, channelNout } = channelClaim || {}; const { claim_id: channelClaimId, canonical_url: channelUri, txid: channelTxid, channelNout } = channelClaim || {};
const haveClaim = Boolean(claim); const haveClaim = Boolean(claim);
const nullClaim = claim === null; const nullClaim = claim === null;
const latestContentClaim =
featureParam === PAGES.LIVE_NOW
? selectActiveLiveClaimForChannel(state, claimId)
: selectLatestClaimByUri(state, canonicalUrl);
const latestClaimUrl = latestContentClaim && latestContentClaim.canonical_url;
if (latestClaimUrl) uri = latestClaimUrl;
return { return {
uri, uri,
claimId, claimId,
@ -38,11 +67,13 @@ const select = (state, props) => {
channelClaimId, channelClaimId,
channelTxid, channelTxid,
channelNout, channelNout,
latestClaimUrl,
isNewestPath,
costInfo: uri && selectCostInfoForUri(state, uri), costInfo: uri && selectCostInfoForUri(state, uri),
streamingUrl: uri && makeSelectStreamingUrlForUri(uri)(state), streamingUrl: uri && makeSelectStreamingUrlForUri(uri)(state),
isResolvingUri: uri && selectIsUriResolving(state, uri), isResolvingUri: uri && selectIsUriResolving(state, uri),
blackListedOutpoints: haveClaim && selectBlackListedOutpoints(state), blackListedOutpoints: haveClaim && selectBlackListedOutpoints(state),
isCurrentClaimLive: canonicalUrl && selectIsActiveLivestreamForUri(state, canonicalUrl), isCurrentClaimLive: selectIsActiveLivestreamForUri(state, isNewestPath ? latestClaimUrl : canonicalUrl),
isLivestreamClaim: isStreamPlaceholderClaim(claim), isLivestreamClaim: isStreamPlaceholderClaim(claim),
obscurePreview: selectShouldObscurePreviewForUri(state, uri), obscurePreview: selectShouldObscurePreviewForUri(state, uri),
claimThumbnail: getThumbnailFromClaim(claim), claimThumbnail: getThumbnailFromClaim(claim),
@ -60,6 +91,7 @@ const perform = {
doCommentSocketDisconnect, doCommentSocketDisconnect,
doFetchActiveLivestreams, doFetchActiveLivestreams,
setReferrer: doUserSetReferrerWithUri, setReferrer: doUserSetReferrerWithUri,
fetchLatestClaimForChannel: doFetchLatestClaimForChannel,
}; };
export default connect(select, perform)(EmbedWrapperPage); export default connect(select, perform)(EmbedWrapperPage);

View file

@ -1,5 +1,6 @@
// @flow // @flow
import { SITE_NAME } from 'config'; import { SITE_NAME } from 'config';
import * as PAGES from 'constants/pages';
import React from 'react'; import React from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import FileRender from 'component/fileRender'; import FileRender from 'component/fileRender';
@ -36,6 +37,9 @@ type Props = {
obscurePreview: boolean, obscurePreview: boolean,
activeLivestreamInitialized: boolean, activeLivestreamInitialized: boolean,
geoRestriction: ?GeoRestriction, geoRestriction: ?GeoRestriction,
latestClaimUrl: ?string,
isNewestPath: ?boolean,
fetchLatestClaimForChannel: (uri: string, isEmbed: boolean) => void,
doResolveUri: (uri: string) => void, doResolveUri: (uri: string) => void,
doPlayUri: (uri: string) => void, doPlayUri: (uri: string) => void,
doFetchCostInfoForUri: (uri: string) => void, doFetchCostInfoForUri: (uri: string) => void,
@ -71,6 +75,9 @@ export default function EmbedWrapperPage(props: Props) {
obscurePreview, obscurePreview,
activeLivestreamInitialized, activeLivestreamInitialized,
geoRestriction, geoRestriction,
latestClaimUrl,
isNewestPath,
fetchLatestClaimForChannel,
doResolveUri, doResolveUri,
doPlayUri, doPlayUri,
doFetchCostInfoForUri, doFetchCostInfoForUri,
@ -90,6 +97,9 @@ export default function EmbedWrapperPage(props: Props) {
const channelUrl = channelUri && formatLbryChannelName(channelUri); const channelUrl = channelUri && formatLbryChannelName(channelUri);
const urlParams = new URLSearchParams(search); const urlParams = new URLSearchParams(search);
const featureParam = urlParams.get('feature');
const latestContentPath = featureParam === PAGES.LATEST;
const liveContentPath = featureParam === PAGES.LIVE_NOW;
const rawReferrerParam = urlParams.get('r'); const rawReferrerParam = urlParams.get('r');
const sanitizedReferrerParam = rawReferrerParam && rawReferrerParam.replace(':', '#'); const sanitizedReferrerParam = rawReferrerParam && rawReferrerParam.replace(':', '#');
const embedLightBackground = urlParams.get('embedBackgroundLight'); const embedLightBackground = urlParams.get('embedBackgroundLight');
@ -111,6 +121,18 @@ export default function EmbedWrapperPage(props: Props) {
const thumbnail = useThumbnail(claimThumbnail, containerRef); const thumbnail = useThumbnail(claimThumbnail, containerRef);
React.useEffect(() => {
if (!latestClaimUrl && liveContentPath && claimId) {
doFetchChannelLiveStatus(claimId);
}
}, [claimId, doFetchChannelLiveStatus, latestClaimUrl, liveContentPath]);
React.useEffect(() => {
if (!latestClaimUrl && latestContentPath && canonicalUrl) {
fetchLatestClaimForChannel(canonicalUrl, true);
}
}, [canonicalUrl, fetchLatestClaimForChannel, latestClaimUrl, latestContentPath]);
React.useEffect(() => { React.useEffect(() => {
if (!sanitizedReferrerParam) setReferrer(uri); if (!sanitizedReferrerParam) setReferrer(uri);
}, [sanitizedReferrerParam, setReferrer, uri]); }, [sanitizedReferrerParam, setReferrer, uri]);
@ -142,10 +164,10 @@ export default function EmbedWrapperPage(props: Props) {
doResolveUri(uri); doResolveUri(uri);
} }
if (uri && haveClaim && costInfo && costInfo.cost === 0) { if (uri && (isNewestPath ? latestClaimUrl : haveClaim) && costInfo && costInfo.cost === 0) {
doPlayUri(uri); doPlayUri(uri);
} }
}, [doPlayUri, doResolveUri, haveClaim, costInfo, uri]); }, [doPlayUri, doResolveUri, haveClaim, costInfo, uri, isNewestPath, latestClaimUrl]);
React.useEffect(() => { React.useEffect(() => {
if (haveClaim && uri && doFetchCostInfoForUri) { if (haveClaim && uri && doFetchCostInfoForUri) {
@ -155,6 +177,15 @@ export default function EmbedWrapperPage(props: Props) {
useFetchLiveStatus(livestreamsFetched ? channelClaimId : undefined, doFetchChannelLiveStatus); useFetchLiveStatus(livestreamsFetched ? channelClaimId : undefined, doFetchChannelLiveStatus);
// Wait for latest claim fetch
if (isNewestPath && latestClaimUrl === undefined) {
return (
<div className="main--empty">
<Spinner delayed />
</div>
);
}
if (isClaimBlackListed) { if (isClaimBlackListed) {
return ( return (
<Card <Card

View file

@ -1135,7 +1135,10 @@ export const doCheckPendingClaims = (onChannelConfirmed: Function) => (dispatch:
}, 30000); }, 30000);
}; };
export const doFetchLatestClaimForChannel = (uri: string) => (dispatch: Dispatch, getState: GetState) => { export const doFetchLatestClaimForChannel = (uri: string, isEmbed?: boolean) => (
dispatch: Dispatch,
getState: GetState
) => {
const searchOptions = { const searchOptions = {
limit_claims_per_channel: 1, limit_claims_per_channel: 1,
channel: uri, channel: uri,
@ -1143,6 +1146,7 @@ export const doFetchLatestClaimForChannel = (uri: string) => (dispatch: Dispatch
order_by: ['release_time'], order_by: ['release_time'],
page: 1, page: 1,
has_source: true, has_source: true,
stream_types: isEmbed ? ['audio', 'video'] : undefined,
}; };
return dispatch(doClaimSearch(searchOptions)) return dispatch(doClaimSearch(searchOptions))

View file

@ -66,7 +66,7 @@ export const selectIsActiveLivestreamForUri = createCachedSelector(
const activeLivestreamValues = Object.values(activeLivestreams); const activeLivestreamValues = Object.values(activeLivestreams);
// $FlowFixMe - unable to resolve claimUri // $FlowFixMe - unable to resolve claimUri
return activeLivestreamValues.some((v) => v.claimUri === uri); return activeLivestreamValues.some((v) => v?.claimUri === uri);
} }
)((state, uri) => String(uri)); )((state, uri) => String(uri));