Empty-cache menu item; catch two more ways an app can notify
Right-click now offers "Empty cache and reload". Caches only - never cookies or local storage: emptying a cache is what you do when a page is serving something stale, and signing you out while doing it would be a different and much less welcome feature. The reload waits for the removal to finish, or it would refill from what was being thrown away. Google Chat could play its own alert sound and still reach none of this, because some apps never construct a Notification - they hand it to their service worker registration instead. ServiceWorkerRegistration.prototype .showNotification now routes to the same place. A worker calling it from its own context is still out of reach; a page calling it is not. Also shims the badge API, which is the precise form of an unread count - a number an app states outright rather than one scraped from its title. WKWebView does not implement it, so an app calling it was talking to nobody. Chat's title carries no count at all, which is why no counter ever appeared for it. The background-apps probe now reports all three signals per app, so which one an app actually uses is a reading rather than a guess.
This commit is contained in:
@@ -215,6 +215,42 @@ fn handle_sentinel(
|
||||
}
|
||||
}
|
||||
|
||||
// An app stating its own unread count outright. Absolute, unlike
|
||||
// the title watcher's deltas, so it replaces rather than adds.
|
||||
"badge" => {
|
||||
let Some(total) = params.get("n").and_then(|n| n.parse::<u32>().ok()) else {
|
||||
return;
|
||||
};
|
||||
let state = handle.state::<crate::commands::AppState>();
|
||||
let showing = state.active.lock().unwrap().clone();
|
||||
let previous = {
|
||||
let mut counts = state.unread.lock().unwrap();
|
||||
let previous = counts.get(&from).copied().unwrap_or(0);
|
||||
if total == 0 {
|
||||
counts.remove(&from);
|
||||
} else {
|
||||
counts.insert(from.clone(), total);
|
||||
}
|
||||
previous
|
||||
};
|
||||
let _ = handle.emit("unread-changed", crate::commands::unread_list(&state));
|
||||
|
||||
if total > previous && showing.as_deref() != Some(from.as_str()) {
|
||||
let name = state
|
||||
.cfg()
|
||||
.app(&from)
|
||||
.map(|a| a.name.clone())
|
||||
.unwrap_or_else(|| "Work".into());
|
||||
let n = total - previous;
|
||||
let title = if n == 1 { "1 new".to_string() } else { format!("{n} new") };
|
||||
notify(&handle, &from, &name, &title, &format!("{total} unread"), String::new());
|
||||
}
|
||||
}
|
||||
|
||||
"emptycache" => {
|
||||
empty_cache(&handle, &from);
|
||||
}
|
||||
|
||||
"manage" => {
|
||||
let _ = handle.emit("manage-hidden", from.clone());
|
||||
}
|
||||
@@ -486,6 +522,52 @@ pub fn set_corner_radius(handle: &AppHandle, app_id: &str, radius: f64) {
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub fn set_corner_radius(_: &AppHandle, _: &str, _: f64) {}
|
||||
|
||||
/// Throws away this app's caches and reloads it.
|
||||
///
|
||||
/// Caches only — never cookies or local storage. Emptying a browser's cache is
|
||||
/// a thing you do when a page is serving something stale; signing you out of
|
||||
/// the tool while you do it would be a different and much less welcome feature.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn empty_cache(handle: &AppHandle, app_id: &str) {
|
||||
use block2::RcBlock;
|
||||
use objc2_foundation::{MainThreadMarker, NSDate, NSSet, NSString};
|
||||
use objc2_web_kit::WKWebsiteDataStore;
|
||||
|
||||
let Some(wv) = handle.get_webview(&label_for(app_id)) else { return };
|
||||
let handle = handle.clone();
|
||||
let app_id = app_id.to_string();
|
||||
|
||||
let _ = wv.with_webview(move |_platform| unsafe {
|
||||
let Some(mtm) = MainThreadMarker::new() else { return };
|
||||
|
||||
let types = NSSet::from_retained_slice(&[
|
||||
NSString::from_str("WKWebsiteDataTypeDiskCache"),
|
||||
NSString::from_str("WKWebsiteDataTypeMemoryCache"),
|
||||
NSString::from_str("WKWebsiteDataTypeOfflineWebApplicationCache"),
|
||||
NSString::from_str("WKWebsiteDataTypeFetchCache"),
|
||||
NSString::from_str("WKWebsiteDataTypeServiceWorkerRegistrations"),
|
||||
]);
|
||||
|
||||
let done = RcBlock::new(move || {
|
||||
// Reloaded only once the cache is actually gone, or the reload
|
||||
// would refill it from what we were trying to throw away.
|
||||
if let Some(wv) = handle.get_webview(&label_for(&app_id)) {
|
||||
let _ = wv.eval("location.reload(true)");
|
||||
}
|
||||
});
|
||||
|
||||
WKWebsiteDataStore::defaultDataStore(mtm)
|
||||
.removeDataOfTypes_modifiedSince_completionHandler(
|
||||
&types,
|
||||
&NSDate::distantPast(),
|
||||
&done,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub fn empty_cache(_: &AppHandle, _: &str) {}
|
||||
|
||||
/// Turns on WKWebView's two-finger back and forward swipes.
|
||||
///
|
||||
/// wry supports it but Tauri does not expose it, so it is set on the native
|
||||
|
||||
Reference in New Issue
Block a user