From a927d966309ca6e1619a00ab9b2dd110fc3ecae8 Mon Sep 17 00:00:00 2001 From: michael Date: Mon, 27 Apr 2026 10:42:11 -0700 Subject: [PATCH] refactor(gui): extract AppContext for shared page handles KeysPage::new now takes a single AppContext that owns parent_window, toast_overlay, and the shared daemon Rc>>. Phases 9, 10, 11, and 12 GUI tab will all consume this struct. Co-Authored-By: Claude Opus 4.7 (1M context) --- gui/src/app_context.rs | 27 +++++++++++++++++++++++++++ gui/src/keys_page.rs | 24 ++++++++++-------------- gui/src/main.rs | 4 +++- 3 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 gui/src/app_context.rs diff --git a/gui/src/app_context.rs b/gui/src/app_context.rs new file mode 100644 index 0000000..6a13868 --- /dev/null +++ b/gui/src/app_context.rs @@ -0,0 +1,27 @@ +//! Shared per-window handles passed to every page constructor. +//! +//! `daemon` is a `RefCell>` because the connect-on-startup attempt +//! may fail (pages render their disconnected banner); a successful Retry +//! repopulates this same cell, and every page sees the new value via the Rc +//! clone chain. + +use crate::bus::Daemon; +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Clone)] +pub(crate) struct AppContext { + pub parent_window: gtk::Window, + pub toast_overlay: adw::ToastOverlay, + pub daemon: Rc>>, +} + +impl AppContext { + pub fn new(parent_window: gtk::Window, toast_overlay: adw::ToastOverlay) -> Self { + Self { + parent_window, + toast_overlay, + daemon: Rc::new(RefCell::new(None)), + } + } +} diff --git a/gui/src/keys_page.rs b/gui/src/keys_page.rs index c85300a..353a2b5 100644 --- a/gui/src/keys_page.rs +++ b/gui/src/keys_page.rs @@ -1,27 +1,23 @@ //! "Security keys" preferences page. v1 surface: the current user's enrolled //! FIDO2 credentials with per-row remove + a single "Enroll a new key" button. +use crate::app_context::AppContext; use crate::bus::{current_user, Daemon}; use crate::error::user_message; use adw::prelude::*; use gtk::glib; -use std::cell::RefCell; use std::rc::Rc; pub(crate) struct KeysPage { pub root: adw::Bin, - daemon: Rc>>, - parent_window: gtk::Window, - toast_overlay: adw::ToastOverlay, + ctx: AppContext, } impl KeysPage { - pub fn new(parent_window: gtk::Window, toast_overlay: adw::ToastOverlay) -> Rc { + pub fn new(ctx: AppContext) -> Rc { let page = Rc::new(Self { root: adw::Bin::new(), - daemon: Rc::new(RefCell::new(None)), - parent_window, - toast_overlay, + ctx, }); let p = page.clone(); glib::MainContext::default().spawn_local(async move { @@ -33,7 +29,7 @@ impl KeysPage { async fn try_connect_and_render(self: Rc) { match Daemon::connect().await { Ok(d) => { - *self.daemon.borrow_mut() = Some(d); + *self.ctx.daemon.borrow_mut() = Some(d); self.render_connected().await; } Err(e) => self.render_disconnected(&user_message(&e)), @@ -63,7 +59,7 @@ impl KeysPage { } async fn render_connected(self: &Rc) { - let daemon = match self.daemon.borrow().clone() { + let daemon = match self.ctx.daemon.borrow().clone() { Some(d) => d, None => { self.render_disconnected("internal: daemon handle missing"); @@ -140,7 +136,7 @@ impl KeysPage { } async fn remove_credential(self: &Rc, cred_id: &str) { - let daemon = match self.daemon.borrow().clone() { + let daemon = match self.ctx.daemon.borrow().clone() { Some(d) => d, None => return, }; @@ -153,12 +149,12 @@ impl KeysPage { } async fn start_enroll(self: &Rc) { - let daemon = match self.daemon.borrow().clone() { + let daemon = match self.ctx.daemon.borrow().clone() { Some(d) => d, None => return, }; let me = self.clone(); - crate::enroll_dialog::present(&self.parent_window, daemon, move || { + crate::enroll_dialog::present(&self.ctx.parent_window, daemon, move || { let me = me.clone(); glib::MainContext::default().spawn_local(async move { me.render_connected().await; @@ -168,7 +164,7 @@ impl KeysPage { fn show_toast(self: &Rc, msg: &str) { let toast = adw::Toast::builder().title(msg).timeout(5).build(); - self.toast_overlay.add_toast(toast); + self.ctx.toast_overlay.add_toast(toast); } } diff --git a/gui/src/main.rs b/gui/src/main.rs index 133775e..9d0634e 100644 --- a/gui/src/main.rs +++ b/gui/src/main.rs @@ -1,6 +1,7 @@ use adw::prelude::*; use gtk::glib; +mod app_context; mod bus; mod enroll_dialog; mod error; @@ -36,7 +37,8 @@ fn main() -> glib::ExitCode { header.set_title_widget(Some(&switcher)); let toast_overlay = adw::ToastOverlay::new(); - let keys = keys_page::KeysPage::new(win.clone().upcast(), toast_overlay.clone()); + let ctx = app_context::AppContext::new(win.clone().upcast(), toast_overlay.clone()); + let keys = keys_page::KeysPage::new(ctx.clone()); stack.add_titled(&keys.root, Some("keys"), "Keys"); // ToolbarView is libadwaita v1_4-gated; the project sticks with the