feat: unify the detail view with QR sharing and default pane layout

This commit is contained in:
randogoth 2026-10-03 09:55:00 +03:00
parent 84650e50bd
commit 22c20427f0
4 changed files with 256 additions and 164 deletions

View file

@ -22,7 +22,6 @@ pub enum Page {
Onboard,
Signup,
Mail,
Contacts,
Settings,
}
@ -159,13 +158,15 @@ pub enum Message {
// Contacts
/// A sidebar contact was clicked: the article pane shows its details.
ContactSelected(String),
/// The own identity was clicked: the same detail view, this time with
/// our own key lines in it.
OwnSelected,
ContactImportChanged(String),
ContactImportPressed,
ContactWrite(String),
ContactAccept(String, String),
ContactBlock(String),
ContactUnblock(String),
CopyOwnAddress,
CopyExport,
// Settings
@ -272,6 +273,8 @@ pub struct Carrier {
/// The contact whose details the article pane shows, selected from
/// the sidebar's bottom panel; a message selection clears it.
pub selected_contact: Option<String>,
/// True when the article pane shows our own identity's details.
pub selected_own: bool,
pub compose: Option<Compose>,
/// Composes set aside by a sidebar trip instead of Cancel, kept in
/// the drafts sidecar; the folder only shows once it holds one.
@ -333,6 +336,7 @@ impl Carrier {
lists: vec![Vec::new(), Vec::new(), Vec::new()],
open: None,
selected_contact: None,
selected_own: false,
compose: None,
drafts,
snapshot: None,
@ -519,10 +523,18 @@ impl Carrier {
}
Message::ContactSelected(address) => {
self.stash_draft();
self.selected_own = false;
self.selected_contact = Some(address);
self.open = None;
self.page = Page::Mail;
}
Message::OwnSelected => {
self.stash_draft();
self.selected_own = true;
self.selected_contact = None;
self.open = None;
self.page = Page::Mail;
}
Message::FetchCancelled => {
if self.busy.is_some() {
self.cancel.store(true, Ordering::Relaxed);
@ -661,6 +673,7 @@ impl Carrier {
self.page = Page::Mail;
self.folder = folder;
self.open = None;
self.selected_own = false;
self.selected_contact = None;
return self.refresh().into_iter().collect();
}
@ -668,12 +681,14 @@ impl Carrier {
self.stash_draft();
self.page = page;
self.open = None;
self.selected_own = false;
self.selected_contact = None;
if page == Page::Mail || page == Page::Contacts || page == Page::Settings {
if page == Page::Mail || page == Page::Settings {
return self.refresh().into_iter().collect();
}
}
Message::MessageSelected(folder, id) => {
self.selected_own = false;
self.selected_contact = None;
if folder == Folder::Drafts {
self.open_draft(&id);
@ -866,14 +881,6 @@ impl Carrier {
};
return vec![PendingOp::Accept { address, identity }];
}
Message::CopyOwnAddress => {
if let Some(snap) = &self.snapshot {
let uri = format!("smol://{}/{}", snap.who.address, snap.who.key);
if let Some(display) = gtk::gdk::Display::default() {
display.clipboard().set_text(&uri);
}
}
}
Message::CopyExport => {
if let Some(text) = &self.backup_text {
if let Some(display) = gtk::gdk::Display::default() {
@ -1036,6 +1043,27 @@ mod tests {
assert_eq!(app.busy.as_deref(), Some("fetching"));
}
#[test]
fn own_and_contact_selection_supersede_each_other() {
let mut app = app();
app.handle(Message::OwnSelected);
assert!(app.selected_own);
assert_eq!(app.page, Page::Mail);
assert!(app.open.is_none());
app.handle(Message::ContactSelected("bob@localhost".into()));
assert!(!app.selected_own);
assert_eq!(app.selected_contact.as_deref(), Some("bob@localhost"));
app.handle(Message::OwnSelected);
assert!(app.selected_own);
assert!(app.selected_contact.is_none());
// A folder trip closes the detail, own included.
app.handle(Message::GotoMail(Folder::Sent));
assert!(!app.selected_own);
}
#[test]
fn text_edits_pass_the_busy_guard() {
let mut app = app();