From fc87fd702c164169b62b9cf45a3ba6fd30804bb3 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Thu, 22 Feb 2024 21:19:09 -0800 Subject: [PATCH] web: refacter header rendering code, add more detail when message open --- web/index.html | 5 + web/src/view/mod.rs | 283 ++++++++++++++++++++++++++++++-------------- 2 files changed, 200 insertions(+), 88 deletions(-) diff --git a/web/index.html b/web/index.html index 4935edb..4fe2fb4 100644 --- a/web/index.html +++ b/web/index.html @@ -22,6 +22,11 @@ width: 100%; } + .message .header table td { + border: 0; + padding: 0; + } + .message .headers { position: relative; width: 100%; diff --git a/web/src/view/mod.rs b/web/src/view/mod.rs index 4c1bb12..760bdd0 100644 --- a/web/src/view/mod.rs +++ b/web/src/view/mod.rs @@ -324,21 +324,6 @@ implement_email!( ShowThreadQueryThreadMessagesFrom ); -fn view_address(email: impl Email) -> Node { - span![ - C!["tag", "is-black"], - email.addr().as_ref().map(|a| attrs! {At::Title=>a}), - email - .name() - .as_ref() - .unwrap_or(&email.addr().unwrap_or("(UNKNOWN)")) - ] -} - -fn view_addresses(addrs: &[impl Email]) -> Vec> { - addrs.into_iter().map(view_address).collect::>() -} - fn raw_text_message(contents: &str) -> Node { let (contents, truncated_msg) = if contents.len() > MAX_RAW_MESSAGE_SIZE { ( @@ -355,21 +340,7 @@ fn has_unread(tags: &[String]) -> bool { tags.contains(&String::from("unread")) } -fn message_render(msg: &ShowThreadQueryThreadMessages, open: bool) -> Node { - let id = msg.id.clone(); - let expand_id = msg.id.clone(); - let is_unread = has_unread(&msg.tags); - let avatar: Option = None; - //let avatar: Option = Some(String::from("https://bulma.io/images/placeholders/64x64.png")); - let from: String = match &msg.from { - Some(ShowThreadQueryThreadMessagesFrom { - name: Some(name), .. - }) => name.to_string(), - Some(ShowThreadQueryThreadMessagesFrom { - addr: Some(addr), .. - }) => addr.to_string(), - _ => String::from("UNKNOWN"), - }; +fn render_avatar(avatar: Option, from: &str) -> Node { let initials: String = from .to_lowercase() .split(" ") @@ -377,12 +348,12 @@ fn message_render(msg: &ShowThreadQueryThreadMessages, open: bool) -> Node // Limit to 2 characters because more characters don't fit in the box .take(2) .collect(); - let img = if let Some(src) = avatar { + if let Some(src) = avatar { img![attrs! {At::Src=>src}] } else { let w = 64; let h = 64; - let from_color = compute_color(&from); + let from_color = compute_color(from); svg![ attrs! {At::ViewBox=>format!("0 0 {w} {h}") }, style! { @@ -416,64 +387,200 @@ fn message_render(msg: &ShowThreadQueryThreadMessages, open: bool) -> Node ] ] ] + } +} + +fn render_open_header(msg: &ShowThreadQueryThreadMessages) -> Node { + let (from, from_detail) = match &msg.from { + Some(ShowThreadQueryThreadMessagesFrom { + name: Some(name), + addr, + }) => (name.to_string(), addr.clone()), + Some(ShowThreadQueryThreadMessagesFrom { + addr: Some(addr), .. + }) => (addr.to_string(), None), + _ => (String::from("UNKNOWN"), None), }; + // TODO(wathiede): get this from server + let avatar: Option = None; + //let avatar: Option = Some(String::from("https://bulma.io/images/placeholders/64x64.png")); + let id = msg.id.clone(); + let is_unread = has_unread(&msg.tags); + let img = render_avatar(avatar, &from); + article![ + C!["media"], + figure![C!["media-left"], p![C!["image", "is-64x64"], img]], + div![ + C!["media-content"], + div![ + C!["content"], + p![ + strong![from], + br![], + small![from_detail], + table![ + IF!(!msg.to.is_empty() => + msg.to.iter().enumerate().map(|(i, to)| + tr![ + td![ if i==0 { "To" }else { "" } ], + td![ + small![ + match to { + ShowThreadQueryThreadMessagesTo { + name: Some(name), + addr:Some(addr), + } => format!("{name} <{addr}>"), + ShowThreadQueryThreadMessagesTo { + name: Some(name), + addr:None + } => format!("{name}"), + ShowThreadQueryThreadMessagesTo { + addr: Some(addr), .. + } => format!("<{addr}>"), + _ => String::from("UNKNOWN"), + } + + ] + ]])), + IF!(!msg.cc.is_empty() => + msg.cc.iter().enumerate().map(|(i, cc)| + tr![ + td![ if i==0 { "CC" }else { "" } ], + td![ + small![ + match cc { + ShowThreadQueryThreadMessagesCc { + name: Some(name), + addr:Some(addr), + } => format!("{name} <{addr}>"), + ShowThreadQueryThreadMessagesCc { + name: Some(name), + addr:None + } => format!("{name}"), + ShowThreadQueryThreadMessagesCc { + addr: Some(addr), .. + } => format!("<{addr}>"), + _ => String::from("UNKNOWN"), + } + + ] + ]])), + tr![ + td!["Date"], + td![msg.timestamp.map(|ts| span![C!["header"], human_age(ts)])] + ] + ], + ], + ], + ], + div![ + C!["media-right"], + span![ + C!["read-status"], + i![ + C![ + "far", + if is_unread { + "fa-envelope" + } else { + "fa-envelope-open" + }, + ], + ev(Ev::Click, move |e| { + e.stop_propagation(); + Msg::SetUnread(format!("id:{id}"), !is_unread) + }) + ] + ] + ] + ] +} + +fn render_closed_header(msg: &ShowThreadQueryThreadMessages) -> Node { + let from: String = match &msg.from { + Some(ShowThreadQueryThreadMessagesFrom { + name: Some(name), .. + }) => name.to_string(), + Some(ShowThreadQueryThreadMessagesFrom { + addr: Some(addr), .. + }) => addr.to_string(), + _ => String::from("UNKNOWN"), + }; + // TODO(wathiede): get this from server + let avatar: Option = None; + //let avatar: Option = Some(String::from("https://bulma.io/images/placeholders/64x64.png")); + let id = msg.id.clone(); + let is_unread = has_unread(&msg.tags); + let img = render_avatar(avatar, &from); + article![ + C!["media"], + figure![C!["media-left"], p![C!["image", "is-64x64"], img]], + div![ + C!["media-content"], + div![ + C!["content"], + p![ + strong![from], + br![], + IF!(!msg.to.is_empty() => + nodes![ + small![" to "], + msg.to.iter().enumerate().map(|(i, to)| small![ + if i > 0 { ", " } else { "" }, + to.name() + .as_ref() + .unwrap_or(&to.addr().unwrap_or("(UNKNOWN)")) + ]).collect::>() + ]), + IF!(!msg.cc.is_empty() => + nodes![ + small![" cc "], + msg.cc.iter().enumerate().map(|(i, cc)| small![ + if i > 0 { ", " } else { "" }, + cc.name() + .as_ref() + .unwrap_or(&cc.addr().unwrap_or("(UNKNOWN)")) + ]).collect::>() + ]), + br![], + msg.timestamp.map(|ts| span![C!["header"], human_age(ts)]), + ], + ], + ], + div![ + C!["media-right"], + span![ + C!["read-status"], + i![ + C![ + "far", + if is_unread { + "fa-envelope" + } else { + "fa-envelope-open" + }, + ], + ev(Ev::Click, move |e| { + e.stop_propagation(); + Msg::SetUnread(format!("id:{id}"), !is_unread) + }) + ] + ] + ] + ] +} + +fn message_render(msg: &ShowThreadQueryThreadMessages, open: bool) -> Node { + let expand_id = msg.id.clone(); div![ C!["message"], - article![ - C!["media"], - figure![C!["media-left"], p![C!["image", "is-64x64"], img],], - div![ - C!["media-content"], - div![ - C!["content"], - p![ - strong![from], - br![], - IF!(!msg.to.is_empty() => - nodes![ - small![" to "], - msg.to.iter().enumerate().map(|(i, to)| small![ - if i > 0 { ", " } else { "" }, - to.name() - .as_ref() - .unwrap_or(&to.addr().unwrap_or("(UNKNOWN)")) - ]).collect::>() - ]), - IF!(!msg.cc.is_empty() => - nodes![ - small![" cc "], - msg.cc.iter().enumerate().map(|(i, cc)| small![ - if i > 0 { ", " } else { "" }, - cc.name() - .as_ref() - .unwrap_or(&cc.addr().unwrap_or("(UNKNOWN)")) - ]).collect::>() - ]), - br![], - msg.timestamp.map(|ts| span![C!["header"], human_age(ts)]), - ], - ], - ], - div![ - C!["media-right"], - span![ - C!["read-status"], - i![ - C![ - "far", - if is_unread { - "fa-envelope" - } else { - "fa-envelope-open" - }, - ], - ev(Ev::Click, move |e| { - e.stop_propagation(); - Msg::SetUnread(format!("id:{id}"), !is_unread) - }), - ], - ], - ], + div![ + C!["header"], + if open { + render_open_header(&msg) + } else { + render_closed_header(&msg) + }, ev(Ev::Click, move |e| { e.stop_propagation(); if open { @@ -481,7 +588,7 @@ fn message_render(msg: &ShowThreadQueryThreadMessages, open: bool) -> Node } else { Msg::MessageExpand(expand_id) } - }), + }) ], IF!(open => div![