mail/view: show multipart/alternative files properly in attachment list
Show entire multipart/alternative alternatives in attachment list instead of only the displayed one, in order for the user to be able to switch alternatives at will.jmap-eventsource
parent
98c1ece28d
commit
883b3e3a4f
|
@ -92,6 +92,11 @@ impl ViewMode {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AttachmentDisplay {
|
pub enum AttachmentDisplay {
|
||||||
|
Alternative {
|
||||||
|
inner: Attachment,
|
||||||
|
shown_display: usize,
|
||||||
|
display: Vec<AttachmentDisplay>,
|
||||||
|
},
|
||||||
InlineText {
|
InlineText {
|
||||||
inner: Attachment,
|
inner: Attachment,
|
||||||
comment: Option<String>,
|
comment: Option<String>,
|
||||||
|
@ -436,6 +441,17 @@ impl MailView {
|
||||||
for d in displays {
|
for d in displays {
|
||||||
use AttachmentDisplay::*;
|
use AttachmentDisplay::*;
|
||||||
match d {
|
match d {
|
||||||
|
Alternative {
|
||||||
|
inner: _,
|
||||||
|
shown_display,
|
||||||
|
display,
|
||||||
|
} => {
|
||||||
|
acc.push_str(&self.attachment_displays_to_text(
|
||||||
|
&display[*shown_display..(*shown_display + 1)],
|
||||||
|
context,
|
||||||
|
show_comments,
|
||||||
|
));
|
||||||
|
}
|
||||||
InlineText {
|
InlineText {
|
||||||
inner: _,
|
inner: _,
|
||||||
text,
|
text,
|
||||||
|
@ -554,10 +570,26 @@ impl MailView {
|
||||||
let mut paths = Vec::with_capacity(displays.len());
|
let mut paths = Vec::with_capacity(displays.len());
|
||||||
let mut cur_path = vec![];
|
let mut cur_path = vec![];
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
for (i, d) in displays.iter().enumerate() {
|
|
||||||
|
fn append_entry(
|
||||||
|
(idx, (depth, att_display)): (&mut usize, (usize, &AttachmentDisplay)),
|
||||||
|
branches: &mut SmallVec<[bool; 8]>,
|
||||||
|
paths: &mut Vec<Vec<usize>>,
|
||||||
|
cur_path: &mut Vec<usize>,
|
||||||
|
has_sibling: bool,
|
||||||
|
s: &mut String,
|
||||||
|
) {
|
||||||
use AttachmentDisplay::*;
|
use AttachmentDisplay::*;
|
||||||
cur_path.push(i);
|
let mut default_alternative: Option<usize> = None;
|
||||||
match d {
|
let (att, sub_att_display_vec) = match att_display {
|
||||||
|
Alternative {
|
||||||
|
inner,
|
||||||
|
shown_display,
|
||||||
|
display,
|
||||||
|
} => {
|
||||||
|
default_alternative = Some(*shown_display);
|
||||||
|
(inner, display.as_slice())
|
||||||
|
}
|
||||||
InlineText {
|
InlineText {
|
||||||
inner,
|
inner,
|
||||||
text: _,
|
text: _,
|
||||||
|
@ -565,41 +597,95 @@ impl MailView {
|
||||||
}
|
}
|
||||||
| InlineOther { inner }
|
| InlineOther { inner }
|
||||||
| Attachment { inner }
|
| Attachment { inner }
|
||||||
| SignedPending {
|
| EncryptedPending { inner, handle: _ }
|
||||||
|
| EncryptedFailed { inner, error: _ } => (inner, &[][..]),
|
||||||
|
SignedPending {
|
||||||
inner,
|
inner,
|
||||||
display: _,
|
display,
|
||||||
handle: _,
|
handle: _,
|
||||||
job_id: _,
|
job_id: _,
|
||||||
}
|
}
|
||||||
| SignedUnverified { inner, display: _ }
|
| SignedUnverified { inner, display }
|
||||||
| SignedFailed {
|
| SignedFailed {
|
||||||
inner,
|
inner,
|
||||||
display: _,
|
display,
|
||||||
error: _,
|
error: _,
|
||||||
}
|
}
|
||||||
| SignedVerified {
|
| SignedVerified {
|
||||||
inner,
|
inner,
|
||||||
display: _,
|
display,
|
||||||
description: _,
|
description: _,
|
||||||
}
|
}
|
||||||
| EncryptedPending { inner, handle: _ }
|
|
||||||
| EncryptedFailed { inner, error: _ }
|
|
||||||
| EncryptedSuccess {
|
| EncryptedSuccess {
|
||||||
inner: _,
|
inner: _,
|
||||||
plaintext: inner,
|
plaintext: inner,
|
||||||
plaintext_display: _,
|
plaintext_display: display,
|
||||||
description: _,
|
description: _,
|
||||||
} => {
|
} => (inner, display.as_slice()),
|
||||||
attachment_tree(
|
};
|
||||||
(&mut idx, (0, inner)),
|
s.extend(format!("\n[{}]", idx).chars());
|
||||||
&mut branches,
|
for &b in branches.iter() {
|
||||||
&mut paths,
|
if b {
|
||||||
&mut cur_path,
|
s.push('|');
|
||||||
i + 1 < displays.len(),
|
} else {
|
||||||
&mut acc,
|
s.push(' ');
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
s.push(' ');
|
||||||
}
|
}
|
||||||
|
if depth > 0 {
|
||||||
|
if has_sibling {
|
||||||
|
s.push('|');
|
||||||
|
} else {
|
||||||
|
s.push(' ');
|
||||||
|
}
|
||||||
|
s.push_str("\\_ ");
|
||||||
|
} else {
|
||||||
|
s.push(' ');
|
||||||
|
s.push(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
s.extend(att.to_string().chars());
|
||||||
|
paths.push(cur_path.clone());
|
||||||
|
match att.content_type {
|
||||||
|
ContentType::Multipart { .. } => {
|
||||||
|
let mut iter = (0..sub_att_display_vec.len()).peekable();
|
||||||
|
if has_sibling {
|
||||||
|
branches.push(true);
|
||||||
|
} else {
|
||||||
|
branches.push(false);
|
||||||
|
}
|
||||||
|
while let Some(i) = iter.next() {
|
||||||
|
*idx += 1;
|
||||||
|
cur_path.push(i);
|
||||||
|
append_entry(
|
||||||
|
(idx, (depth + 1, &sub_att_display_vec[i])),
|
||||||
|
branches,
|
||||||
|
paths,
|
||||||
|
cur_path,
|
||||||
|
iter.peek() != None,
|
||||||
|
s,
|
||||||
|
);
|
||||||
|
if Some(i) == default_alternative {
|
||||||
|
s.push_str(" (displayed by default)");
|
||||||
|
}
|
||||||
|
cur_path.pop();
|
||||||
|
}
|
||||||
|
branches.pop();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, d) in displays.iter().enumerate() {
|
||||||
|
cur_path.push(i);
|
||||||
|
append_entry(
|
||||||
|
(&mut idx, (0, d)),
|
||||||
|
&mut branches,
|
||||||
|
&mut paths,
|
||||||
|
&mut cur_path,
|
||||||
|
i + 1 < displays.len(),
|
||||||
|
&mut acc,
|
||||||
|
);
|
||||||
cur_path.pop();
|
cur_path.pop();
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
|
@ -692,6 +778,11 @@ impl MailView {
|
||||||
{
|
{
|
||||||
match kind {
|
match kind {
|
||||||
MultipartType::Alternative => {
|
MultipartType::Alternative => {
|
||||||
|
if parts.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut display = vec![];
|
||||||
|
let mut chosen_attachment_idx = 0;
|
||||||
if let Some(text_attachment_pos) =
|
if let Some(text_attachment_pos) =
|
||||||
parts.iter().position(|a| a.content_type == "text/plain")
|
parts.iter().position(|a| a.content_type == "text/plain")
|
||||||
{
|
{
|
||||||
|
@ -708,33 +799,21 @@ impl MailView {
|
||||||
parts.iter().position(|a| a.content_type == "text/html")
|
parts.iter().position(|a| a.content_type == "text/html")
|
||||||
{
|
{
|
||||||
/* Select html alternative since text/plain is empty */
|
/* Select html alternative since text/plain is empty */
|
||||||
rec(
|
chosen_attachment_idx = text_attachment_pos;
|
||||||
&parts[text_attachment_pos],
|
|
||||||
context,
|
|
||||||
coordinates,
|
|
||||||
acc,
|
|
||||||
active_jobs,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
for a in parts {
|
|
||||||
rec(a, context, coordinates, acc, active_jobs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Select text/plain alternative */
|
/* Select text/plain alternative */
|
||||||
rec(
|
chosen_attachment_idx = text_attachment_pos;
|
||||||
&parts[text_attachment_pos],
|
|
||||||
context,
|
|
||||||
coordinates,
|
|
||||||
acc,
|
|
||||||
active_jobs,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for a in parts {
|
|
||||||
rec(a, context, coordinates, acc, active_jobs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for a in parts {
|
||||||
|
rec(a, context, coordinates, &mut display, active_jobs);
|
||||||
|
}
|
||||||
|
acc.push(AttachmentDisplay::Alternative {
|
||||||
|
inner: a.clone(),
|
||||||
|
shown_display: chosen_attachment_idx,
|
||||||
|
display,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
MultipartType::Signed => {
|
MultipartType::Signed => {
|
||||||
#[cfg(not(feature = "gpgme"))]
|
#[cfg(not(feature = "gpgme"))]
|
||||||
|
@ -868,7 +947,12 @@ impl MailView {
|
||||||
let first = path[0];
|
let first = path[0];
|
||||||
use AttachmentDisplay::*;
|
use AttachmentDisplay::*;
|
||||||
let root_attachment = match &display[first] {
|
let root_attachment = match &display[first] {
|
||||||
InlineText {
|
Alternative {
|
||||||
|
inner,
|
||||||
|
shown_display: _,
|
||||||
|
display: _,
|
||||||
|
}
|
||||||
|
| InlineText {
|
||||||
inner,
|
inner,
|
||||||
text: _,
|
text: _,
|
||||||
comment: _,
|
comment: _,
|
||||||
|
@ -2441,67 +2525,6 @@ impl Component for MailView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attachment_tree(
|
|
||||||
(idx, (depth, att)): (&mut usize, (usize, &Attachment)),
|
|
||||||
branches: &mut SmallVec<[bool; 8]>,
|
|
||||||
paths: &mut Vec<Vec<usize>>,
|
|
||||||
cur_path: &mut Vec<usize>,
|
|
||||||
has_sibling: bool,
|
|
||||||
s: &mut String,
|
|
||||||
) {
|
|
||||||
s.extend(format!("\n[{}]", idx).chars());
|
|
||||||
for &b in branches.iter() {
|
|
||||||
if b {
|
|
||||||
s.push('|');
|
|
||||||
} else {
|
|
||||||
s.push(' ');
|
|
||||||
}
|
|
||||||
s.push(' ');
|
|
||||||
}
|
|
||||||
if depth > 0 {
|
|
||||||
if has_sibling {
|
|
||||||
s.push('|');
|
|
||||||
} else {
|
|
||||||
s.push(' ');
|
|
||||||
}
|
|
||||||
s.push_str("\\_ ");
|
|
||||||
} else {
|
|
||||||
s.push(' ');
|
|
||||||
s.push(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
s.extend(att.to_string().chars());
|
|
||||||
paths.push(cur_path.clone());
|
|
||||||
match att.content_type {
|
|
||||||
ContentType::Multipart {
|
|
||||||
parts: ref sub_att_vec,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let mut iter = (0..sub_att_vec.len()).peekable();
|
|
||||||
if has_sibling {
|
|
||||||
branches.push(true);
|
|
||||||
} else {
|
|
||||||
branches.push(false);
|
|
||||||
}
|
|
||||||
while let Some(i) = iter.next() {
|
|
||||||
*idx += 1;
|
|
||||||
cur_path.push(i);
|
|
||||||
attachment_tree(
|
|
||||||
(idx, (depth + 1, &sub_att_vec[i])),
|
|
||||||
branches,
|
|
||||||
paths,
|
|
||||||
cur_path,
|
|
||||||
iter.peek() != None,
|
|
||||||
s,
|
|
||||||
);
|
|
||||||
cur_path.pop();
|
|
||||||
}
|
|
||||||
branches.pop();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn save_attachment(path: &std::path::Path, bytes: &[u8]) -> Result<()> {
|
fn save_attachment(path: &std::path::Path, bytes: &[u8]) -> Result<()> {
|
||||||
let mut f = std::fs::File::create(path)?;
|
let mut f = std::fs::File::create(path)?;
|
||||||
let mut permissions = f.metadata()?.permissions();
|
let mut permissions = f.metadata()?.permissions();
|
||||||
|
|
Loading…
Reference in New Issue