feat: ask whether a reply carries the sender's frontmatter
This commit is contained in:
parent
dacbc9de38
commit
cda8985218
4 changed files with 167 additions and 10 deletions
108
src/app.rs
108
src/app.rs
|
|
@ -107,6 +107,14 @@ pub enum Dialog {
|
|||
},
|
||||
/// Rotation only moves the index forward, so the action asks first.
|
||||
Rotate,
|
||||
/// The message being answered carries the sender's own frontmatter
|
||||
/// fields; the reply asks whether to carry copies of them.
|
||||
Reply {
|
||||
to: String,
|
||||
subject: String,
|
||||
id: String,
|
||||
rows: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
/// UI inputs. Background results arrive as [`OpResult`] instead, so the
|
||||
|
|
@ -154,6 +162,8 @@ pub enum Message {
|
|||
ComposeClosed,
|
||||
SendPressed,
|
||||
ReplyPressed,
|
||||
ReplyCancelled,
|
||||
ReplyConfirmed(bool),
|
||||
DeletePressed,
|
||||
DeleteConfirmed(bool),
|
||||
DeleteCancelled,
|
||||
|
|
@ -763,12 +773,49 @@ impl Carrier {
|
|||
} else {
|
||||
format!("Re: {}", msg.subject)
|
||||
};
|
||||
// The sender's own fields are the one thing a reply has to
|
||||
// decide about: carry copies, or start clean. Only the
|
||||
// fields that would survive send-time validation ask —
|
||||
// a mail whose only extra field is the threading id
|
||||
// replies straight through.
|
||||
let rows = ops::reply_rows(&msg.headers);
|
||||
if rows.is_empty() {
|
||||
self.compose = Some(Compose {
|
||||
to,
|
||||
subject,
|
||||
body: String::new(),
|
||||
reply_to: Some(msg.id.clone()),
|
||||
headers: Vec::new(),
|
||||
});
|
||||
self.compose_fresh.set(true);
|
||||
} else {
|
||||
self.dialog = Some(Dialog::Reply {
|
||||
to,
|
||||
subject,
|
||||
id: msg.id.clone(),
|
||||
rows,
|
||||
});
|
||||
}
|
||||
}
|
||||
Message::ReplyCancelled => {
|
||||
self.dialog = None;
|
||||
}
|
||||
Message::ReplyConfirmed(copy) => {
|
||||
let Some(Dialog::Reply {
|
||||
to,
|
||||
subject,
|
||||
id,
|
||||
rows,
|
||||
}) = self.dialog.take()
|
||||
else {
|
||||
return Vec::new();
|
||||
};
|
||||
self.compose = Some(Compose {
|
||||
to,
|
||||
subject,
|
||||
body: String::new(),
|
||||
reply_to: Some(msg.id.clone()),
|
||||
headers: Vec::new(),
|
||||
reply_to: Some(id),
|
||||
headers: if copy { rows } else { Vec::new() },
|
||||
});
|
||||
self.compose_fresh.set(true);
|
||||
}
|
||||
|
|
@ -1245,6 +1292,63 @@ mod tests {
|
|||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replying_to_custom_fields_asks_what_to_carry() {
|
||||
let mut app = app();
|
||||
app.open = Some((
|
||||
Folder::Inbox,
|
||||
OpenedMsg {
|
||||
subject: "hello".into(),
|
||||
headers: vec![
|
||||
("priority".to_string(), "high".to_string()),
|
||||
("in-reply-to".to_string(), "cd".repeat(32)),
|
||||
],
|
||||
..opened()
|
||||
},
|
||||
));
|
||||
|
||||
// The ask: a dialog, not a compose yet, and cancelling aborts.
|
||||
app.handle(Message::ReplyPressed);
|
||||
assert!(app.compose.is_none());
|
||||
assert!(matches!(app.dialog, Some(Dialog::Reply { .. })));
|
||||
app.handle(Message::ReplyCancelled);
|
||||
assert!(app.dialog.is_none());
|
||||
assert!(app.compose.is_none());
|
||||
|
||||
// Either answer opens the same reply; the fields follow the choice.
|
||||
app.handle(Message::ReplyPressed);
|
||||
app.handle(Message::ReplyConfirmed(false));
|
||||
let compose = app.compose.as_ref().expect("reply open");
|
||||
assert_eq!(compose.subject, "Re: hello");
|
||||
assert!(compose.headers.is_empty());
|
||||
assert_eq!(compose.reply_to.as_deref(), Some("ab".repeat(32).as_str()));
|
||||
|
||||
app.compose = None;
|
||||
app.handle(Message::ReplyPressed);
|
||||
app.handle(Message::ReplyConfirmed(true));
|
||||
let compose = app.compose.as_ref().expect("reply open");
|
||||
// The sender's field arrives; the threading id does not — the
|
||||
// reply carries its own.
|
||||
assert_eq!(compose.headers, ["priority: high"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_thread_only_mail_replies_straight_through() {
|
||||
let mut app = app();
|
||||
app.open = Some((
|
||||
Folder::Inbox,
|
||||
OpenedMsg {
|
||||
subject: "hello".into(),
|
||||
headers: vec![("in-reply-to".to_string(), "cd".repeat(32))],
|
||||
..opened()
|
||||
},
|
||||
));
|
||||
app.handle(Message::ReplyPressed);
|
||||
assert!(app.dialog.is_none());
|
||||
assert!(app.compose.is_some());
|
||||
assert!(app.compose.as_ref().unwrap().headers.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn headers_survive_a_stash_and_reopen() {
|
||||
let mut app = app();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue