Re-implement/enable sticky ad

## Ticket
1583: "add incontent ads to category/channel pages (break up every X claims?), or add back bottom ad in those areas."

## Behavioral changes
- Hide when in homepage (as currently we have ads between categories).
- Fix the light theme (it was transparent).

## Code changes
- While an Effect is the 'right' choice given that there is no jsx to mount, the need to prop-drill from the parent was getting a bit annoying, so converted to a Component instead.
- Remove the delay for Core Vitals avoidance for now -- seems to make the sticky less likely to serve an ad.
- Now that the membership state is correctly populated for incognito (see 9d830615), there is no more need to check for `isAuthenticated`.
This commit is contained in:
infinite-persistence 2022-05-31 17:19:21 +08:00 committed by Thomas Zarebczan
parent 4ea6b848d1
commit 0bcea943d5
5 changed files with 105 additions and 65 deletions

View file

@ -21,6 +21,7 @@ import usePersistedState from 'effects/use-persisted-state';
import useConnectionStatus from 'effects/use-connection-status';
import Spinner from 'component/spinner';
import LANGUAGES from 'constants/languages';
import AdsSticky from 'web/component/adsSticky';
import YoutubeWelcome from 'web/component/youtubeReferralWelcome';
import {
useDegradedPerformance,
@ -558,6 +559,8 @@ function App(props: Props) {
)}
{getStatusNag()}
</React.Suspense>
<AdsSticky />
</React.Fragment>
)}
</div>

View file

@ -1,65 +0,0 @@
// @flow
import React from 'react';
import * as PAGES from 'constants/pages';
function inIFrame() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
const EXCLUDED_PATHS = Object.freeze([`/$/${PAGES.AUTH}`, `/$/${PAGES.AUTH_SIGNIN}`, `/$/${PAGES.AUTH_VERIFY}`]);
const LOAD_AD_DELAY_MS = 3000; // Wait past boot-up and core-vitals period.
const OUTBRAIN_CONTAINER_KEY = 'outbrainSizeDiv';
let script;
/**
* @param hasPremiumPlus `undefined` if not yet fetched, boolean otherwise.
* @param isAuthenticated `undefined` if email is not fetched, boolean
* otherwise.
* @param pathname Reminder: the component using this effect must be listening
* to path changes (e.g. useHistory, etc.). This value must not
* come from window.location.pathname, which doesn't spark an
* update.
*/
export default function useAdOutbrain(hasPremiumPlus: ?boolean, isAuthenticated: ?boolean, pathname: string) {
// Still need to look at `isAuthenticated` because `hasPremiumPlus` remains
// in unfetched (`undefined) state in Incognito.
const loadScript = isAuthenticated === false || hasPremiumPlus === false;
const propRef = React.useRef({ hasPremiumPlus, pathname });
propRef.current = { hasPremiumPlus, pathname };
function resolveVisibility() {
if (window[OUTBRAIN_CONTAINER_KEY]) {
if (propRef.current.hasPremiumPlus) {
window[OUTBRAIN_CONTAINER_KEY].style.display = 'none';
} else {
window[OUTBRAIN_CONTAINER_KEY].style.display = EXCLUDED_PATHS.includes(propRef.current.pathname) ? 'none' : '';
}
}
}
React.useEffect(() => {
if (!inIFrame() && loadScript && !script) {
const loadTimer = setTimeout(() => {
script = document.createElement('script');
script.src = 'https://adncdnend.azureedge.net/adtags/odysee.adn.js';
script.async = true;
script.addEventListener('load', resolveVisibility);
// $FlowFixMe
document.body.appendChild(script);
}, LOAD_AD_DELAY_MS);
return () => clearTimeout(loadTimer);
}
}, [loadScript]);
React.useEffect(() => {
resolveVisibility();
}, [hasPremiumPlus, pathname]);
}

View file

@ -323,6 +323,8 @@
.ob-widget .ob-unit.ob-rec-text {
font-size: 12px !important;
}
background-color: var(--color-ads-background);
}
.closeButton {

View file

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

View file

@ -0,0 +1,80 @@
// @flow
import React from 'react';
import { useHistory } from 'react-router-dom';
import * as PAGES from 'constants/pages';
import useShouldShowAds from 'effects/use-should-show-ads';
// ****************************************************************************
// AdsSticky
// ****************************************************************************
const PATH_BLACKLIST = [
// Don't show sticky in these paths:
{ path: `/`, exact: true },
{ path: `/$/${PAGES.AUTH}`, exact: false },
{ path: `/$/${PAGES.AUTH_SIGNIN}`, exact: false },
{ path: `/$/${PAGES.AUTH_VERIFY}`, exact: false },
{ path: `/$/${PAGES.SETTINGS}`, exact: false },
];
const OUTBRAIN_CONTAINER_KEY = 'outbrainSizeDiv';
let gScript;
type Props = {
isAdBlockerFound: ?boolean,
userHasPremiumPlus: boolean,
userCountry: string,
currentTheme: string,
doSetAdBlockerFound: (boolean) => void,
};
export default function AdsSticky(props: Props) {
const { isAdBlockerFound, userHasPremiumPlus, userCountry, doSetAdBlockerFound } = props;
const shouldShowAds = useShouldShowAds(userHasPremiumPlus, userCountry, isAdBlockerFound, doSetAdBlockerFound);
const {
location: { pathname },
} = useHistory();
// -- Mount script; 1 per session.
React.useEffect(() => {
if (shouldShowAds && !gScript && !inIFrame()) {
gScript = document.createElement('script');
gScript.src = 'https://adncdnend.azureedge.net/adtags/odysee.adn.js';
gScript.async = true;
// $FlowFixMe
document.body.appendChild(gScript);
}
}, [shouldShowAds]);
// -- Update visibility per pathname
React.useEffect(() => {
const container = window[OUTBRAIN_CONTAINER_KEY];
if (container) {
for (const x of PATH_BLACKLIST) {
const found = (x.exact && pathname === x.path) || (!x.exact && pathname.startsWith(x.path));
if (found) {
container.style.display = 'none';
return;
}
}
container.style.display = '';
}
}, [pathname]);
// Nothing for us to mount; the ad script will handle everything.
return null;
}
// ****************************************************************************
// Helpers
// ****************************************************************************
function inIFrame() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}