Skip to content

Commit

Permalink
TW-1518: Ads replacement algorithm improvement (#1196)
Browse files Browse the repository at this point in the history
* TW-1518 Implement blacklisting Hypelab campaigns

* TW-1518 Fix a type error

* TW-1518 Change the algorithm for picking ad type

* TW-1518 Revert harmful changes

* TW-1518 Restore using Persona 321x101 banner as one more fallback

* TW-1518 Update @temple-wallet/extension-ads
  • Loading branch information
keshan3262 authored Sep 25, 2024
1 parent 86ea1ff commit 0be3314
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 95 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,6 @@
"follow-redirects": "^1.15.4"
},
"optionalDependencies": {
"@temple-wallet/extension-ads": "8.1.1"
"@temple-wallet/extension-ads": "9.0.0-dev.2"
}
}
15 changes: 10 additions & 5 deletions src/content-scripts/replace-ads/ads-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { importExtensionAdsModule } from 'lib/ads/import-extension-ads-module';
import { ALL_ADS_RULES_STORAGE_KEY, ADS_RULES_UPDATE_INTERVAL } from 'lib/constants';
import { fetchFromStorage } from 'lib/storage';

export const getRulesFromContentScript = memoizee(
async (location: Location) => {
export const getRulesFromStorage = memoizee(
async (locationOrHref: Location | string) => {
try {
const { transformRawRules } = await importExtensionAdsModule();
const rulesStored = await fetchFromStorage(ALL_ADS_RULES_STORAGE_KEY);

if (!rulesStored) throw new Error('No rules for ads found');

return transformRawRules(location, rulesStored);
return transformRawRules(locationOrHref, rulesStored);
} catch (error) {
console.error(error);

Expand All @@ -22,11 +22,16 @@ export const getRulesFromContentScript = memoizee(
providersSelectors: [],
providersNegativeSelectors: [],
elementsToHideOrRemoveRules: [],
blacklistedHypelabCampaignsSlugs: [],
timestamp: 0
};
}
},
{ maxAge: ADS_RULES_UPDATE_INTERVAL, normalizer: ([location]) => location.href, promise: true }
{
maxAge: ADS_RULES_UPDATE_INTERVAL,
normalizer: ([locationOrHref]) => (typeof locationOrHref === 'string' ? locationOrHref : locationOrHref.href),
promise: true
}
);

export const clearRulesCache = () => getRulesFromContentScript.clear();
export const clearRulesCache = () => getRulesFromStorage.clear();
19 changes: 17 additions & 2 deletions src/content-scripts/replace-ads/ads-stack.iframe.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
import { configureAds } from 'lib/ads/configure-ads';
import { importExtensionAdsModule } from 'lib/ads/import-extension-ads-module';
import { ADS_META_SEARCH_PARAM_NAME, ORIGIN_SEARCH_PARAM_NAME } from 'lib/constants';
import { ADS_META_SEARCH_PARAM_NAME, AD_CATEGORIES_PARAM_NAME, ORIGIN_SEARCH_PARAM_NAME } from 'lib/constants';

import { getRulesFromStorage } from './ads-rules';

const usp = new URLSearchParams(window.location.search);
const id = usp.get('id');
const origin = usp.get(ORIGIN_SEARCH_PARAM_NAME) ?? window.location.href;
const adsMetadataIds = usp.getAll(ADS_META_SEARCH_PARAM_NAME).map(value => JSON.parse(value));
const adCategories = usp.getAll(AD_CATEGORIES_PARAM_NAME);

configureAds()
.then(() => importExtensionAdsModule())
.then(({ renderAdsStack }) => renderAdsStack(id ?? '', adsMetadataIds, origin))
.then(async ({ renderAdsStack }) => ({ renderAdsStack, rules: await getRulesFromStorage(origin) }))
.then(({ renderAdsStack, rules }) => {
const { blacklistedHypelabCampaignsSlugs, permanentAdPlacesRules, adPlacesRules } = rules;

renderAdsStack(
id ?? '',
adsMetadataIds,
origin,
permanentAdPlacesRules.length > 0 || adPlacesRules.length > 0,
adCategories,
blacklistedHypelabCampaignsSlugs
);
})
.catch(error => console.error(error));
2 changes: 1 addition & 1 deletion src/content-scripts/replace-ads/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { getRulesFromContentScript, clearRulesCache } from './ads-rules';
export { getRulesFromStorage as getRulesFromContentScript, clearRulesCache } from './ads-rules';
223 changes: 141 additions & 82 deletions src/lib/ads/configure-ads.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
import browser from 'webextension-polyfill';

import { buildSwapPageUrlQuery } from 'app/pages/Swap/utils/build-url-query';
import { ADS_META_SEARCH_PARAM_NAME, ContentScriptType, ORIGIN_SEARCH_PARAM_NAME } from 'lib/constants';
import {
ADS_META_SEARCH_PARAM_NAME,
AD_CATEGORIES_PARAM_NAME,
ContentScriptType,
ORIGIN_SEARCH_PARAM_NAME
} from 'lib/constants';
import { APP_VERSION, EnvVars, IS_MISES_BROWSER } from 'lib/env';
import { isTruthy } from 'lib/utils';

import { importExtensionAdsModule } from './import-extension-ads-module';

// Three interfaces below are copied from '@temple-wallet/extension-ads' to avoid importing it to ensure that a core
// build runs without errors.
interface AdSource {
shouldNotUseStrictContainerLimits?: boolean;
providerName: 'Temple' | 'Persona' | 'HypeLab' | 'SmartyAds';
native?: boolean;
slug: string;
categories?: string[];
}

interface AdDimensions {
width: number;
height: number;
minContainerWidth: number;
minContainerHeight: number;
maxContainerWidth: number;
maxContainerHeight: number;
}

interface AdMetadata {
source: AdSource;
dimensions: AdDimensions;
}

const smallTkeyInpageAdUrl = browser.runtime.getURL(`/misc/ad-banners/small-tkey-inpage-ad.png`);
const tkeyInpageAdUrl = browser.runtime.getURL(`/misc/ad-banners/tkey-inpage-ad.png`);

Expand All @@ -16,13 +45,14 @@ const swapTkeyUrl = `${browser.runtime.getURL('fullpage.html')}#/swap?${buildSwa
true
)}`;

const getAdsStackIframeURL = (id: string, adsMetadataIds: any[], origin: string) => {
const getAdsStackIframeURL = (id: string, adsMetadataIds: any[], origin: string, adCategories: string[]) => {
const url = new URL(browser.runtime.getURL('iframes/ads-stack.html'));
url.searchParams.set('id', id);
adsMetadataIds.forEach(adMetadataId =>
url.searchParams.append(ADS_META_SEARCH_PARAM_NAME, JSON.stringify(adMetadataId))
);
url.searchParams.set(ORIGIN_SEARCH_PARAM_NAME, origin);
adCategories.forEach(category => url.searchParams.append(AD_CATEGORIES_PARAM_NAME, category));

return url.toString();
};
Expand Down Expand Up @@ -61,7 +91,7 @@ const buildNativeAdsMeta = (containerWidth: number, containerHeight: number) =>
}
].filter(isTruthy);

const bannerAdsMetaBase = [
const bannerAdsMetaBase: (AdMetadata | false)[] = [
{
source: {
providerName: 'SmartyAds' as const,
Expand Down Expand Up @@ -110,6 +140,52 @@ const bannerAdsMetaBase = [
maxContainerHeight: 300
}
},
{
source: {
providerName: 'HypeLab' as const,
native: false,
slug: IS_MISES_BROWSER ? EnvVars.HYPELAB_MISES_WIDE_PLACEMENT_SLUG : EnvVars.HYPELAB_WIDE_PLACEMENT_SLUG
},
dimensions: {
width: 728,
height: 90,
minContainerWidth: 727,
minContainerHeight: 89,
maxContainerWidth: Infinity,
maxContainerHeight: 300
}
},
{
source: {
providerName: 'Persona' as const,
native: false,
slug: IS_MISES_BROWSER ? EnvVars.PERSONA_ADS_MISES_WIDE_BANNER_UNIT_ID : EnvVars.PERSONA_ADS_WIDE_BANNER_UNIT_ID
},
dimensions: {
width: 728,
height: 90,
minContainerWidth: 727,
minContainerHeight: 89,
maxContainerWidth: Infinity,
maxContainerHeight: 300
}
},
{
source: {
providerName: 'Persona' as const,
slug: IS_MISES_BROWSER
? EnvVars.PERSONA_ADS_MISES_MEDIUM_BANNER_UNIT_ID
: EnvVars.PERSONA_ADS_MEDIUM_BANNER_UNIT_ID
},
dimensions: {
width: 600,
height: 160,
minContainerWidth: 599,
minContainerHeight: 159,
maxContainerWidth: 800,
maxContainerHeight: 300
}
},
{
source: {
providerName: 'SmartyAds' as const,
Expand Down Expand Up @@ -174,6 +250,37 @@ const bannerAdsMetaBase = [
maxContainerHeight: Infinity
}
},
{
source: {
providerName: 'HypeLab' as const,
native: false,
slug: IS_MISES_BROWSER ? EnvVars.HYPELAB_MISES_HIGH_PLACEMENT_SLUG : EnvVars.HYPELAB_HIGH_PLACEMENT_SLUG
},
dimensions: {
width: 300,
height: 250,
minContainerWidth: 299,
minContainerHeight: 249,
maxContainerWidth: 700,
maxContainerHeight: Infinity
}
},
{
source: {
providerName: 'Persona' as const,
slug: IS_MISES_BROWSER
? EnvVars.PERSONA_ADS_MISES_SQUARISH_BANNER_UNIT_ID
: EnvVars.PERSONA_ADS_SQUARISH_BANNER_UNIT_ID
},
dimensions: {
width: 300,
height: 250,
minContainerWidth: 299,
minContainerHeight: 249,
maxContainerWidth: 700,
maxContainerHeight: Infinity
}
},
{
source: {
providerName: 'SmartyAds' as const,
Expand Down Expand Up @@ -207,82 +314,6 @@ const bannerAdsMetaBase = [
maxContainerHeight: 130
}
},
{
source: {
providerName: 'HypeLab' as const,
native: false,
slug: IS_MISES_BROWSER ? EnvVars.HYPELAB_MISES_WIDE_PLACEMENT_SLUG : EnvVars.HYPELAB_WIDE_PLACEMENT_SLUG
},
dimensions: {
width: 728,
height: 90,
minContainerWidth: 727,
minContainerHeight: 89,
maxContainerWidth: Infinity,
maxContainerHeight: 300
}
},
{
source: {
providerName: 'Temple' as const,
slug: ''
},
dimensions: {
width: 728,
height: 90,
minContainerWidth: 727,
minContainerHeight: 89,
maxContainerWidth: Infinity,
maxContainerHeight: 300
}
},
{
source: {
providerName: 'Persona' as const,
slug: IS_MISES_BROWSER
? EnvVars.PERSONA_ADS_MISES_MEDIUM_BANNER_UNIT_ID
: EnvVars.PERSONA_ADS_MEDIUM_BANNER_UNIT_ID
},
dimensions: {
width: 600,
height: 160,
minContainerWidth: 599,
minContainerHeight: 159,
maxContainerWidth: 800,
maxContainerHeight: 300
}
},
{
source: {
providerName: 'HypeLab' as const,
native: false,
slug: IS_MISES_BROWSER ? EnvVars.HYPELAB_MISES_HIGH_PLACEMENT_SLUG : EnvVars.HYPELAB_HIGH_PLACEMENT_SLUG
},
dimensions: {
width: 300,
height: 250,
minContainerWidth: 299,
minContainerHeight: 249,
maxContainerWidth: 700,
maxContainerHeight: Infinity
}
},
{
source: {
providerName: 'Persona' as const,
slug: IS_MISES_BROWSER
? EnvVars.PERSONA_ADS_MISES_SQUARISH_BANNER_UNIT_ID
: EnvVars.PERSONA_ADS_SQUARISH_BANNER_UNIT_ID
},
dimensions: {
width: 300,
height: 250,
minContainerWidth: 299,
minContainerHeight: 249,
maxContainerWidth: 700,
maxContainerHeight: 500
}
},
{
source: {
providerName: 'HypeLab' as const,
Expand All @@ -302,7 +333,8 @@ const bannerAdsMetaBase = [
{
source: {
providerName: 'Persona' as const,
slug: IS_MISES_BROWSER ? EnvVars.PERSONA_ADS_MISES_BANNER_UNIT_ID : EnvVars.PERSONA_ADS_BANNER_UNIT_ID
slug: IS_MISES_BROWSER ? EnvVars.PERSONA_ADS_MISES_BANNER_UNIT_ID : EnvVars.PERSONA_ADS_BANNER_UNIT_ID,
shouldNotUseStrictContainerLimits: true
},
dimensions: {
width: 321,
Expand Down Expand Up @@ -330,6 +362,32 @@ const bannerAdsMetaBase = [
}
];

const bannerAdsMeta = bannerAdsMetaBase.filter(isTruthy);

const pickNextAdMetadata = (
allAdsMetadata: AdMetadata[],
currentAdMetadata: AdMetadata | undefined,
validAdsCounter: number,
pageHasPlacesRules: boolean,
adCategories: string[]
) => {
const currentAdMetadataIndex = currentAdMetadata ? allAdsMetadata.indexOf(currentAdMetadata) : -1;
const isPureCryptoAd = adCategories.length === 1 && adCategories[0] === 'crypto';

if (
!currentAdMetadata ||
(!pageHasPlacesRules && !isPureCryptoAd && validAdsCounter > 0 && currentAdMetadataIndex > 0)
) {
return allAdsMetadata[0];
}

if (allAdsMetadata.length === 1) {
return undefined;
}

return allAdsMetadata[(currentAdMetadataIndex + 1) % allAdsMetadata.length];
};

export const configureAds = async () => {
const { configureAds: originalConfigureAds } = await importExtensionAdsModule();
originalConfigureAds({
Expand All @@ -341,9 +399,10 @@ export const configureAds = async () => {
personaIframePath: browser.runtime.getURL('iframes/persona-ad.html'),
getAdsStackIframeURL,
buildNativeAdsMeta,
bannerAdsMeta: bannerAdsMetaBase.filter(isTruthy),
bannerAdsMeta,
extVersion: APP_VERSION,
templePassphrase: EnvVars.TEMPLE_ADS_ORIGIN_PASSPHRASE,
isMisesBrowser: IS_MISES_BROWSER
isMisesBrowser: IS_MISES_BROWSER,
pickNextAdMetadata
});
};
2 changes: 2 additions & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export enum ContentScriptType {
ReferralClick = 'ReferralClick'
}

export const AD_CATEGORIES_PARAM_NAME = 'categories';

export const ORIGIN_SEARCH_PARAM_NAME = 'o';

export const ADS_META_SEARCH_PARAM_NAME = 'ads-meta';
Expand Down
Loading

0 comments on commit 0be3314

Please sign in to comment.