mirror of
https://github.com/modrinth/code.git
synced 2026-07-31 13:16:38 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a65223b13a |
@@ -276,6 +276,8 @@ fn main() {
|
||||
"hide_ads_window",
|
||||
"scroll_ads_window",
|
||||
"show_ads_window",
|
||||
"show_ads_consent_overlay",
|
||||
"hide_ads_consent_overlay",
|
||||
"record_ads_click",
|
||||
"open_link",
|
||||
"get_ads_personalization",
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
const MODRINTH_ORIGIN = 'https://modrinth.com'
|
||||
|
||||
document.addEventListener(
|
||||
'click',
|
||||
function (e) {
|
||||
window.top.postMessage({ modrinthAdClick: true }, 'https://modrinth.com')
|
||||
window.top.postMessage({ modrinthAdClick: true }, MODRINTH_ORIGIN)
|
||||
|
||||
let target = e.target
|
||||
while (target != null) {
|
||||
if (target.matches('a')) {
|
||||
e.preventDefault()
|
||||
if (target.href) {
|
||||
window.top.postMessage({ modrinthOpenUrl: target.href }, 'https://modrinth.com')
|
||||
window.top.postMessage({ modrinthOpenUrl: target.href }, MODRINTH_ORIGIN)
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -19,7 +21,93 @@ document.addEventListener(
|
||||
)
|
||||
|
||||
window.open = (url, target, features) => {
|
||||
window.top.postMessage({ modrinthOpenUrl: url }, 'https://modrinth.com')
|
||||
window.top.postMessage({ modrinthOpenUrl: url }, MODRINTH_ORIGIN)
|
||||
}
|
||||
|
||||
let modrinthAdsConsentOverlayShown = false
|
||||
let modrinthTcfListenerInstalled = false
|
||||
let modrinthTcfListenerAttempts = 0
|
||||
|
||||
function installAdsConsentOverlayStyle() {
|
||||
if (document.getElementById('modrinth-ads-consent-overlay-style')) {
|
||||
return
|
||||
}
|
||||
const style = document.createElement('style')
|
||||
style.id = 'modrinth-ads-consent-overlay-style'
|
||||
style.textContent = `
|
||||
html.modrinth-ads-consent-overlay #modrinth-rail-1 {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
`
|
||||
document.documentElement.appendChild(style)
|
||||
}
|
||||
|
||||
function getTauriInvoke() {
|
||||
return window.__TAURI__?.core?.invoke ?? window.__TAURI_INTERNALS__?.invoke
|
||||
}
|
||||
|
||||
function invokeAdsConsentOverlayCommand(shown) {
|
||||
const invoke = getTauriInvoke()
|
||||
|
||||
if (typeof invoke !== 'function') {
|
||||
return
|
||||
}
|
||||
|
||||
const command = shown ? 'show_ads_consent_overlay' : 'hide_ads_consent_overlay'
|
||||
const args = shown ? {} : { dpr: window.devicePixelRatio }
|
||||
|
||||
invoke(`plugin:ads|${command}`, args).catch(() => {})
|
||||
}
|
||||
|
||||
function setAdsConsentOverlay(shown) {
|
||||
if (modrinthAdsConsentOverlayShown === shown) return
|
||||
|
||||
modrinthAdsConsentOverlayShown = shown
|
||||
installAdsConsentOverlayStyle()
|
||||
document.documentElement.classList.toggle('modrinth-ads-consent-overlay', shown)
|
||||
|
||||
if (window.top === window) {
|
||||
invokeAdsConsentOverlayCommand(shown)
|
||||
} else {
|
||||
window.top.postMessage({ modrinthAdsConsentOverlay: shown }, MODRINTH_ORIGIN)
|
||||
}
|
||||
}
|
||||
|
||||
if (window.top === window) {
|
||||
window.addEventListener('message', (event) => {
|
||||
if (
|
||||
event.origin === MODRINTH_ORIGIN &&
|
||||
typeof event.data?.modrinthAdsConsentOverlay === 'boolean'
|
||||
) {
|
||||
setAdsConsentOverlay(event.data.modrinthAdsConsentOverlay)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handleTcfConsentEvent(tcData, success) {
|
||||
if (!success || !tcData) return
|
||||
|
||||
if (tcData.eventStatus === 'cmpuishown') {
|
||||
setAdsConsentOverlay(true)
|
||||
} else if (tcData.eventStatus === 'useractioncomplete' || tcData.eventStatus === 'tcloaded') {
|
||||
setAdsConsentOverlay(false)
|
||||
}
|
||||
}
|
||||
|
||||
// polling to install listener on tcf api
|
||||
function installTcfConsentListener() {
|
||||
if (modrinthTcfListenerInstalled) return
|
||||
|
||||
if (typeof window.__tcfapi === 'function') {
|
||||
modrinthTcfListenerInstalled = true
|
||||
window.__tcfapi('addEventListener', 2, handleTcfConsentEvent)
|
||||
return
|
||||
}
|
||||
|
||||
if (modrinthTcfListenerAttempts < 60) {
|
||||
modrinthTcfListenerAttempts += 1
|
||||
setTimeout(installTcfConsentListener, 500)
|
||||
}
|
||||
}
|
||||
|
||||
function muteAudioContext() {
|
||||
@@ -100,7 +188,10 @@ function muteVideos() {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
muteVideos()
|
||||
muteAudioContext()
|
||||
installTcfConsentListener()
|
||||
|
||||
const observer = new MutationObserver(muteVideos)
|
||||
observer.observe(document.body, { childList: true, subtree: true })
|
||||
})
|
||||
|
||||
installTcfConsentListener()
|
||||
|
||||
+157
-18
@@ -11,12 +11,14 @@ use tokio::sync::RwLock;
|
||||
pub struct AdsState {
|
||||
pub shown: bool,
|
||||
pub modal_shown: bool,
|
||||
pub consent_overlay_shown: bool,
|
||||
pub occluded: bool,
|
||||
pub last_click: Option<Instant>,
|
||||
pub malicious_origins: HashSet<String>,
|
||||
}
|
||||
|
||||
const AD_LINK: &str = "https://modrinth.com/wrapper/app-ads-cookie";
|
||||
const APP_TITLE_BAR_HEIGHT: f32 = 48.0;
|
||||
#[cfg(any(windows, target_os = "macos"))]
|
||||
pub(super) const OCCLUDED_AREA_THRESHOLD: f64 = 0.5;
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
@@ -131,13 +133,16 @@ fn set_webview_visible_for_window<R: Runtime>(
|
||||
.and_then(|window| window.is_minimized().ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
let is_occluded = app
|
||||
let (is_occluded, consent_overlay_shown) = app
|
||||
.state::<RwLock<AdsState>>()
|
||||
.try_read()
|
||||
.map(|state| state.occluded)
|
||||
.unwrap_or(false);
|
||||
.map(|state| (state.occluded, state.consent_overlay_shown))
|
||||
.unwrap_or((false, false));
|
||||
|
||||
set_webview_visible(webview, visible && !is_minimized && !is_occluded);
|
||||
set_webview_visible(
|
||||
webview,
|
||||
visible && !is_minimized && (!is_occluded || consent_overlay_shown),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(windows, target_os = "macos"))]
|
||||
@@ -195,7 +200,8 @@ async fn sync_ads_occlusion<R: Runtime>(app: &tauri::AppHandle<R>) {
|
||||
}
|
||||
|
||||
state.occluded = occluded;
|
||||
let visible = state.shown && !state.modal_shown;
|
||||
let visible =
|
||||
state.shown && (!state.modal_shown || state.consent_overlay_shown);
|
||||
drop(state);
|
||||
|
||||
if let Some(webview) = app.webviews().get("ads-window") {
|
||||
@@ -211,21 +217,36 @@ fn sync_webview_visibility_for_main_window<R: Runtime>(
|
||||
let is_minimized = main_window.is_minimized().unwrap_or(false);
|
||||
let was = was_minimized.load(Ordering::SeqCst);
|
||||
|
||||
let ads_state = if is_minimized {
|
||||
None
|
||||
} else {
|
||||
match app.state::<RwLock<AdsState>>().try_read() {
|
||||
Ok(state) => Some((
|
||||
state.shown
|
||||
&& (!state.modal_shown || state.consent_overlay_shown)
|
||||
&& (!state.occluded || state.consent_overlay_shown),
|
||||
state.consent_overlay_shown,
|
||||
)),
|
||||
Err(_) => None,
|
||||
}
|
||||
};
|
||||
let ads_visible = ads_state.map(|state| state.0).unwrap_or(false);
|
||||
|
||||
if ads_state.map(|state| state.1).unwrap_or(false)
|
||||
&& let Some(webview) = app.webviews().get("ads-window")
|
||||
&& let Ok((position, size)) =
|
||||
get_overlay_webview_position_for_window(main_window)
|
||||
{
|
||||
webview.set_position(position).ok();
|
||||
webview.set_size(size).ok();
|
||||
}
|
||||
|
||||
if is_minimized == was {
|
||||
return;
|
||||
}
|
||||
|
||||
was_minimized.store(is_minimized, Ordering::SeqCst);
|
||||
|
||||
let ads_visible = if is_minimized {
|
||||
false
|
||||
} else {
|
||||
match app.state::<RwLock<AdsState>>().try_read() {
|
||||
Ok(state) => state.shown && !state.modal_shown && !state.occluded,
|
||||
Err(_) => false,
|
||||
}
|
||||
};
|
||||
|
||||
let mut webviews = Vec::new();
|
||||
let mut seen_webviews = HashSet::new();
|
||||
|
||||
@@ -254,6 +275,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
app.manage(RwLock::new(AdsState {
|
||||
shown: true,
|
||||
modal_shown: false,
|
||||
consent_overlay_shown: false,
|
||||
occluded: false,
|
||||
last_click: None,
|
||||
malicious_origins: HashSet::new(),
|
||||
@@ -269,7 +291,10 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
.state::<RwLock<AdsState>>()
|
||||
.try_read()
|
||||
.map(|state| {
|
||||
state.shown && !state.modal_shown && !state.occluded
|
||||
state.shown
|
||||
&& !state.modal_shown
|
||||
&& !state.consent_overlay_shown
|
||||
&& !state.occluded
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
@@ -332,6 +357,8 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
init_ads_window,
|
||||
hide_ads_window,
|
||||
show_ads_window,
|
||||
show_ads_consent_overlay,
|
||||
hide_ads_consent_overlay,
|
||||
record_ads_click,
|
||||
open_link,
|
||||
get_ads_personalization,
|
||||
@@ -358,6 +385,42 @@ fn get_webview_position<R: Runtime>(
|
||||
))
|
||||
}
|
||||
|
||||
fn get_overlay_webview_position_for_window<R: Runtime>(
|
||||
main_window: &tauri::Window<R>,
|
||||
) -> crate::api::Result<(PhysicalPosition<f32>, PhysicalSize<f32>)> {
|
||||
let main_window_size = main_window.outer_size()?;
|
||||
let title_bar_height =
|
||||
APP_TITLE_BAR_HEIGHT * main_window.scale_factor()? as f32;
|
||||
|
||||
Ok((
|
||||
PhysicalPosition::new(0.0, title_bar_height),
|
||||
PhysicalSize::new(
|
||||
main_window_size.width as f32,
|
||||
(main_window_size.height as f32 - title_bar_height).max(0.0),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
fn get_overlay_webview_position<R: Runtime>(
|
||||
app: &tauri::AppHandle<R>,
|
||||
) -> crate::api::Result<(PhysicalPosition<f32>, PhysicalSize<f32>)> {
|
||||
let main_window = app.get_window("main").unwrap();
|
||||
|
||||
get_overlay_webview_position_for_window(&main_window)
|
||||
}
|
||||
|
||||
fn get_device_pixel_ratio<R: Runtime>(
|
||||
app: &tauri::AppHandle<R>,
|
||||
dpr: Option<f32>,
|
||||
) -> f32 {
|
||||
dpr.unwrap_or_else(|| {
|
||||
app.get_window("main")
|
||||
.and_then(|window| window.scale_factor().ok())
|
||||
.map(|scale_factor| scale_factor as f32)
|
||||
.unwrap_or(1.0)
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub async fn init_ads_window<R: Runtime>(
|
||||
@@ -374,11 +437,17 @@ pub async fn init_ads_window<R: Runtime>(
|
||||
state.shown = true;
|
||||
}
|
||||
|
||||
if state.modal_shown {
|
||||
if state.modal_shown && !state.consent_overlay_shown {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Ok((position, size)) = get_webview_position(&app, dpr) {
|
||||
let layout = if state.consent_overlay_shown {
|
||||
get_overlay_webview_position(&app)
|
||||
} else {
|
||||
get_webview_position(&app, dpr)
|
||||
};
|
||||
|
||||
if let Ok((position, size)) = layout {
|
||||
let webview = if let Some(webview) = app.webviews().get("ads-window") {
|
||||
// set both the `hide`/`show` state and `position`,
|
||||
// to ensure that the webview is actually shown/hidden
|
||||
@@ -586,7 +655,11 @@ pub async fn show_ads_window<R: Runtime>(
|
||||
state.modal_shown = false;
|
||||
|
||||
if state.shown {
|
||||
let (position, size) = get_webview_position(&app, dpr)?;
|
||||
let (position, size) = if state.consent_overlay_shown {
|
||||
get_overlay_webview_position(&app)?
|
||||
} else {
|
||||
get_webview_position(&app, dpr)?
|
||||
};
|
||||
// set both the `hide`/`show` state and `position`,
|
||||
// to ensure that the webview is actually shown/hidden
|
||||
webview.set_size(size).ok();
|
||||
@@ -610,8 +683,19 @@ pub async fn hide_ads_window<R: Runtime>(
|
||||
|
||||
if reset.unwrap_or(false) {
|
||||
state.shown = false;
|
||||
state.consent_overlay_shown = false;
|
||||
} else {
|
||||
state.modal_shown = true;
|
||||
|
||||
if state.consent_overlay_shown {
|
||||
let (position, size) = get_overlay_webview_position(&app)?;
|
||||
webview.set_size(size).ok();
|
||||
webview.set_position(position).ok();
|
||||
webview.show().ok();
|
||||
set_webview_visible_for_window(&app, webview, true);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// set both the `hide`/`show` state and `position`,
|
||||
@@ -625,6 +709,61 @@ pub async fn hide_ads_window<R: Runtime>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn show_ads_consent_overlay<R: Runtime>(
|
||||
app: tauri::AppHandle<R>,
|
||||
) -> crate::api::Result<()> {
|
||||
if let Some(webview) = app.webviews().get("ads-window") {
|
||||
let state = app.state::<RwLock<AdsState>>();
|
||||
let mut state = state.write().await;
|
||||
|
||||
// dont show for hidden ads so consent events cannot re-enable the webview.
|
||||
if !state.shown {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
state.consent_overlay_shown = true;
|
||||
|
||||
let (position, size) = get_overlay_webview_position(&app)?;
|
||||
webview.set_size(size).ok();
|
||||
webview.set_position(position).ok();
|
||||
webview.show().ok();
|
||||
set_webview_visible_for_window(&app, webview, true);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn hide_ads_consent_overlay<R: Runtime>(
|
||||
app: tauri::AppHandle<R>,
|
||||
dpr: Option<f32>,
|
||||
) -> crate::api::Result<()> {
|
||||
if let Some(webview) = app.webviews().get("ads-window") {
|
||||
let state = app.state::<RwLock<AdsState>>();
|
||||
let mut state = state.write().await;
|
||||
|
||||
state.consent_overlay_shown = false;
|
||||
|
||||
if state.shown && !state.modal_shown {
|
||||
let dpr = get_device_pixel_ratio(&app, dpr);
|
||||
let (position, size) = get_webview_position(&app, dpr)?;
|
||||
|
||||
webview.set_size(size).ok();
|
||||
webview.set_position(position).ok();
|
||||
webview.show().ok();
|
||||
set_webview_visible_for_window(&app, webview, true);
|
||||
} else {
|
||||
webview
|
||||
.set_position(PhysicalPosition::new(-1000, -1000))
|
||||
.ok();
|
||||
webview.hide().ok();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn record_ads_click<R: Runtime>(
|
||||
app: tauri::AppHandle<R>,
|
||||
|
||||
@@ -482,3 +482,8 @@ input {
|
||||
.button-transparent {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.qc-cmp2-close-tooltip {
|
||||
background-color: transparent;
|
||||
color: hsl(145, 78%, 28%);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user