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<RefCell<Option<Daemon>>>. Phases 9, 10, 11, and 12 GUI tab will all consume this struct. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
27
gui/src/app_context.rs
Normal file
27
gui/src/app_context.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//! Shared per-window handles passed to every page constructor.
|
||||||
|
//!
|
||||||
|
//! `daemon` is a `RefCell<Option<...>>` 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<RefCell<Option<Daemon>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +1,23 @@
|
|||||||
//! "Security keys" preferences page. v1 surface: the current user's enrolled
|
//! "Security keys" preferences page. v1 surface: the current user's enrolled
|
||||||
//! FIDO2 credentials with per-row remove + a single "Enroll a new key" button.
|
//! 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::bus::{current_user, Daemon};
|
||||||
use crate::error::user_message;
|
use crate::error::user_message;
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use gtk::glib;
|
use gtk::glib;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub(crate) struct KeysPage {
|
pub(crate) struct KeysPage {
|
||||||
pub root: adw::Bin,
|
pub root: adw::Bin,
|
||||||
daemon: Rc<RefCell<Option<Daemon>>>,
|
ctx: AppContext,
|
||||||
parent_window: gtk::Window,
|
|
||||||
toast_overlay: adw::ToastOverlay,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeysPage {
|
impl KeysPage {
|
||||||
pub fn new(parent_window: gtk::Window, toast_overlay: adw::ToastOverlay) -> Rc<Self> {
|
pub fn new(ctx: AppContext) -> Rc<Self> {
|
||||||
let page = Rc::new(Self {
|
let page = Rc::new(Self {
|
||||||
root: adw::Bin::new(),
|
root: adw::Bin::new(),
|
||||||
daemon: Rc::new(RefCell::new(None)),
|
ctx,
|
||||||
parent_window,
|
|
||||||
toast_overlay,
|
|
||||||
});
|
});
|
||||||
let p = page.clone();
|
let p = page.clone();
|
||||||
glib::MainContext::default().spawn_local(async move {
|
glib::MainContext::default().spawn_local(async move {
|
||||||
@@ -33,7 +29,7 @@ impl KeysPage {
|
|||||||
async fn try_connect_and_render(self: Rc<Self>) {
|
async fn try_connect_and_render(self: Rc<Self>) {
|
||||||
match Daemon::connect().await {
|
match Daemon::connect().await {
|
||||||
Ok(d) => {
|
Ok(d) => {
|
||||||
*self.daemon.borrow_mut() = Some(d);
|
*self.ctx.daemon.borrow_mut() = Some(d);
|
||||||
self.render_connected().await;
|
self.render_connected().await;
|
||||||
}
|
}
|
||||||
Err(e) => self.render_disconnected(&user_message(&e)),
|
Err(e) => self.render_disconnected(&user_message(&e)),
|
||||||
@@ -63,7 +59,7 @@ impl KeysPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn render_connected(self: &Rc<Self>) {
|
async fn render_connected(self: &Rc<Self>) {
|
||||||
let daemon = match self.daemon.borrow().clone() {
|
let daemon = match self.ctx.daemon.borrow().clone() {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
None => {
|
None => {
|
||||||
self.render_disconnected("internal: daemon handle missing");
|
self.render_disconnected("internal: daemon handle missing");
|
||||||
@@ -140,7 +136,7 @@ impl KeysPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_credential(self: &Rc<Self>, cred_id: &str) {
|
async fn remove_credential(self: &Rc<Self>, cred_id: &str) {
|
||||||
let daemon = match self.daemon.borrow().clone() {
|
let daemon = match self.ctx.daemon.borrow().clone() {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
@@ -153,12 +149,12 @@ impl KeysPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn start_enroll(self: &Rc<Self>) {
|
async fn start_enroll(self: &Rc<Self>) {
|
||||||
let daemon = match self.daemon.borrow().clone() {
|
let daemon = match self.ctx.daemon.borrow().clone() {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
let me = self.clone();
|
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();
|
let me = me.clone();
|
||||||
glib::MainContext::default().spawn_local(async move {
|
glib::MainContext::default().spawn_local(async move {
|
||||||
me.render_connected().await;
|
me.render_connected().await;
|
||||||
@@ -168,7 +164,7 @@ impl KeysPage {
|
|||||||
|
|
||||||
fn show_toast(self: &Rc<Self>, msg: &str) {
|
fn show_toast(self: &Rc<Self>, msg: &str) {
|
||||||
let toast = adw::Toast::builder().title(msg).timeout(5).build();
|
let toast = adw::Toast::builder().title(msg).timeout(5).build();
|
||||||
self.toast_overlay.add_toast(toast);
|
self.ctx.toast_overlay.add_toast(toast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use gtk::glib;
|
use gtk::glib;
|
||||||
|
|
||||||
|
mod app_context;
|
||||||
mod bus;
|
mod bus;
|
||||||
mod enroll_dialog;
|
mod enroll_dialog;
|
||||||
mod error;
|
mod error;
|
||||||
@@ -36,7 +37,8 @@ fn main() -> glib::ExitCode {
|
|||||||
header.set_title_widget(Some(&switcher));
|
header.set_title_widget(Some(&switcher));
|
||||||
|
|
||||||
let toast_overlay = adw::ToastOverlay::new();
|
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");
|
stack.add_titled(&keys.root, Some("keys"), "Keys");
|
||||||
|
|
||||||
// ToolbarView is libadwaita v1_4-gated; the project sticks with the
|
// ToolbarView is libadwaita v1_4-gated; the project sticks with the
|
||||||
|
|||||||
Reference in New Issue
Block a user