This commit is contained in:
infinite-persistence 2022-05-27 20:54:50 +08:00
commit 4d7f1c1d42
No known key found for this signature in database
GPG key ID: B9C3252EDC3D0AA0
12 changed files with 323 additions and 84 deletions

View file

@ -8,13 +8,7 @@ import {
} from 'redux/selectors/sync'; } from 'redux/selectors/sync';
import { doUserSetReferrer } from 'redux/actions/user'; import { doUserSetReferrer } from 'redux/actions/user';
import { doSetLastViewedAnnouncement } from 'redux/actions/content'; import { doSetLastViewedAnnouncement } from 'redux/actions/content';
import { import { selectUser, selectUserLocale, selectUserVerifiedEmail, selectHomepageFetched } from 'redux/selectors/user';
selectOdyseeMembershipIsPremiumPlus,
selectUser,
selectUserLocale,
selectUserVerifiedEmail,
selectHomepageFetched,
} from 'redux/selectors/user';
import { selectUnclaimedRewards } from 'redux/selectors/rewards'; import { selectUnclaimedRewards } from 'redux/selectors/rewards';
import { doFetchChannelListMine, doFetchCollectionListMine } from 'redux/actions/claims'; import { doFetchChannelListMine, doFetchCollectionListMine } from 'redux/actions/claims';
import { selectMyChannelClaimIds } from 'redux/selectors/claims'; import { selectMyChannelClaimIds } from 'redux/selectors/claims';
@ -49,7 +43,6 @@ const select = (state) => ({
syncFatalError: selectSyncFatalError(state), syncFatalError: selectSyncFatalError(state),
activeChannelClaim: selectActiveChannelClaim(state), activeChannelClaim: selectActiveChannelClaim(state),
myChannelClaimIds: selectMyChannelClaimIds(state), myChannelClaimIds: selectMyChannelClaimIds(state),
hasPremiumPlus: selectOdyseeMembershipIsPremiumPlus(state),
homepageFetched: selectHomepageFetched(state), homepageFetched: selectHomepageFetched(state),
defaultChannelClaim: selectDefaultChannelClaim(state), defaultChannelClaim: selectDefaultChannelClaim(state),
}); });

View file

