initial commit for inline video ads

This commit is contained in:
Sean Yesmunt 2020-03-26 17:47:07 -04:00
parent 829c2eac50
commit c4fc2993d5
12 changed files with 162 additions and 67 deletions

View file

@ -4,28 +4,49 @@ import React, { useEffect } from 'react';
import { withRouter } from 'react-router';
import I18nMessage from 'component/i18nMessage';
import Button from 'component/button';
import classnames from 'classnames';
const ADS_URL = '//assets.revcontent.com/master/delivery.js';
const IS_MOBILE = typeof window.orientation !== 'undefined';
type Props = {
location: { pathname: string },
type: string,
small: boolean,
};
function Ads(props: Props) {
const {
location: { pathname },
type = 'sidebar',
small,
} = props;
useEffect(() => {
if (!IS_MOBILE) {
const script = document.createElement('script');
useEffect(() => {
if (type === 'video') {
try {
const d = document;
const s = 'script';
const n = 'playbuzz-stream';
let js;
let fjs = d.getElementsByTagName(s)[0];
js = d.createElement(s);
js.className = n;
js.src = 'https://stream.playbuzz.com/player/62d1eb10-e362-4873-99ed-c64a4052b43b';
// $FlowFixMe
fjs.parentNode.inmodifiedUrlQuerysertBefore(js, fjs);
} catch (e) {}
}
}, [type]);
useEffect(() => {
if (!IS_MOBILE && type === 'sidebar') {
const script = document.createElement('script');
script.src = ADS_URL;
script.async = true;
// $FlowFixMe
document.body.appendChild(script);
return () => {
// $FlowFixMe
document.body.removeChild(script);
@ -37,27 +58,37 @@ function Ads(props: Props) {
}
};
}
}, []);
}, [type]);
return (
const adsSignInDriver = (
<I18nMessage
tokens={{
sign_in_to_lbrytv: (
<Button button="link" label={__('Sign in to lbry.tv')} navigate={`/$/${PAGES.AUTH}?redirect=${pathname}`} />
),
download_the_app: <Button button="link" label={__('download the app')} href="https://lbry.com/get" />,
}}
>
Hate these? %sign_in_to_lbrytv% or %download_the_app% for an ad free experience.
</I18nMessage>
);
return type === 'video' ? (
<div className="ads__claim-item">
<div id="62d1eb10-e362-4873-99ed-c64a4052b43b" />
<div
className={classnames('ads__claim-text', {
'ads__claim-text--small': small,
})}
>
<div>Ad</div>
<p>{adsSignInDriver}</p>
</div>
</div>
) : (
<div className="ads-wrapper">
<p>Ads</p>
<p>
<I18nMessage
tokens={{
sign_in_to_lbrytv: (
<Button
button="link"
label={__('Sign in to lbry.tv')}
navigate={`/$/${PAGES.AUTH}?redirect=${pathname}`}
/>
),
download_the_app: <Button button="link" label={__('download the app')} href="https://lbry.com/get" />,
}}
>
Hate these? %sign_in_to_lbrytv% or %download_the_app% for an ad free experience.
</I18nMessage>
</p>
<p>{adsSignInDriver}</p>
<div
id="rc-widget-0a74cf"
data-rc-widget

View file

@ -10,6 +10,7 @@ import {
makeSelectClaimForUri,
} from 'lbry-redux';
import { withRouter } from 'react-router';
import { selectUserVerifiedEmail } from 'lbryinc';
import ChannelPage from './view';
const select = (state, props) => {
@ -23,6 +24,7 @@ const select = (state, props) => {
channelIsMine: makeSelectClaimIsMine(props.uri)(state),
channelIsBlocked: selectChannelIsBlocked(props.uri)(state),
claim: props.uri && makeSelectClaimForUri(props.uri)(state),
isAuthenticated: selectUserVerifiedEmail(state),
};
};
@ -30,9 +32,4 @@ const perform = dispatch => ({
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
});
export default withRouter(
connect(
select,
perform
)(ChannelPage)
);
export default withRouter(connect(select, perform)(ChannelPage));

View file

@ -5,6 +5,7 @@ import { withRouter } from 'react-router-dom';
import Button from 'component/button';
import ClaimListDiscover from 'component/claimListDiscover';
import * as CS from 'constants/claim_search';
import Ads from 'lbrytv/component/ads';
type Props = {
uri: string,
@ -17,10 +18,11 @@ type Props = {
fetchClaims: (string, number) => void,
channelIsBlackListed: boolean,
claim: ?Claim,
isAuthenticated: boolean,
};
function ChannelContent(props: Props) {
const { uri, fetching, channelIsMine, channelIsBlocked, channelIsBlackListed, claim } = props;
const { uri, fetching, channelIsMine, channelIsBlocked, channelIsBlackListed, claim, isAuthenticated } = props;
const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0;
return (
@ -51,7 +53,11 @@ function ChannelContent(props: Props) {
{!channelIsMine && claimsInChannel > 0 && <HiddenNsfwClaims uri={uri} />}
{claim && claimsInChannel > 0 ? (
<ClaimListDiscover channelIds={[claim.claim_id]} defaultOrderBy={CS.ORDER_BY_NEW} />
<ClaimListDiscover
channelIds={[claim.claim_id]}
defaultOrderBy={CS.ORDER_BY_NEW}
injectedItem={!isAuthenticated && IS_WEB && <Ads type="video" />}
/>
) : (
<section className="main--empty">This channel hasn't published anything yet</section>
)}

View file

@ -31,6 +31,7 @@ type Props = {
renderProperties: ?(Claim) => Node,
includeSupportAction?: boolean,
hideBlock: boolean,
injectedItem: ?Node,
};
export default function ClaimList(props: Props) {
@ -53,6 +54,7 @@ export default function ClaimList(props: Props) {
renderProperties,
includeSupportAction,
hideBlock,
injectedItem,
} = props;
const [scrollBottomCbMap, setScrollBottomCbMap] = useState({});
const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW);
@ -130,26 +132,28 @@ export default function ClaimList(props: Props) {
{urisLength > 0 && (
<ul className="card ul--no-style">
{sortedUris.map((uri, index) => (
<ClaimPreview
key={uri}
uri={uri}
type={type}
includeSupportAction={includeSupportAction}
showUnresolvedClaim={showUnresolvedClaims}
properties={renderProperties || (type !== 'small' ? undefined : false)}
showUserBlocked={showHiddenByUser}
hideBlock={hideBlock}
customShouldHide={(claim: StreamClaim) => {
// Hack to hide spee.ch thumbnail publishes
// If it meets these requirements, it was probably uploaded here:
// https://github.com/lbryio/lbry-redux/blob/master/src/redux/actions/publish.js#L74-L79
if (claim.name.length === 24 && !claim.name.includes(' ') && claim.value.author === 'Spee.ch') {
return true;
} else {
return false;
}
}}
/>
<React.Fragment key={uri}>
{injectedItem && index === 4 && <li>{injectedItem}</li>}
<ClaimPreview
uri={uri}
type={type}
includeSupportAction={includeSupportAction}
showUnresolvedClaim={showUnresolvedClaims}
properties={renderProperties || (type !== 'small' ? undefined : false)}
showUserBlocked={showHiddenByUser}
hideBlock={hideBlock}
customShouldHide={(claim: StreamClaim) => {
// Hack to hide spee.ch thumbnail publishes
// If it meets these requirements, it was probably uploaded here:
// https://github.com/lbryio/lbry-redux/blob/master/src/redux/actions/publish.js#L74-L79
if (claim.name.length === 24 && !claim.name.includes(' ') && claim.value.author === 'Spee.ch') {
return true;
} else {
return false;
}
}}
/>
</React.Fragment>
))}
</ul>
)}

View file

@ -52,6 +52,7 @@ type Props = {
repostedClaimId?: string,
pageSize?: number,
followedTags?: Array<Tag>,
injectedItem: ?Node,
};
function ClaimListDiscover(props: Props) {
@ -87,6 +88,7 @@ function ClaimListDiscover(props: Props) {
repostedClaimId,
hideFilter,
followedTags,
injectedItem,
} = props;
const didNavigateForward = history.action === 'PUSH';
const { search } = location;
@ -596,6 +598,7 @@ function ClaimListDiscover(props: Props) {
renderProperties={renderProperties}
includeSupportAction={includeSupportAction}
hideBlock={hideBlock}
injectedItem={injectedItem}
/>
{loading && (

View file

@ -6,6 +6,7 @@ import {
makeSelectRecommendedContentForUri,
selectIsSearching,
} from 'lbry-redux';
import { selectUserVerifiedEmail } from 'lbryinc';
import RecommendedVideos from './view';
const select = (state, props) => ({
@ -13,13 +14,11 @@ const select = (state, props) => ({
mature: makeSelectClaimIsNsfw(props.uri)(state),
recommendedContent: makeSelectRecommendedContentForUri(props.uri)(state),
isSearching: selectIsSearching(state),
isAuthenticated: selectUserVerifiedEmail(state),
});
const perform = dispatch => ({
search: (query, options) => dispatch(doSearch(query, options)),
});
export default connect(
select,
perform
)(RecommendedVideos);
export default connect(select, perform)(RecommendedVideos);

View file

@ -1,6 +1,7 @@
// @flow
import React from 'react';
import ClaimList from 'component/claimList';
import Ads from 'lbrytv/component/ads';
type Options = {
related_to: string,
@ -15,6 +16,7 @@ type Props = {
isSearching: boolean,
search: (string, Options) => void,
mature: boolean,
isAuthenticated: boolean,
};
export default class RecommendedContent extends React.PureComponent<Props> {
@ -59,13 +61,14 @@ export default class RecommendedContent extends React.PureComponent<Props> {
didSearch: ?boolean;
render() {
const { recommendedContent, isSearching } = this.props;
const { recommendedContent, isSearching, isAuthenticated } = this.props;
return (
<ClaimList
type="small"
loading={isSearching}
uris={recommendedContent}
injectedItem={!isAuthenticated && IS_WEB && <Ads type="video" small />}
header={__('Related')}
empty={__('No related content found')}
/>

View file

@ -1,5 +1,6 @@
import { connect } from 'react-redux';
import { makeSelectClaimForUri, selectFollowedTags, doResolveUri } from 'lbry-redux';
import { selectUserVerifiedEmail } from 'lbryinc';
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
import * as CS from 'constants/claim_search';
import Tags from './view';
@ -13,6 +14,7 @@ const select = (state, props) => {
followedTags: selectFollowedTags(state),
repostedUri: repostedUri,
repostedClaim: repostedUri ? makeSelectClaimForUri(repostedUri)(state) : null,
isAuthenticated: selectUserVerifiedEmail(state),
};
};

View file

@ -9,6 +9,7 @@ import analytics from 'analytics';
import HiddenNsfw from 'component/common/hidden-nsfw';
import Icon from 'component/common/icon';
import * as CS from 'constants/claim_search';
import Ads from 'lbrytv/component/ads';
type Props = {
location: { search: string },
@ -17,6 +18,7 @@ type Props = {
repostedClaim: ?GenericClaim,
doToggleTagFollowDesktop: string => void,
doResolveUri: string => void,
isAuthenticated: boolean,
};
function DiscoverPage(props: Props) {
@ -27,6 +29,7 @@ function DiscoverPage(props: Props) {
repostedUri,
doToggleTagFollowDesktop,
doResolveUri,
isAuthenticated,
} = props;
const buttonRef = useRef();
const isHovering = useHover(buttonRef);
@ -89,6 +92,7 @@ function DiscoverPage(props: Props) {
tags={tags}
hiddenNsfwMessage={<HiddenNsfw type="page" />}
repostedClaimId={repostedClaim ? repostedClaim.claim_id : null}
injectedItem={!isAuthenticated && IS_WEB && <Ads type="video" />}
meta={
tag && (
<Button

View file

@ -2,6 +2,7 @@ import { connect } from 'react-redux';
import * as SETTINGS from 'constants/settings';
import { doSearch, selectIsSearching, makeSelectSearchUris, makeSelectQueryWithOptions, doToast } from 'lbry-redux';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { selectUserVerifiedEmail } from 'lbryinc';
import analytics from 'analytics';
import SearchPage from './view';
@ -17,6 +18,7 @@ const select = state => {
isSearching: selectIsSearching(state),
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),
uris: uris,
isAuthenticated: selectUserVerifiedEmail(state),
};
};
@ -42,7 +44,4 @@ const perform = dispatch => ({
},
});
export default connect(
select,
perform
)(SearchPage);
export default connect(select, perform)(SearchPage);

View file

@ -9,6 +9,7 @@ import Page from 'component/page';
import SearchOptions from 'component/searchOptions';
import Button from 'component/button';
import ClaimUri from 'component/claimUri';
import Ads from 'lbrytv/component/ads';
type AdditionalOptions = {
isBackgroundSearch: boolean,
@ -23,10 +24,20 @@ type Props = {
onFeedbackNegative: string => void,
onFeedbackPositive: string => void,
showNsfw: boolean,
isAuthenticated: boolean,
};
export default function SearchPage(props: Props) {
const { search, uris, onFeedbackPositive, onFeedbackNegative, location, isSearching, showNsfw } = props;
const {
search,
uris,
onFeedbackPositive,
onFeedbackNegative,
location,
isSearching,
showNsfw,
isAuthenticated,
} = props;
const urlParams = new URLSearchParams(location.search);
const urlQuery = urlParams.get('q') || '';
const additionalOptions: AdditionalOptions = { isBackgroundSearch: false };
@ -44,12 +55,13 @@ export default function SearchPage(props: Props) {
isValid = false;
}
const modifiedUrlQuery = isValid
? path
: urlQuery
.trim()
.replace(/\s+/g, '-')
.replace(INVALID_URI_CHARS, '');
const modifiedUrlQuery =
isValid && path
? path
: urlQuery
.trim()
.replace(/\s+/g, '-')
.replace(INVALID_URI_CHARS, '');
const uriFromQuery = `lbry://${modifiedUrlQuery}`;
useEffect(() => {
@ -85,6 +97,7 @@ export default function SearchPage(props: Props) {
uris={uris}
loading={isSearching}
header={<SearchOptions additionalOptions={additionalOptions} />}
injectedItem={!isAuthenticated && IS_WEB && <Ads type="video" />}
headerAltControls={
<Fragment>
<span>{__('Find what you were looking for?')}</span>

View file

@ -14,3 +14,37 @@
color: var(--color-ads-link);
}
}
// Inline Video Ads
.ads__claim-item {
border-bottom: 1px solid var(--color-border);
padding: var(--spacing-medium);
background-color: var(--color-ads-background);
display: flex;
> div {
width: 50%;
position: relative !important;
}
.avp-p-gui {
z-index: 1 !important;
}
.pbs__player.pb-stream-sticky-on {
position: relative !important;
z-index: 1 !important;
margin-top: -140px !important;
}
}
.ads__claim-text {
display: flex;
flex-direction: column;
justify-content: center;
padding-left: var(--spacing-large);
}
.ads__claim-text--small {
font-size: var(--font-small);
}