mirror of
https://github.com/modrinth/code.git
synced 2026-08-25 09:04:55 +00:00
fix: hide ad webview when showing fullscreen content (#7110)
* fix: hide ad webview when showing fullscreen content * fixes
This commit is contained in:
+97
-141
@@ -3,14 +3,14 @@ use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Duration, Instant};
|
||||
use tauri::plugin::TauriPlugin;
|
||||
use tauri::{Emitter, Manager, PhysicalPosition, PhysicalSize, Rect, Runtime};
|
||||
use tauri::{Emitter, Manager, PhysicalPosition, PhysicalSize, Runtime};
|
||||
use tauri_plugin_opener::OpenerExt;
|
||||
use theseus::settings;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub struct AdsState {
|
||||
pub shown: bool,
|
||||
pub modal_shown: bool,
|
||||
pub visibility_holds: usize,
|
||||
pub consent_required: bool,
|
||||
pub consent_notification_enabled: bool,
|
||||
pub consent_overlay_shown: bool,
|
||||
@@ -24,6 +24,11 @@ const ADS_CONSENT_REQUIRED_EVENT: &str = "ads-consent-required";
|
||||
const APP_TITLE_BAR_HEIGHT: f32 = 48.0;
|
||||
#[cfg(any(windows, target_os = "macos"))]
|
||||
pub(super) const OCCLUDED_AREA_THRESHOLD: f64 = 0.5;
|
||||
|
||||
fn should_show_ads_webview(state: &AdsState) -> bool {
|
||||
state.shown && (state.visibility_holds == 0 || state.consent_overlay_shown)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
const ADS_USER_AGENT: &str = concat!(
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ",
|
||||
@@ -126,28 +131,6 @@ fn set_webview_visible<R: Runtime>(webview: &tauri::Webview<R>, visible: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_webview_visible_for_window<R: Runtime>(
|
||||
app: &tauri::AppHandle<R>,
|
||||
webview: &tauri::Webview<R>,
|
||||
visible: bool,
|
||||
) {
|
||||
let is_minimized = app
|
||||
.get_window("main")
|
||||
.and_then(|window| window.is_minimized().ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
let (is_occluded, consent_overlay_shown) = app
|
||||
.state::<RwLock<AdsState>>()
|
||||
.try_read()
|
||||
.map(|state| (state.occluded, state.consent_overlay_shown))
|
||||
.unwrap_or((false, false));
|
||||
|
||||
set_webview_visible(
|
||||
webview,
|
||||
visible && !is_minimized && (!is_occluded || consent_overlay_shown),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(windows, target_os = "macos"))]
|
||||
fn compute_ads_webview_occlusion<R: Runtime>(
|
||||
app: &tauri::AppHandle<R>,
|
||||
@@ -203,8 +186,7 @@ async fn sync_ads_occlusion<R: Runtime>(app: &tauri::AppHandle<R>) {
|
||||
}
|
||||
|
||||
state.occluded = occluded;
|
||||
let visible =
|
||||
state.shown && (!state.modal_shown || state.consent_overlay_shown);
|
||||
let visible = should_show_ads_webview(&state);
|
||||
drop(state);
|
||||
|
||||
if let Some(webview) = app.webviews().get("ads-window") {
|
||||
@@ -225,8 +207,7 @@ fn sync_webview_visibility_for_main_window<R: Runtime>(
|
||||
} else {
|
||||
match app.state::<RwLock<AdsState>>().try_read() {
|
||||
Ok(state) => Some((
|
||||
state.shown
|
||||
&& (!state.modal_shown || state.consent_overlay_shown)
|
||||
should_show_ads_webview(&state)
|
||||
&& (!state.occluded || state.consent_overlay_shown),
|
||||
state.consent_overlay_shown,
|
||||
)),
|
||||
@@ -277,7 +258,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
.setup(|app, _api| {
|
||||
app.manage(RwLock::new(AdsState {
|
||||
shown: true,
|
||||
modal_shown: false,
|
||||
visibility_holds: 0,
|
||||
consent_required: false,
|
||||
consent_notification_enabled: false,
|
||||
consent_overlay_shown: false,
|
||||
@@ -297,7 +278,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
.try_read()
|
||||
.map(|state| {
|
||||
state.shown
|
||||
&& !state.modal_shown
|
||||
&& state.visibility_holds == 0
|
||||
&& !state.consent_required
|
||||
&& !state.consent_overlay_shown
|
||||
&& !state.occluded
|
||||
@@ -362,7 +343,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
init_ads_window,
|
||||
hide_ads_window,
|
||||
show_ads_window,
|
||||
update_ads_window_hold,
|
||||
show_ads_consent_ui,
|
||||
expand_ads_consent_webview,
|
||||
open_ads_consent_preferences,
|
||||
@@ -431,6 +412,46 @@ fn get_device_pixel_ratio<R: Runtime>(
|
||||
})
|
||||
}
|
||||
|
||||
fn sync_ads_webview_visibility<R: Runtime>(
|
||||
app: &tauri::AppHandle<R>,
|
||||
state: &AdsState,
|
||||
dpr: f32,
|
||||
) -> crate::api::Result<()> {
|
||||
let webviews = app.webviews();
|
||||
let Some(webview) = webviews.get("ads-window") else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if should_show_ads_webview(state) {
|
||||
let (position, size) = if state.consent_overlay_shown {
|
||||
get_overlay_webview_position(app)?
|
||||
} else {
|
||||
get_webview_position(app, dpr)?
|
||||
};
|
||||
|
||||
let is_minimized = app
|
||||
.get_window("main")
|
||||
.and_then(|window| window.is_minimized().ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
webview.set_size(size).ok();
|
||||
webview.set_position(position).ok();
|
||||
webview.show().ok();
|
||||
set_webview_visible(
|
||||
webview,
|
||||
!is_minimized && (!state.occluded || state.consent_overlay_shown),
|
||||
);
|
||||
} else {
|
||||
webview
|
||||
.set_position(PhysicalPosition::new(-1000, -1000))
|
||||
.ok();
|
||||
webview.hide().ok();
|
||||
set_webview_visible(webview, false);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub async fn init_ads_window<R: Runtime>(
|
||||
@@ -447,10 +468,6 @@ pub async fn init_ads_window<R: Runtime>(
|
||||
state.shown = true;
|
||||
}
|
||||
|
||||
if state.modal_shown && !state.consent_overlay_shown {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let layout = if state.consent_overlay_shown {
|
||||
get_overlay_webview_position(&app)
|
||||
} else {
|
||||
@@ -459,21 +476,7 @@ pub async fn init_ads_window<R: Runtime>(
|
||||
|
||||
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
|
||||
if state.shown {
|
||||
webview.show().ok();
|
||||
webview.set_position(position).ok();
|
||||
webview.set_size(size).ok();
|
||||
set_webview_visible_for_window(&app, webview, true);
|
||||
} else {
|
||||
webview.hide().ok();
|
||||
webview
|
||||
.set_position(PhysicalPosition::new(-1000, -1000))
|
||||
.ok();
|
||||
set_webview_visible(webview, false);
|
||||
}
|
||||
|
||||
sync_ads_webview_visibility(&app, &state, dpr)?;
|
||||
Some(webview.clone())
|
||||
} else if let Some(window) = app.get_window("main") {
|
||||
let ads_consent_script = [
|
||||
@@ -510,7 +513,7 @@ pub async fn init_ads_window<R: Runtime>(
|
||||
}),
|
||||
// set both the `hide`/`show` state and `position`,
|
||||
// to ensure that the webview is actually shown/hidden
|
||||
if state.shown {
|
||||
if should_show_ads_webview(&state) {
|
||||
position
|
||||
} else {
|
||||
PhysicalPosition::new(-1000.0, -1000.0)
|
||||
@@ -518,13 +521,7 @@ pub async fn init_ads_window<R: Runtime>(
|
||||
size,
|
||||
)?;
|
||||
|
||||
if state.shown {
|
||||
webview.show().ok();
|
||||
set_webview_visible_for_window(&app, &webview, true);
|
||||
} else {
|
||||
webview.hide().ok();
|
||||
set_webview_visible(&webview, false);
|
||||
}
|
||||
sync_ads_webview_visibility(&app, &state, dpr)?;
|
||||
|
||||
webview.with_webview(#[allow(unused_variables)] |webview2| {
|
||||
#[cfg(windows)]
|
||||
@@ -672,38 +669,32 @@ pub async fn init_ads_window<R: Runtime>(
|
||||
pub async fn init_ads_window() {}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn show_ads_window<R: Runtime>(
|
||||
pub async fn update_ads_window_hold<R: Runtime>(
|
||||
app: tauri::AppHandle<R>,
|
||||
acquire: bool,
|
||||
dpr: f32,
|
||||
) -> crate::api::Result<()> {
|
||||
let mut show_consent_notification = false;
|
||||
let state = app.state::<RwLock<AdsState>>();
|
||||
let mut state = state.write().await;
|
||||
|
||||
if let Some(webview) = app.webviews().get("ads-window") {
|
||||
let state = app.state::<RwLock<AdsState>>();
|
||||
let mut state = state.write().await;
|
||||
|
||||
state.modal_shown = false;
|
||||
|
||||
if state.shown {
|
||||
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();
|
||||
webview.set_position(position).ok();
|
||||
webview.show().ok();
|
||||
set_webview_visible_for_window(&app, webview, true);
|
||||
}
|
||||
|
||||
show_consent_notification = state.shown
|
||||
&& state.consent_required
|
||||
&& state.consent_notification_enabled;
|
||||
if acquire {
|
||||
state.visibility_holds = state.visibility_holds.saturating_add(1);
|
||||
} else if state.visibility_holds > 0 {
|
||||
state.visibility_holds -= 1;
|
||||
} else {
|
||||
tracing::warn!(
|
||||
"Attempted to release an ads window hold when none were active"
|
||||
);
|
||||
}
|
||||
|
||||
if show_consent_notification {
|
||||
sync_ads_webview_visibility(&app, &state, dpr)?;
|
||||
|
||||
if !acquire
|
||||
&& state.visibility_holds == 0
|
||||
&& state.shown
|
||||
&& state.consent_required
|
||||
&& state.consent_notification_enabled
|
||||
{
|
||||
app.emit_to("main", ADS_CONSENT_REQUIRED_EVENT, true).ok();
|
||||
}
|
||||
|
||||
@@ -722,29 +713,11 @@ pub async fn hide_ads_window<R: Runtime>(
|
||||
if reset {
|
||||
state.shown = false;
|
||||
state.consent_overlay_shown = false;
|
||||
}
|
||||
|
||||
if let Some(webview) = app.webviews().get("ads-window") {
|
||||
if !reset {
|
||||
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`,
|
||||
// to ensure that the webview is actually shown/hidden
|
||||
webview
|
||||
.set_position(PhysicalPosition::new(-1000, -1000))
|
||||
.ok();
|
||||
webview.hide().ok();
|
||||
sync_ads_webview_visibility(
|
||||
&app,
|
||||
&state,
|
||||
get_device_pixel_ratio(&app, None),
|
||||
)?;
|
||||
}
|
||||
|
||||
if reset {
|
||||
@@ -761,7 +734,7 @@ pub async fn show_ads_consent_ui<R: Runtime>(
|
||||
) -> crate::api::Result<()> {
|
||||
let mut show_notification = false;
|
||||
|
||||
if let Some(webview) = app.webviews().get("ads-window") {
|
||||
if app.webviews().contains_key("ads-window") {
|
||||
let state = app.state::<RwLock<AdsState>>();
|
||||
let mut state = state.write().await;
|
||||
|
||||
@@ -772,14 +745,11 @@ pub async fn show_ads_consent_ui<R: Runtime>(
|
||||
state.consent_overlay_shown = false;
|
||||
show_notification = state.shown && notification_enabled;
|
||||
|
||||
if state.shown && !state.modal_shown {
|
||||
let dpr = get_device_pixel_ratio(&app, None);
|
||||
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);
|
||||
}
|
||||
sync_ads_webview_visibility(
|
||||
&app,
|
||||
&state,
|
||||
get_device_pixel_ratio(&app, None),
|
||||
)?;
|
||||
}
|
||||
|
||||
app.emit_to("main", ADS_CONSENT_REQUIRED_EVENT, show_notification)
|
||||
@@ -792,7 +762,7 @@ pub async fn show_ads_consent_ui<R: Runtime>(
|
||||
pub async fn expand_ads_consent_webview<R: Runtime>(
|
||||
app: tauri::AppHandle<R>,
|
||||
) -> crate::api::Result<()> {
|
||||
if let Some(webview) = app.webviews().get("ads-window") {
|
||||
if app.webviews().contains_key("ads-window") {
|
||||
let state = app.state::<RwLock<AdsState>>();
|
||||
let mut state = state.write().await;
|
||||
|
||||
@@ -801,16 +771,11 @@ pub async fn expand_ads_consent_webview<R: Runtime>(
|
||||
}
|
||||
|
||||
state.consent_overlay_shown = true;
|
||||
|
||||
let (position, size) = get_overlay_webview_position(&app)?;
|
||||
webview
|
||||
.set_bounds(Rect {
|
||||
position: position.into(),
|
||||
size: size.into(),
|
||||
})
|
||||
.ok();
|
||||
webview.show().ok();
|
||||
set_webview_visible_for_window(&app, webview, true);
|
||||
sync_ads_webview_visibility(
|
||||
&app,
|
||||
&state,
|
||||
get_device_pixel_ratio(&app, None),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -854,20 +819,11 @@ pub async fn finish_ads_consent_flow<R: Runtime>(
|
||||
state.consent_notification_enabled = false;
|
||||
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();
|
||||
}
|
||||
sync_ads_webview_visibility(
|
||||
&app,
|
||||
&state,
|
||||
get_device_pixel_ratio(&app, dpr),
|
||||
)?;
|
||||
|
||||
drop(state);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user