@ -14,7 +14,6 @@ import useKonamiListener from 'util/enhanced-layout';
import Yrbl from 'component/yrbl'; import Yrbl from 'component/yrbl';
import FileRenderFloating from 'component/fileRenderFloating'; import FileRenderFloating from 'component/fileRenderFloating';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import useAdOutbrain from 'effects/use-ad-outbrain';
import usePrevious from 'effects/use-previous'; import usePrevious from 'effects/use-previous';
import Nag from 'component/common/nag'; import Nag from 'component/common/nag';
import REWARDS from 'rewards'; import REWARDS from 'rewards';
@ -83,7 +82,6 @@ type Props = {
syncFatalError: boolean, syncFatalError: boolean,
activeChannelClaim: ?ChannelClaim, activeChannelClaim: ?ChannelClaim,
myChannelClaimIds: ?Array<string>, myChannelClaimIds: ?Array<string>,
hasPremiumPlus: ?boolean,
setIncognito: (boolean) => void, setIncognito: (boolean) => void,
fetchModBlockedList: () => void, fetchModBlockedList: () => void,
fetchModAmIList: () => void, fetchModAmIList: () => void,
@ -122,7 +120,6 @@ function App(props: Props) {
activeChannelClaim, activeChannelClaim,
setIncognito, setIncognito,
fetchModBlockedList, fetchModBlockedList,
hasPremiumPlus,
fetchModAmIList, fetchModAmIList,
homepageFetched, homepageFetched,
defaultChannelClaim, defaultChannelClaim,
@ -509,8 +506,6 @@ function App(props: Props) {
useDegradedPerformance(setLbryTvApiStatus, user); useDegradedPerformance(setLbryTvApiStatus, user);
useAdOutbrain(Boolean(hasPremiumPlus), isAuthenticated, history?.location?.pathname);
useEffect(() => { useEffect(() => {
// When language is changed or translations are fetched, we render. // When language is changed or translations are fetched, we render.
setLangRenderKey(Date.now()); setLangRenderKey(Date.now());

View file

@ -166,7 +166,13 @@ function ChannelContent(props: Props) {
infiniteScroll={defaultInfiniteScroll} infiniteScroll={defaultInfiniteScroll}
injectedItem={ injectedItem={
!hasPremiumPlus && { !hasPremiumPlus && {
node: <Ads small type="video" tileLayout />, node: (index, lastVisibleIndex, pageSize) => {
if (pageSize && index < pageSize) {
return index === lastVisibleIndex ? <Ads type="video" tileLayout={tileLayout} small /> : null;
} else {
return index % (pageSize * 2) === 0 ? <Ads type="video" tileLayout={tileLayout} small /> : null;
}
},
} }
} }
meta={ meta={

View file

@ -17,25 +17,25 @@ const OUTBRAIN_CONTAINER_KEY = 'outbrainSizeDiv';
let script; let script;
/** /**
* @param hasPremiumPlus * @param hasPremiumPlus `undefined` if not yet fetched, boolean otherwise.
* @param isAuthenticated * @param isAuthenticated `undefined` if email is not fetched, boolean
* otherwise.
* @param pathname Reminder: the component using this effect must be listening * @param pathname Reminder: the component using this effect must be listening
* to path changes (e.g. useHistory, etc.). This value must not * to path changes (e.g. useHistory, etc.). This value must not
* come from window.location.pathname, which doesn't spark an * come from window.location.pathname, which doesn't spark an
* update. * update.
*/ */
export default function useAdOutbrain(hasPremiumPlus: boolean, isAuthenticated: boolean, pathname: string) { export default function useAdOutbrain(hasPremiumPlus: ?boolean, isAuthenticated: ?boolean, pathname: string) {
// Only look at authentication for now. Eventually, we'll only use 'hasPremiumPlus'. // Still need to look at `isAuthenticated` because `hasPremiumPlus` remains
// Authenticated will return undefined if not yet populated, so wait and only show // in unfetched (`undefined) state in Incognito.
// when returned as false const loadScript = isAuthenticated === false || hasPremiumPlus === false;
const isNotAuthenticated = isAuthenticated === false;
const propRef = React.useRef({ isAuthenticated, pathname }); const propRef = React.useRef({ hasPremiumPlus, pathname });
propRef.current = { isAuthenticated, pathname }; propRef.current = { hasPremiumPlus, pathname };
function resolveVisibility() { function resolveVisibility() {
if (window[OUTBRAIN_CONTAINER_KEY]) { if (window[OUTBRAIN_CONTAINER_KEY]) {
if (propRef.current.isAuthenticated) { if (propRef.current.hasPremiumPlus) {
window[OUTBRAIN_CONTAINER_KEY].style.display = 'none'; window[OUTBRAIN_CONTAINER_KEY].style.display = 'none';
} else { } else {
window[OUTBRAIN_CONTAINER_KEY].style.display = EXCLUDED_PATHS.includes(propRef.current.pathname) ? 'none' : ''; window[OUTBRAIN_CONTAINER_KEY].style.display = EXCLUDED_PATHS.includes(propRef.current.pathname) ? 'none' : '';
@ -44,7 +44,7 @@ export default function useAdOutbrain(hasPremiumPlus: boolean, isAuthenticated:
} }
React.useEffect(() => { React.useEffect(() => {
if (!inIFrame() && isNotAuthenticated && !script) { if (!inIFrame() && loadScript && !script) {
const loadTimer = setTimeout(() => { const loadTimer = setTimeout(() => {
script = document.createElement('script'); script = document.createElement('script');
script.src = 'https://adncdnend.azureedge.net/adtags/odysee.adn.js'; script.src = 'https://adncdnend.azureedge.net/adtags/odysee.adn.js';
@ -57,10 +57,9 @@ export default function useAdOutbrain(hasPremiumPlus: boolean, isAuthenticated:
return () => clearTimeout(loadTimer); return () => clearTimeout(loadTimer);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps }, [loadScript]);
}, [isNotAuthenticated]);
React.useEffect(() => { React.useEffect(() => {
resolveVisibility(); resolveVisibility();
}, [isAuthenticated, pathname]); }, [hasPremiumPlus, pathname]);
} }

View file

@ -0,0 +1,50 @@
// @flow
import React from 'react';
import { SHOW_ADS } from 'config';
const NO_COUNTRY_CHECK = true;
const GOOGLE_AD_URL = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
let ad_blocker_detected;
export default function useShouldShowAds(
hasPremiumPlus: boolean,
userCountry: string,
doSetAdBlockerFound: (boolean) => void
) {
const [shouldShowAds, setShouldShowAds] = React.useState(resolveAdVisibility());
function resolveAdVisibility() {
// 'ad_blocker_detected' will be undefined at startup. Wait until we are
// sure it is not blocked (i.e. === false) before showing the component.
return ad_blocker_detected === false && SHOW_ADS && !hasPremiumPlus && (NO_COUNTRY_CHECK || userCountry === 'US');
}
React.useEffect(() => {
if (ad_blocker_detected === undefined) {
let mounted = true;
fetch(GOOGLE_AD_URL)
.then((response) => {
const detected = response.redirected === true;
ad_blocker_detected = detected;
doSetAdBlockerFound(detected); // Also stash in redux for components to listen to.
})
.catch(() => {
ad_blocker_detected = true;
doSetAdBlockerFound(true);
})
.finally(() => {
if (mounted) {
setShouldShowAds(resolveAdVisibility());
}
});
return () => {
mounted = false;
};
}
}, []);
return shouldShowAds;
}

View file

@ -223,7 +223,18 @@ function DiscoverPage(props: Props) {
tags={tags} tags={tags}
hiddenNsfwMessage={<HiddenNsfw type="page" />} hiddenNsfwMessage={<HiddenNsfw type="page" />}
repostedClaimId={repostedClaim ? repostedClaim.claim_id : null} repostedClaimId={repostedClaim ? repostedClaim.claim_id : null}
injectedItem={!isWildWest && !hasPremiumPlus && { node: <Ads small type="video" tileLayout /> }} injectedItem={
!isWildWest &&
!hasPremiumPlus && {
node: (index, lastVisibleIndex, pageSize) => {
if (pageSize && index < pageSize) {
return index === lastVisibleIndex ? <Ads small type="video" tileLayout={tileLayout} /> : null;
} else {
return index % (pageSize * 2) === 0 ? <Ads small type="video" tileLayout={tileLayout} /> : null;
}
},
}
}
// TODO: find a better way to determine discover / wild west vs other modes release times // TODO: find a better way to determine discover / wild west vs other modes release times
// for now including && !tags so that // for now including && !tags so that
releaseTime={releaseTime || undefined} releaseTime={releaseTime || undefined}

View file

@ -34,6 +34,7 @@ const select = (state) => ({
homepageOrder: selectClientSetting(state, SETTINGS.HOMEPAGE_ORDER), homepageOrder: selectClientSetting(state, SETTINGS.HOMEPAGE_ORDER),
hasMembership: selectHasOdyseeMembership(state), hasMembership: selectHasOdyseeMembership(state),
hasPremiumPlus: selectOdyseeMembershipIsPremiumPlus(state), hasPremiumPlus: selectOdyseeMembershipIsPremiumPlus(state),
currentTheme: selectClientSetting(state, SETTINGS.THEME),
}); });
const perform = (dispatch) => ({ const perform = (dispatch) => ({

View file

@ -15,12 +15,13 @@ import Icon from 'component/common/icon';
import WaitUntilOnPage from 'component/common/wait-until-on-page'; import WaitUntilOnPage from 'component/common/wait-until-on-page';
import RecommendedPersonal from 'component/recommendedPersonal'; import RecommendedPersonal from 'component/recommendedPersonal';
import Yrbl from 'component/yrbl'; import Yrbl from 'component/yrbl';
import { useIsLargeScreen } from 'effects/use-screensize'; import { useIsLargeScreen, useIsMobile } from 'effects/use-screensize';
import { GetLinksData } from 'util/buildHomepage'; import { GetLinksData } from 'util/buildHomepage';
import { getLivestreamUris } from 'util/livestream'; import { getLivestreamUris } from 'util/livestream';
import ScheduledStreams from 'component/scheduledStreams'; import ScheduledStreams from 'component/scheduledStreams';
import { splitBySeparator } from 'util/lbryURI'; import { splitBySeparator } from 'util/lbryURI';
import Ads from 'web/component/ads'; import Ads from 'web/component/ads';
import AdsBanner from 'web/component/adsBanner';
import Meme from 'web/component/meme'; import Meme from 'web/component/meme';
const CATEGORY_LIVESTREAM_LIMIT = 3; const CATEGORY_LIVESTREAM_LIMIT = 3;
@ -43,6 +44,7 @@ type Props = {
doOpenModal: (id: string, ?{}) => void, doOpenModal: (id: string, ?{}) => void,
hasMembership: ?boolean, hasMembership: ?boolean,
hasPremiumPlus: boolean, hasPremiumPlus: boolean,
currentTheme: string,
}; };
function HomePage(props: Props) { function HomePage(props: Props) {
@ -62,12 +64,14 @@ function HomePage(props: Props) {
doOpenModal, doOpenModal,
hasMembership, hasMembership,
hasPremiumPlus, hasPremiumPlus,
currentTheme,
} = props; } = props;
const showPersonalizedChannels = (authenticated || !IS_WEB) && subscribedChannels && subscribedChannels.length > 0; const showPersonalizedChannels = (authenticated || !IS_WEB) && subscribedChannels && subscribedChannels.length > 0;
const showPersonalizedTags = (authenticated || !IS_WEB) && followedTags && followedTags.length > 0; const showPersonalizedTags = (authenticated || !IS_WEB) && followedTags && followedTags.length > 0;
const showIndividualTags = showPersonalizedTags && followedTags.length < 5; const showIndividualTags = showPersonalizedTags && followedTags.length < 5;
const isLargeScreen = useIsLargeScreen(); const isLargeScreen = useIsLargeScreen();
const isMobileScreen = useIsMobile();
const subscriptionChannelIds = subscribedChannels.map((sub) => splitBySeparator(sub.uri)[1]); const subscriptionChannelIds = subscribedChannels.map((sub) => splitBySeparator(sub.uri)[1]);
const rowData: Array<RowDataItem> = GetLinksData( const rowData: Array<RowDataItem> = GetLinksData(
@ -182,6 +186,8 @@ function HomePage(props: Props) {
label={__('View More')} label={__('View More')}
/> />
)} )}
{isMobileScreen && <AdsBanner key={`${currentTheme}:${title}`} />}
{!isMobileScreen && (index === 0 || index % 2 === 0) && <AdsBanner key={`${currentTheme}:${title}`} />}
</> </>
)} )}
</div> </div>

View file

@ -319,6 +319,7 @@
height: 27px !important; height: 27px !important;
} }
/*
.ob-widget-footer { .ob-widget-footer {
position: absolute !important; position: absolute !important;
right: 32px; right: 32px;
@ -339,6 +340,7 @@
display: none !important; display: none !important;
} }
} }
*/
.ob-widget-items-container { .ob-widget-items-container {
padding-left: var(--spacing-xs); padding-left: var(--spacing-xs);
@ -366,3 +368,81 @@
#av-container #av-inner #gui #timeline #timeline-progress { #av-container #av-inner #gui #timeline #timeline-progress {
background: var(--color-primary) !important; background: var(--color-primary) !important;
} }
// ****************************************************************************
// Banner
// ****************************************************************************
$BANNER_BORDER: 1px;
.banner-ad {
margin-top: var(--spacing-l);
background-color: var(--color-ads-background);
border: $BANNER_BORDER solid var(--color-border);
border-radius: var(--border-radius);
}
.banner-ad__container {
min-height: 250px;
padding: var(--spacing-xxs) var(--spacing-m);
@media (max-width: $breakpoint-small) {
padding: var(--spacing-xxs) var(--spacing-xxs);
}
@media (min-width: $breakpoint-small) {
margin: auto;
width: 100%;
}
@media (min-width: $breakpoint-xlarge) {
max-width: calc(#{$breakpoint-xlarge} * 3 / 4);
}
}
.banner-ad__driver {
display: flex;
flex-direction: row;
max-width: 50%;
border-left: $BANNER_BORDER solid var(--color-border);
border-bottom: $BANNER_BORDER solid var(--color-border);
border-radius: 0 var(--border-radius) 0 var(--border-radius);
float: right;
font-size: var(--font-xsmall);
@media (max-width: $breakpoint-small) {
font-size: var(--font-xxsmall);
}
> * {
padding: 0 var(--spacing-xxxs);
}
}
.banner-ad__driver-label {
color: var(--color-text-subtitle);
}
.banner-ad__driver-value {
color: var(--color-text-subtitle);
border-left: $BANNER_BORDER solid var(--color-border);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@supports (-webkit-line-clamp: 3) {
white-space: initial;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
@media (max-width: $breakpoint-small) {
line-height: 1.2;
padding: calc(var(--spacing-xxxs) / 2) var(--spacing-xxxs);
}
}

View file

@ -1,16 +1,18 @@
// @flow // @flow
import { SHOW_ADS } from 'config';
import * as PAGES from 'constants/pages'; import * as PAGES from 'constants/pages';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import I18nMessage from 'component/i18nMessage'; import I18nMessage from 'component/i18nMessage';
import Button from 'component/button'; import Button from 'component/button';
import PremiumPlusTile from 'component/premiumPlusTile'; import PremiumPlusTile from 'component/premiumPlusTile';
import classnames from 'classnames'; import classnames from 'classnames';
import useShouldShowAds from 'effects/use-should-show-ads';
import { platform } from 'util/platform'; import { platform } from 'util/platform';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import { LocalStorage, LS } from 'util/storage'; import { LocalStorage, LS } from 'util/storage';
const USE_ADNIMATION = true;
// prettier-ignore // prettier-ignore
const AD_CONFIGS = Object.freeze({ const AD_CONFIGS = Object.freeze({
DEFAULT: { DEFAULT: {
@ -25,10 +27,30 @@ const AD_CONFIGS = Object.freeze({
url: 'https://tg1.vidcrunch.com/api/adserver/spt?AV_TAGID=61dff05c599f1e20b01085d4&AV_PUBLISHERID=6182c8993c8ae776bd5635e9', url: 'https://tg1.vidcrunch.com/api/adserver/spt?AV_TAGID=61dff05c599f1e20b01085d4&AV_PUBLISHERID=6182c8993c8ae776bd5635e9',
tag: 'AV61dff05c599f1e20b01085d4', tag: 'AV61dff05c599f1e20b01085d4',
}, },
ADNIMATION: {
url: 'https://tg1.aniview.com/api/adserver/spt?AV_TAGID=6252bb6f28951333ec10a7a6&AV_PUBLISHERID=601d9a7f2e688a79e17c1265',
tag: 'AV6252bb6f28951333ec10a7a6',
},
}); });
// Internal use only. One-time update flag. // ****************************************************************************
let ad_blocker_detected; // Helpers
// ****************************************************************************
function removeIfExists(querySelector) {
const element = document.querySelector(querySelector);
if (element) element.remove();
}
function resolveVidcrunchConfig() {
const mobileAds = platform.isAndroid() || platform.isIOS();
const isInEu = LocalStorage.getItem(LS.GDPR_REQUIRED) === 'true';
return isInEu ? AD_CONFIGS.EU : mobileAds ? AD_CONFIGS.MOBILE : AD_CONFIGS.DEFAULT;
}
// ****************************************************************************
// Ads
// ****************************************************************************
type Props = { type Props = {
type: string, type: string,
@ -37,18 +59,11 @@ type Props = {
className?: string, className?: string,
noFallback?: boolean, noFallback?: boolean,
// --- redux --- // --- redux ---
claim: Claim,
isMature: boolean,
userHasPremiumPlus: boolean, userHasPremiumPlus: boolean,
userCountry: string, userCountry: string,
doSetAdBlockerFound: (boolean) => void, doSetAdBlockerFound: (boolean) => void,
}; };
function removeIfExists(querySelector) {
const element = document.querySelector(querySelector);
if (element) element.remove();
}
function Ads(props: Props) { function Ads(props: Props) {
const { const {
type = 'video', type = 'video',
@ -61,46 +76,8 @@ function Ads(props: Props) {
doSetAdBlockerFound, doSetAdBlockerFound,
} = props; } = props;
const [shouldShowAds, setShouldShowAds] = React.useState(resolveAdVisibility()); const shouldShowAds = useShouldShowAds(userHasPremiumPlus, userCountry, doSetAdBlockerFound);
const mobileAds = platform.isAndroid() || platform.isIOS(); const adConfig = USE_ADNIMATION ? AD_CONFIGS.ADNIMATION : resolveVidcrunchConfig();
// this is populated from app based on location
const isInEu = LocalStorage.getItem(LS.GDPR_REQUIRED) === 'true';
const adConfig = isInEu ? AD_CONFIGS.EU : mobileAds ? AD_CONFIGS.MOBILE : AD_CONFIGS.DEFAULT;
function resolveAdVisibility() {
// 'ad_blocker_detected' will be undefined at startup. Wait until we are
// sure it is not blocked (i.e. === false) before showing the component.
return ad_blocker_detected === false && SHOW_ADS && !userHasPremiumPlus && userCountry === 'US';
}
useEffect(() => {
if (ad_blocker_detected === undefined) {
let mounted = true;
const GOOGLE_AD_URL = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
fetch(GOOGLE_AD_URL)
.then((response) => {
const detected = response.redirected === true;
window.odysee_ad_blocker_detected = detected;
ad_blocker_detected = detected;
doSetAdBlockerFound(detected);
})
.catch(() => {
ad_blocker_detected = true;
doSetAdBlockerFound(true);
})
.finally(() => {
if (mounted) {
setShouldShowAds(resolveAdVisibility());
}
});
return () => {
mounted = false;
};
}
}, []);
// add script to DOM // add script to DOM
useEffect(() => { useEffect(() => {
@ -122,11 +99,20 @@ function Ads(props: Props) {
delete window.__VIDCRUNCH_CONFIG_618bb4d28aac298191eec411__; delete window.__VIDCRUNCH_CONFIG_618bb4d28aac298191eec411__;
delete window.__player_618bb4d28aac298191eec411__; delete window.__player_618bb4d28aac298191eec411__;
const styles = document.querySelectorAll('body > style');
styles.forEach((s) => {
// We are asking Adnimation to supply us with a specific ID or
// pattern so that our query wouldn't break when they change their
// script. For now, this is the "best effort".
if (s.innerText && s.innerText.startsWith('#outbrain')) {
s.remove();
}
});
// clean DOM elements from ad related elements // clean DOM elements from ad related elements
removeIfExists('[src^="https://cdn.vidcrunch.com/618bb4d28aac298191eec411.js"]'); removeIfExists('[src^="https://player.avplayer.com"]');
removeIfExists('[src^="https://player.aniview.com/script/6.1/aniview.js"]'); removeIfExists('[src^="https://gum.criteo.com"]');
removeIfExists('[id^="AVLoaderaniplayer_vidcrunch"]'); removeIfExists('[id^="AVLoaderaniview_slot"]');
removeIfExists('#av_css_id');
}; };
} catch (e) {} } catch (e) {}
} }

View file

@ -0,0 +1,18 @@
import { connect } from 'react-redux';
import * as SETTINGS from 'constants/settings';
import { doSetAdBlockerFound } from 'redux/actions/app';
import { selectClientSetting } from 'redux/selectors/settings';
import { selectOdyseeMembershipIsPremiumPlus, selectUserCountry } from 'redux/selectors/user';
import AdsBanner from './view';
const select = (state, props) => ({
userHasPremiumPlus: selectOdyseeMembershipIsPremiumPlus(state),
userCountry: selectUserCountry(state),
currentTheme: selectClientSetting(state, SETTINGS.THEME),
});
const perform = {
doSetAdBlockerFound,
};
export default connect(select, perform)(AdsBanner);

View file

@ -0,0 +1,94 @@
// @flow
import React from 'react';
import Button from 'component/button';
import I18nMessage from 'component/i18nMessage';
import * as PAGES from 'constants/pages';
import useShouldShowAds from 'effects/use-should-show-ads';
const AD_SCRIPT_URL = 'https://widgets.outbrain.com/outbrain.js';
const AD_CONFIG = {
AR_18: 'AR_18', // 5 tiles.
AR_60: 'AR_60', // 6 tiles. Doesn't work well on mobile (6 tiles compresses to 1, text only).
AR_3: 'AR_3', // 4 tiles on desktop, dynamic count on mobile.
};
// ****************************************************************************
// ****************************************************************************
const adsSignInDriver = (
<I18nMessage
tokens={{
sign_up_for_premium: (
<Button button="link" label={__('Get Odysee Premium+')} navigate={`/$/${PAGES.ODYSEE_MEMBERSHIP}`} />
),
}}
>
%sign_up_for_premium% for an ad free experience.
</I18nMessage>
);
// ****************************************************************************
// AdsBanner
// ****************************************************************************
let gReferenceCounter = 0;
type Props = {
userHasPremiumPlus: boolean,
userCountry: string,
currentTheme: string,
doSetAdBlockerFound: (boolean) => void,
};
export default function AdsBanner(props: Props) {
const { userHasPremiumPlus, userCountry, currentTheme, doSetAdBlockerFound } = props;
const shouldShowAds = useShouldShowAds(userHasPremiumPlus, userCountry, doSetAdBlockerFound);
React.useEffect(() => {
if (shouldShowAds) {
try {
const script = document.createElement('script');
script.src = AD_SCRIPT_URL;
script.async = true;
script.onload = () => {
++gReferenceCounter;
};
// $FlowFixMe
document.body.appendChild(script);
return () => {
// $FlowFixMe
document.body.removeChild(script);
if (--gReferenceCounter <= 0) {
// Note: This method has the bad requirement of the parent having to
// mount and dismount all banners in the same cycle.
delete window.OBR;
// TODO: clear styles after the team adds an ID or class for us to query.
}
};
} catch (e) {}
}
}, [shouldShowAds]);
if (!shouldShowAds) {
return null;
}
return (
<div className="banner-ad">
<div className="banner-ad__driver">
<div className="banner-ad__driver-label">Ad</div>
<div className="banner-ad__driver-value">{adsSignInDriver}</div>
</div>
<div
className="banner-ad__container OUTBRAIN"
data-ob-contenturl="DROP_PERMALINK_HERE"
data-widget-id={AD_CONFIG.AR_18}
data-ob-installation-key="ADNIMKAJDGAG4GAO6AGG6H5KP"
data-dark-mode={currentTheme === 'dark' ? 'true' : 'false'}
/>
</div>
);
}