Add ad to the homepage as a card (#362)

* coming along well

* coming along well

* adding custom react element

* coming along well

* coming along well

* coming along well

* working pretty well

* almost done

* essentially working just could use a couple touchups

* cleanup and lint errors

* fix lint errors

* fix flow errors

* possible bugfix

* dynamically set width and height

* only run when rowdata is populated

* trying using ref

* better way to check for card population

* working implementation

* working implementation

* clean up flow and clean up script

* fix typo in comment and logs
This commit is contained in:
mayeaux 2021-11-30 23:01:03 +01:00 committed by GitHub
parent 873ac4dc5d
commit 1e071550ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 172 additions and 33 deletions

View file

@ -1,8 +1,9 @@
// @flow // @flow
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import * as PAGES from 'constants/pages'; import * as PAGES from 'constants/pages';
import { SITE_NAME, SIMPLE_SITE, ENABLE_NO_SOURCE_CLAIMS } from 'config'; import { SHOW_ADS, SITE_NAME, SIMPLE_SITE, ENABLE_NO_SOURCE_CLAIMS } from 'config';
import React from 'react'; import Ads from 'web/component/ads';
import React from 'react';
import Page from 'component/page'; import Page from 'component/page';
import Button from 'component/button'; import Button from 'component/button';
import ClaimTilesDiscover from 'component/claimTilesDiscover'; import ClaimTilesDiscover from 'component/claimTilesDiscover';
@ -74,6 +75,7 @@ function HomePage(props: Props) {
return ( return (
<div key={title} className="claim-grid__wrapper"> <div key={title} className="claim-grid__wrapper">
{/* category header */}
{index !== 0 && title && typeof title === 'string' && ( {index !== 0 && title && typeof title === 'string' && (
<h1 className="claim-grid__header"> <h1 className="claim-grid__header">
<Button navigate={route || link} button="link"> <Button navigate={route || link} button="link">
@ -91,6 +93,7 @@ function HomePage(props: Props) {
</WaitUntilOnPage> </WaitUntilOnPage>
)} )}
{/* view more button */}
{(route || link) && ( {(route || link) && (
<Button <Button
className="claim-grid__title--secondary" className="claim-grid__title--secondary"
@ -108,6 +111,135 @@ function HomePage(props: Props) {
doFetchActiveLivestreams(); doFetchActiveLivestreams();
}, []); }, []);
// returns true if passed element is fully visible on screen
function isScrolledIntoView(el) {
const rect = el.getBoundingClientRect();
const elemTop = rect.top;
const elemBottom = rect.bottom;
// Only completely visible elements return true:
const isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
return isVisible;
}
//
React.useEffect(() => {
if (authenticated || !SHOW_ADS) {
return;
}
(async function() {
// test if adblock is enabled
let adBlockEnabled = false;
const googleAdUrl = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
try {
await fetch(new Request(googleAdUrl)).catch(_ => { adBlockEnabled = true });
} catch (e) {
adBlockEnabled = true;
} finally {
if (!adBlockEnabled) {
// select the cards on page
let cards = document.getElementsByClassName('card claim-preview--tile');
// eslint-disable-next-line no-inner-declarations
function checkFlag() {
if (cards.length === 0) {
window.setTimeout(checkFlag, 100);
} else {
// find the last fully visible card
let lastCard;
for (const card of cards) {
const isFullyVisible = isScrolledIntoView(card);
if (!isFullyVisible) break;
lastCard = card;
}
// clone the last card
// $FlowFixMe
const clonedCard = lastCard.cloneNode(true);
// insert cloned card
// $FlowFixMe
lastCard.parentNode.insertBefore(clonedCard, lastCard);
// delete last card so that it doesn't mess up formatting
// $FlowFixMe
// lastCard.remove();
// change the appearance of the cloned card
// $FlowFixMe
clonedCard.querySelector('.claim__menu-button').remove();
// $FlowFixMe
clonedCard.querySelector('.truncated-text').innerHTML = 'Hate these? Login to Odysee for an ad free experience';
// $FlowFixMe
clonedCard.querySelector('.claim-tile__info').remove();
// $FlowFixMe
clonedCard.querySelector('[role="none"]').removeAttribute('href');
// $FlowFixMe
clonedCard.querySelector('.claim-tile__header').firstChild.href = '/$/signin';
// $FlowFixMe
clonedCard.querySelector('.claim-tile__title').firstChild.removeAttribute('aria-label');
// $FlowFixMe
clonedCard.querySelector('.claim-tile__title').firstChild.removeAttribute('title');
// $FlowFixMe
clonedCard.querySelector('.claim-tile__header').firstChild.removeAttribute('aria-label');
// $FlowFixMe
clonedCard.querySelector('.media__thumb').replaceWith(document.getElementsByClassName('homepageAdContainer')[0]);
// show the homepage ad which is not displayed at first
document.getElementsByClassName('homepageAdContainer')[0].style.display = 'block';
// $FlowFixMe
const imageHeight = window.getComputedStyle(lastCard.querySelector('.media__thumb')).height;
// $FlowFixMe
const imageWidth = window.getComputedStyle(lastCard.querySelector('.media__thumb')).width;
var styles = `#av-container, #AVcontent, #aniBox {
height: ${imageHeight} !important;
width: ${imageWidth} !important;
}`;
var styleSheet = document.createElement('style');
styleSheet.type = 'text/css';
styleSheet.innerText = styles;
// $FlowFixMe
document.head.appendChild(styleSheet);
clonedCard.style.display = 'none';
let timeoutCount = 0;
// eslint-disable-next-line no-inner-declarations
function checkForAniview() {
const aniBoxDiv = document.getElementsByClassName('homepageAdContainer')[0].querySelector('#aniBox');
if (!aniBoxDiv) {
timeoutCount += 100;
if (timeoutCount < 500) {
window.setTimeout(checkForAniview, 100);
} else {
}
} else {
clonedCard.style.display = 'block';
}
}
checkForAniview();
}
}
checkFlag();
}
}
})();
}, []);
return ( return (
<Page fullWidthPage> <Page fullWidthPage>
{!SIMPLE_SITE && (authenticated || !IS_WEB) && !subscribedChannels.length && ( {!SIMPLE_SITE && (authenticated || !IS_WEB) && !subscribedChannels.length && (
@ -126,6 +258,7 @@ function HomePage(props: Props) {
)} )}
{/* @if TARGET='web' */} {/* @if TARGET='web' */}
{SIMPLE_SITE && <Meme />} {SIMPLE_SITE && <Meme />}
<Ads type="homepage" />
{/* @endif */} {/* @endif */}
{rowData.map(({ title, route, link, icon, help, pinnedUrls: pinUrls, options = {} }, index) => { {rowData.map(({ title, route, link, icon, help, pinnedUrls: pinUrls, options = {} }, index) => {
// add pins here // add pins here

View file

@ -6,9 +6,6 @@ import { withRouter } from 'react-router';
import I18nMessage from 'component/i18nMessage'; import I18nMessage from 'component/i18nMessage';
import Button from 'component/button'; import Button from 'component/button';
import classnames from 'classnames'; import classnames from 'classnames';
// $FlowFixMe
const IS_MOBILE = typeof window.orientation !== 'undefined';
const ADS_URL = 'https://cdn.vidcrunch.com/integrations/618bb4d28aac298191eec411/Lbry_Odysee.com_Responsive_Floating_DFP_Rev70_1011.js'; const ADS_URL = 'https://cdn.vidcrunch.com/integrations/618bb4d28aac298191eec411/Lbry_Odysee.com_Responsive_Floating_DFP_Rev70_1011.js';
const ADS_TAG = 'vidcrunchJS537102317'; const ADS_TAG = 'vidcrunchJS537102317';
@ -16,6 +13,9 @@ const ADS_TAG = 'vidcrunchJS537102317';
const IOS_ADS_URL = 'https://cdn.vidcrunch.com/integrations/618bb4d28aac298191eec411/Lbry_Odysee.com_Mobile_Floating_DFP_Rev70_1611.js'; const IOS_ADS_URL = 'https://cdn.vidcrunch.com/integrations/618bb4d28aac298191eec411/Lbry_Odysee.com_Mobile_Floating_DFP_Rev70_1611.js';
const IOS_ADS_TAG = 'vidcrunchJS199212779'; const IOS_ADS_TAG = 'vidcrunchJS199212779';
const HOMEPAGE_ADS_URL = 'https://cdn.vidcrunch.com/integrations/618bb4d28aac298191eec411/Lbry_Odysee.com_Responsive_Floating_300x169_DFP_Rev70_1211.js';
const HOMEPAGE_ADS_TAG = 'vidcrunchJS330442776';
const IS_IOS = const IS_IOS =
(/iPad|iPhone|iPod/.test(navigator.platform) || (/iPad|iPhone|iPod/.test(navigator.platform) ||
// for iOS 13+ , platform is MacIntel, so use this to test // for iOS 13+ , platform is MacIntel, so use this to test
@ -33,22 +33,29 @@ type Props = {
function Ads(props: Props) { function Ads(props: Props) {
const { const {
location: { pathname }, location: { pathname },
type = 'sidebar', type = 'video',
small, small,
} = props; } = props;
// load ad and tags here
let scriptUrlToUse; let scriptUrlToUse;
let tagNameToUse; let tagNameToUse;
if (IS_IOS) { if (type === 'video') {
tagNameToUse = IOS_ADS_TAG; if (IS_IOS) {
scriptUrlToUse = IOS_ADS_URL; tagNameToUse = IOS_ADS_TAG;
} else { scriptUrlToUse = IOS_ADS_URL;
tagNameToUse = ADS_TAG; } else {
scriptUrlToUse = ADS_URL; tagNameToUse = ADS_TAG;
scriptUrlToUse = ADS_URL;
}
} else if (type === 'homepage') {
tagNameToUse = HOMEPAGE_ADS_TAG;
scriptUrlToUse = HOMEPAGE_ADS_URL;
} }
// add script to DOM
useEffect(() => { useEffect(() => {
if (SHOW_ADS && type === 'video') { if (SHOW_ADS) {
let script; let script;
try { try {
let fjs = document.getElementsByTagName('script')[0]; let fjs = document.getElementsByTagName('script')[0];
@ -56,30 +63,18 @@ function Ads(props: Props) {
script.src = scriptUrlToUse; script.src = scriptUrlToUse;
// $FlowFixMe // $FlowFixMe
fjs.parentNode.insertBefore(script, fjs); fjs.parentNode.insertBefore(script, fjs);
return () => {
// $FlowFixMe
document.head.removeChild(script);
};
} catch (e) {} } catch (e) {}
} }
// TODO: remove the script when it exists?
}, [type]); }, [type]);
useEffect(() => { // display to say "sign up to not see these"
if (SHOW_ADS && !IS_MOBILE && type === 'sidebar') {
const script = document.createElement('script');
script.src = ADS_URL;
script.defer = true;
// $FlowFixMe
document.body.appendChild(script);
return () => {
// $FlowFixMe
document.body.removeChild(script);
// if user navigates too rapidly, <style> tags can build up
// $FlowFixMe
if (document.body.getElementsByTagName('style').length) {
// $FlowFixMe
document.body.getElementsByTagName('style')[0].remove();
}
};
}
}, [type]);
const adsSignInDriver = ( const adsSignInDriver = (
<I18nMessage <I18nMessage
tokens={{ tokens={{
@ -96,6 +91,7 @@ function Ads(props: Props) {
</I18nMessage> </I18nMessage>
); );
// ad shown in the related videos area
const videoAd = ( const videoAd = (
<div className="ads__claim-item"> <div className="ads__claim-item">
<div id={tagNameToUse} className="ads__injected-video" style={{display: 'none'}} /> <div id={tagNameToUse} className="ads__injected-video" style={{display: 'none'}} />
@ -110,12 +106,22 @@ function Ads(props: Props) {
</div> </div>
); );
// homepage ad in a card
const homepageCardAd = (
<div className="homepageAdContainer media__thumb" style={{display: 'none'}}>
<div id={tagNameToUse} className="homepageAdDiv media__thumb" style={{display: 'none'}} />
</div>
);
if (!SHOW_ADS) { if (!SHOW_ADS) {
return false; return false;
} }
if (type === 'video') { if (type === 'video') {
return videoAd; return videoAd;
} }
if (type === 'homepage') {
return homepageCardAd;
}
} }
export default withRouter(Ads); export default withRouter(Ads);