1
0
forked from GRIN/grim

nostr: pause the @name re-verify sweep while the app is backgrounded

The 78s sweep fired regardless of whether anyone was looking, spending mixnet
round-trips in the background. Gate it on a frame heartbeat: the GUI stamps
crate::mark_frame() each draw (with a light ~2s repaint cadence so it stays
fresh while visible); when the app is backgrounded eframe stops drawing and the
stamp goes stale, so crate::app_foreground() reads false and the sweep skips.

The skip deliberately does NOT advance last_name_sweep, so the first tick after
the app returns to the foreground runs the sweep immediately — catching up on
resume rather than waiting out another full interval. Heartbeat lives at the
crate root so nostr reads it without depending on the gui module.
This commit is contained in:
2ro
2026-06-16 22:58:07 -04:00
parent 55b78b78ef
commit 991670d863
3 changed files with 44 additions and 1 deletions
+7
View File
@@ -77,6 +77,13 @@ impl<Platform: PlatformCallbacks> App<Platform> {
self.first_draw = false;
}
// Heartbeat for "is the app on-screen": stamp this frame, and keep a light
// ~2s repaint cadence so the stamp stays fresh while visible. When the app
// is backgrounded eframe stops calling this, the stamp goes stale, and
// background workers (the @name re-verify sweep) pause until we're back.
crate::mark_frame();
ctx.request_repaint_after(std::time::Duration::from_secs(2));
// Keep the Android status-bar icons readable against the in-app theme
// (the app draws edge-to-edge over the status bar). Only on change.
let white_icons = crate::gui::theme::status_bar_white_icons();
+32
View File
@@ -378,6 +378,38 @@ pub fn on_data(data: String) {
*w_data = Some(data);
}
/// Unix-seconds timestamp of the most recent GUI frame. Background workers read
/// it to tell whether the app is actually on-screen: while the app is
/// backgrounded, eframe stops calling the per-frame draw and this stops
/// advancing. Crate-root so both `gui` and `nostr` can reach it without coupling.
static LAST_FRAME_AT: std::sync::atomic::AtomicI64 = std::sync::atomic::AtomicI64::new(0);
/// A frame older than this many seconds means the app isn't drawing — i.e. it's
/// backgrounded/occluded. The GUI keeps a ~2s repaint heartbeat while visible, so
/// this leaves a couple of frames of margin before declaring "not foreground".
const FOREGROUND_STALE_SECS: i64 = 5;
fn now_unix_secs() -> i64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs() as i64)
.unwrap_or(0)
}
/// Stamp that the GUI just drew a frame. Called once per frame from the app loop.
pub fn mark_frame() {
LAST_FRAME_AT.store(now_unix_secs(), std::sync::atomic::Ordering::Relaxed);
}
/// True when the GUI drew a frame within the last few seconds — i.e. the app is
/// foreground and visible. While backgrounded (no frames), returns false, so
/// periodic background work (the @name re-verify sweep) can pause and catch up
/// on resume instead of burning mixnet round-trips while nobody's looking.
pub fn app_foreground() -> bool {
let last = LAST_FRAME_AT.load(std::sync::atomic::Ordering::Relaxed);
last != 0 && now_unix_secs() - last <= FOREGROUND_STALE_SECS
}
lazy_static! {
/// Data provided from deeplink or opened file.
pub static ref INCOMING_DATA: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
+5 -1
View File
@@ -894,7 +894,11 @@ async fn run_service(svc: Arc<NostrService>, wallet: Wallet) {
// Re-validate cached @usernames so a released/reassigned name
// stops showing. Only the stalest few per sweep (capped) to bound
// mixnet lookups; each worker re-checks against the identity server.
if now - last_name_sweep >= NAME_REVERIFY_INTERVAL_SECS {
// Skipped while the app is backgrounded — no point spending mixnet
// round-trips when nobody's looking. We DON'T advance last_name_sweep
// in that case, so the very next foreground tick runs the sweep
// immediately to catch up on resume.
if now - last_name_sweep >= NAME_REVERIFY_INTERVAL_SECS && crate::app_foreground() {
last_name_sweep = now;
svc.store.set_last_name_sweep_at(now);
let mut due: Vec<_> = svc