web: render messages that are solely text/plain.

This commit is contained in:
Bill Thiede 2021-11-08 20:25:17 -08:00
parent 066dca02ca
commit 5ad8e1449f

View File

@ -5,9 +5,8 @@
use log::{error, info, Level};
use seed::{prelude::*, *};
use serde::Deserialize;
use notmuch::{Message, SearchSummary, ThreadNode, ThreadSet};
use notmuch::{Content, Part, SearchSummary, ThreadNode, ThreadSet};
// ------ ------
// Init
@ -17,7 +16,7 @@ use notmuch::{Message, SearchSummary, ThreadNode, ThreadSet};
fn init(_: Url, orders: &mut impl Orders<Msg>) -> Model {
orders
.skip()
.perform_cmd(async { Msg::SearchResult(search_request("is:unread").await) });
.perform_cmd(async { Msg::SearchResult(search_request("*").await) });
Model {
search_results: None,
show_results: None,
@ -120,61 +119,139 @@ async fn show_request(query: &str) -> fetch::Result<ThreadSet> {
// View
// ------ ------
// <subject>
// <tags>
//
// <from1> <date>
// <to1>
// <content1>
// <zippy>
// <children1>
// </zippy>
//
// <from2> <date>
// <to2>
// <body2>
fn view_message(thread: &ThreadNode) -> Node<Msg> {
let msg = thread.0.as_ref();
if let Some(msg) = msg {
let message = thread.0.as_ref().expect("ThreadNode missing Message");
let children = &thread.1;
div![
C!["message"],
/* TODO(wathiede): collect all the tags and show them here. */
div![C!["header"], "From: ", &message.headers.from],
div![C!["header"], "Date: ", &message.headers.date],
div![C!["header"], "To: ", &message.headers.to],
div![
a![attrs! {At::Href=>api::original(&msg.id)}, "Original"],
table![
tr![th!["Subject"], td![&msg.headers.subject],],
tr![th!["From"], td![&msg.headers.from],],
tr![th!["To"], td![&msg.headers.to],],
tr![th!["CC"], td![&msg.headers.cc],],
tr![th!["BCC"], td![&msg.headers.bcc],],
tr![th!["Reply-To"], td![&msg.headers.reply_to],],
tr![th!["Date"], td![&msg.headers.date],],
C!["body"],
match &message.body {
Some(body) => view_body(body.as_slice()),
None => div!["<no body>"],
},
],
children.iter().map(view_message)
]
}
fn view_body(body: &[Part]) -> Node<Msg> {
div![body.iter().map(view_part)]
}
fn view_part(part: &Part) -> Node<Msg> {
match part.content_type.as_str() {
"text/plain" => match &part.content {
Some(Content::String(content)) => pre![content],
_ => div![
C!["error"],
format!("Unhandled content enum for text/plain"),
],
table![
tr![th!["MessageId"], td![&msg.id],],
tr![
th!["Match"],
td![if msg.r#match { "true" } else { "false" }],
],
tr![
th!["Excluded"],
td![if msg.excluded { "true" } else { "false" }],
],
tr![th!["Filename"], td![&msg.filename],],
tr![th!["Timestamp"], td![msg.timestamp.to_string()],],
tr![th!["Date"], td![&msg.date_relative],],
tr![th!["Tags"], td![format!("{:?}", msg.tags)],],
],
// msg.body.iter().map(|part| pre![part])
pre![format!("{:#?}", thread)]
]
} else {
div![h2!["No message"]]
},
_ => div![
C!["error"],
format!("Unhandled content type: {}", part.content_type)
],
}
}
fn first_subject(thread: &ThreadNode) -> Option<String> {
if let Some(msg) = &thread.0 {
return Some(msg.headers.subject.clone());
} else {
for tn in &thread.1 {
if let Some(s) = first_subject(&tn) {
return Some(s);
}
}
}
None
}
/*
let msg = thread.0.as_ref();
if let Some(msg) = msg {
div![
a![attrs! {At::Href=>api::original(&msg.id)}, "Original"],
table![
tr![th!["Subject"], td![&msg.headers.subject],],
tr![th!["From"], td![&msg.headers.from],],
tr![th!["To"], td![&msg.headers.to],],
tr![th!["CC"], td![&msg.headers.cc],],
tr![th!["BCC"], td![&msg.headers.bcc],],
tr![th!["Reply-To"], td![&msg.headers.reply_to],],
tr![th!["Date"], td![&msg.headers.date],],
],
table![
tr![th!["MessageId"], td![&msg.id],],
tr![
th!["Match"],
td![if msg.r#match { "true" } else { "false" }],
],
tr![
th!["Excluded"],
td![if msg.excluded { "true" } else { "false" }],
],
tr![th!["Filename"], td![&msg.filename],],
tr![th!["Timestamp"], td![msg.timestamp.to_string()],],
tr![th!["Date"], td![&msg.date_relative],],
tr![th!["Tags"], td![format!("{:?}", msg.tags)],],
],
]
} else {
div![h2!["No message"]]
}
*/
// `view` describes what to display.
fn view(model: &Model) -> Node<Msg> {
let content = if let Some(show_results) = &model.show_results {
div![show_results
.0
.iter()
.enumerate()
.map(|(thread_idx, thread)| div![
h2![format!("thread {}", thread_idx)],
thread
.0
.iter()
.enumerate()
.map(|(thread_node_idx, thread_node)| div![
h3![format!("thread node {}", thread_node_idx)],
view_message(thread_node)
])
])]
assert_eq!(show_results.0.len(), 1);
let thread = &show_results.0[0];
assert_eq!(thread.0.len(), 1);
let thread_node = &thread.0[0];
div![
h1![first_subject(&thread_node)],
a![
attrs! {At::Href=>api::original(&thread_node.0.as_ref().expect("message missing").id)},
"Original"
],
view_message(&thread_node),
/*
show_results
.0
.iter()
.enumerate()
.map(|(thread_idx, thread)| div![
h2![format!("thread {}", thread_idx)],
thread
.0
.iter()
.enumerate()
.map(|(thread_node_idx, thread_node)| div![
h3![format!("thread node {}", thread_node_idx)],
view_message(thread_node)
])
]),
*/
pre![format!("Thread: {:#?}", show_results).replace(" ", " ")]
]
} else if let Some(search_results) = &model.search_results {
let rows = search_results.0.iter().map(|r| {
let tid = r.thread.clone();
@ -198,6 +275,10 @@ fn view(model: &Model) -> Node<Msg> {
"Unread",
ev(Ev::Click, |_| Msg::SearchRequest("is:unread".to_string())),
],
button![
"All",
ev(Ev::Click, |_| Msg::SearchRequest("*".to_string())),
],
input![
attrs! {
At::Placeholder => "Search";