Fix stale-closure in ad-detection code

Using that global variable was a bad idea. Stick to redux as the source of truth.

The flag is not listed for rehydration, so it will always start off as `undefined`, which is what we need anyways.

`undefined` indicates we haven't tested for ad-blockers, so we'll run the fetch and update the store with a true|false value.
This commit is contained in:
infinite-persistence 2022-05-30 18:50:15 +08:00
parent 1768e4a5cd
commit deb95cd443
No known key found for this signature in database
GPG key ID: B9C3252EDC3D0AA0
5 changed files with 15 additions and 27 deletions

View file

@ -3,13 +3,12 @@ import React from 'react';
import { SHOW_ADS } from 'config'; import { SHOW_ADS } from 'config';
const NO_COUNTRY_CHECK = true; const NO_COUNTRY_CHECK = true;
const GOOGLE_AD_URL = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js'; const GOOGLE_AD_URL = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
let ad_blocker_detected;
export default function useShouldShowAds( export default function useShouldShowAds(
hasPremiumPlus: boolean, hasPremiumPlus: boolean,
userCountry: string, userCountry: string,
isAdBlockerFound: ?boolean,
doSetAdBlockerFound: (boolean) => void doSetAdBlockerFound: (boolean) => void
) { ) {
const [shouldShowAds, setShouldShowAds] = React.useState(resolveAdVisibility()); const [shouldShowAds, setShouldShowAds] = React.useState(resolveAdVisibility());
@ -18,44 +17,26 @@ export default function useShouldShowAds(
// 'ad_blocker_detected' and 'hasPremiumPlus' will be undefined until // 'ad_blocker_detected' and 'hasPremiumPlus' will be undefined until
// fetched. Only show when it is exactly 'false'. // fetched. Only show when it is exactly 'false'.
return ( return (
SHOW_ADS && SHOW_ADS && (NO_COUNTRY_CHECK || userCountry === 'US') && isAdBlockerFound === false && hasPremiumPlus === false
(NO_COUNTRY_CHECK || userCountry === 'US') &&
ad_blocker_detected === false &&
hasPremiumPlus === false
); );
} }
// -- Check for ad-blockers
React.useEffect(() => { React.useEffect(() => {
if (ad_blocker_detected === undefined) { if (isAdBlockerFound === undefined) {
let mounted = true;
fetch(GOOGLE_AD_URL) fetch(GOOGLE_AD_URL)
.then((response) => { .then((response) => {
const detected = response.redirected === true; const detected = response.redirected === true;
ad_blocker_detected = detected; doSetAdBlockerFound(detected);
doSetAdBlockerFound(detected); // Also stash in redux for components to listen to.
}) })
.catch(() => { .catch(() => {
ad_blocker_detected = true;
doSetAdBlockerFound(true); doSetAdBlockerFound(true);
})
.finally(() => {
if (mounted) {
setShouldShowAds(resolveAdVisibility());
}
}); });
return () => {
mounted = false;
};
} }
}, []); }, []);
// --- Check for Premium+
React.useEffect(() => { React.useEffect(() => {
setShouldShowAds(resolveAdVisibility()); setShouldShowAds(resolveAdVisibility());
}, [hasPremiumPlus]); }, [hasPremiumPlus, isAdBlockerFound]);
return shouldShowAds; return shouldShowAds;
} }

View file

@ -1,5 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { doSetAdBlockerFound } from 'redux/actions/app'; import { doSetAdBlockerFound } from 'redux/actions/app';
import { selectAdBlockerFound } from 'redux/selectors/app';
import { makeSelectClaimForUri, selectClaimIsNsfwForUri } from 'redux/selectors/claims'; import { makeSelectClaimForUri, selectClaimIsNsfwForUri } from 'redux/selectors/claims';
import { selectOdyseeMembershipIsPremiumPlus, selectUserCountry } from 'redux/selectors/user'; import { selectOdyseeMembershipIsPremiumPlus, selectUserCountry } from 'redux/selectors/user';
import Ads from './view'; import Ads from './view';
@ -7,6 +8,7 @@ import Ads from './view';
const select = (state, props) => ({ const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
isMature: selectClaimIsNsfwForUri(state, props.uri), isMature: selectClaimIsNsfwForUri(state, props.uri),
isAdBlockerFound: selectAdBlockerFound(state),
userHasPremiumPlus: selectOdyseeMembershipIsPremiumPlus(state), userHasPremiumPlus: selectOdyseeMembershipIsPremiumPlus(state),
userCountry: selectUserCountry(state), userCountry: selectUserCountry(state),
}); });

View file

@ -59,6 +59,7 @@ type Props = {
className?: string, className?: string,
noFallback?: boolean, noFallback?: boolean,
// --- redux --- // --- redux ---
isAdBlockerFound: ?boolean,
userHasPremiumPlus: boolean, userHasPremiumPlus: boolean,
userCountry: string, userCountry: string,
doSetAdBlockerFound: (boolean) => void, doSetAdBlockerFound: (boolean) => void,
@ -69,6 +70,7 @@ function Ads(props: Props) {
type = 'video', type = 'video',
tileLayout, tileLayout,
small, small,
isAdBlockerFound,
userHasPremiumPlus, userHasPremiumPlus,
userCountry, userCountry,
className, className,
@ -76,7 +78,7 @@ function Ads(props: Props) {
doSetAdBlockerFound, doSetAdBlockerFound,
} = props; } = props;
const shouldShowAds = useShouldShowAds(userHasPremiumPlus, userCountry, doSetAdBlockerFound); const shouldShowAds = useShouldShowAds(userHasPremiumPlus, userCountry, isAdBlockerFound, doSetAdBlockerFound);
const adConfig = USE_ADNIMATION ? AD_CONFIGS.ADNIMATION : resolveVidcrunchConfig(); const adConfig = USE_ADNIMATION ? AD_CONFIGS.ADNIMATION : resolveVidcrunchConfig();
// add script to DOM // add script to DOM

View file

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

View file

@ -35,6 +35,7 @@ const adsSignInDriver = (
let gReferenceCounter = 0; let gReferenceCounter = 0;
type Props = { type Props = {
isAdBlockerFound: ?boolean,
userHasPremiumPlus: boolean, userHasPremiumPlus: boolean,
userCountry: string, userCountry: string,
currentTheme: string, currentTheme: string,
@ -42,8 +43,8 @@ type Props = {
}; };
export default function AdsBanner(props: Props) { export default function AdsBanner(props: Props) {
const { userHasPremiumPlus, userCountry, currentTheme, doSetAdBlockerFound } = props; const { isAdBlockerFound, userHasPremiumPlus, userCountry, currentTheme, doSetAdBlockerFound } = props;
const shouldShowAds = useShouldShowAds(userHasPremiumPlus, userCountry, doSetAdBlockerFound); const shouldShowAds = useShouldShowAds(userHasPremiumPlus, userCountry, isAdBlockerFound, doSetAdBlockerFound);
React.useEffect(() => { React.useEffect(() => {
if (shouldShowAds) { if (shouldShowAds) {