From ae47a6f30189b9d1b13612b18def6706fc9ea251 Mon Sep 17 00:00:00 2001 From: tdgao Date: Thu, 23 Jul 2026 21:40:29 -0700 Subject: [PATCH] harden CPM fallbacks for unknown layouts --- apps/app/src/api/ads-consent/bridge.js | 25 ++++++---- apps/app/src/api/ads-consent/controller.js | 53 +++++++++++++++++++++- apps/app/src/api/ads-consent/index.js | 4 ++ apps/app/src/api/ads.rs | 1 - 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/apps/app/src/api/ads-consent/bridge.js b/apps/app/src/api/ads-consent/bridge.js index 3c5b576e4b..c6f56ea487 100644 --- a/apps/app/src/api/ads-consent/bridge.js +++ b/apps/app/src/api/ads-consent/bridge.js @@ -27,19 +27,26 @@ function openExternalUrl(url) { /** * @param {AdsConsentPopupMode} mode - * @returns {void} + * @returns {Promise} */ -function invokeAdsConsentPopupMode(mode) { +async function invokeAdsConsentPopupMode(mode) { const invoke = getTauriInvoke() if (!invoke) return - const show = mode !== 'hidden' - const command = show ? 'show_ads_consent_overlay' : 'hide_ads_consent_overlay' - const args = - mode === 'hidden' - ? { dpr: window.devicePixelRatio } - : { notificationEnabled: mode === 'custom' } - void invoke(`plugin:ads|${command}`, args).catch(() => {}) + try { + if (mode === 'hidden') { + await invoke('plugin:ads|hide_ads_consent_overlay', { dpr: window.devicePixelRatio }) + return + } + + await invoke('plugin:ads|show_ads_consent_overlay', { + notificationEnabled: mode === 'custom', + }) + + if (mode === 'fallback') { + await invoke('plugin:ads|show_ads_consent_preferences') + } + } catch {} } /** @returns {Promise} */ diff --git a/apps/app/src/api/ads-consent/controller.js b/apps/app/src/api/ads-consent/controller.js index c44d6b7e5b..5eae00290c 100644 --- a/apps/app/src/api/ads-consent/controller.js +++ b/apps/app/src/api/ads-consent/controller.js @@ -1,5 +1,6 @@ const ACTION_TIMEOUT = 10_000 const LAYOUT_DELAY = 100 +const POPUP_READINESS_TIMEOUT = 10_000 const SUBMISSION_TIMEOUT = 10_000 class AdsConsentController { @@ -12,6 +13,9 @@ class AdsConsentController { /** @type {ReturnType | null} */ this.submissionTimeout = null + + /** @type {ReturnType | null} */ + this.popupReadinessTimeout = null } /** @returns {void} */ @@ -22,6 +26,7 @@ class AdsConsentController { const cmpMain = document.getElementById('qc-cmp2-main') if (!cmpMain) { + this.clearPopupReadinessTimeout() if (this.state.phase === 'idle') { document.documentElement.classList.remove('modrinth-ads-consent-overlay') } else if (this.state.phase !== 'submitting-consent' && this.state.phase !== 'finishing') { @@ -34,12 +39,42 @@ class AdsConsentController { document.documentElement.classList.add('modrinth-ads-consent-overlay') const variant = this.detectVariant() - if (!areConsentControlsPresent(variant)) return + if (!areConsentControlsPresent(variant)) { + this.waitForConsentControls() + return + } + this.clearPopupReadinessTimeout() this.state.setState('showing-popup') this.setPopupMode('custom') } + /** @returns {void} */ + waitForConsentControls() { + if (this.popupReadinessTimeout) return + + this.popupReadinessTimeout = setTimeout(() => { + this.popupReadinessTimeout = null + + if (this.state.phase !== 'idle' || !document.getElementById('qc-cmp2-main')) return + + const variant = this.detectVariant() + if (areConsentControlsPresent(variant)) { + this.syncConsentPopup() + return + } + + this.state.setState('showing-popup') + this.showNativeCmpFallback() + }, POPUP_READINESS_TIMEOUT) + } + + /** @returns {void} */ + clearPopupReadinessTimeout() { + clearTimeout(this.popupReadinessTimeout ?? undefined) + this.popupReadinessTimeout = null + } + /** * Sends the clicks from custom privacy popup to native popup buttons * @param {ConsentAction} action @@ -149,6 +184,19 @@ class AdsConsentController { } } + /** @returns {void} */ + handleTcfClose() { + if (this.state.variant !== 'tcf') return + + if (this.state.phase === 'showing-preferences') { + this.state.setState('showing-popup') + this.concealPreferences() + this.setPopupMode('custom') + } else if (this.state.phase === 'showing-reopened-preferences') { + this.finishReopenedPopup() + } + } + /** @returns {Promise} */ async reopenPreferences() { if (document.documentElement.classList.contains('modrinth-ads-consent-overlay')) { @@ -278,7 +326,7 @@ class AdsConsentController { document.documentElement.classList.toggle('modrinth-ads-consent-overlay', shown) document.documentElement.classList.toggle('modrinth-ads-consent-fallback', mode === 'fallback') if (hidden) this.concealPreferences() - invokeAdsConsentPopupMode(mode) + void invokeAdsConsentPopupMode(mode) } /** @returns {void} */ @@ -300,6 +348,7 @@ class AdsConsentController { clearTimeout(this.submissionTimeout ?? undefined) this.submissionTimeout = null + this.clearPopupReadinessTimeout() this.preSubmissionPhase = null this.state.setState('finishing') this.setPopupMode('hidden') diff --git a/apps/app/src/api/ads-consent/index.js b/apps/app/src/api/ads-consent/index.js index a96d5b2cd7..4a747053f0 100644 --- a/apps/app/src/api/ads-consent/index.js +++ b/apps/app/src/api/ads-consent/index.js @@ -60,6 +60,10 @@ document.addEventListener( notifyAdClick() const target = event.target instanceof Element ? event.target : null + if (target?.closest('.qc-cmp2-close-icon')) { + setTimeout(() => controller.handleTcfClose()) + } + if (target?.closest('#qc-cmp2-usp .qc-usp-ui-form-content button[mode="primary"]')) { controller.beginConsentSubmission() } diff --git a/apps/app/src/api/ads.rs b/apps/app/src/api/ads.rs index 53aa4c86aa..2521524025 100644 --- a/apps/app/src/api/ads.rs +++ b/apps/app/src/api/ads.rs @@ -503,7 +503,6 @@ pub async fn init_ads_window( // Aditude has separately informed SSPs and IVT vendors that this traffic // originates from a desktop app. .user_agent(ADS_USER_AGENT) - .incognito(true) .zoom_hotkeys_enabled(false) .transparent(true) .on_new_window(|_, _| {