web: refacter header rendering code, add more detail when message open

This commit is contained in:
Bill Thiede 2024-02-22 21:19:09 -08:00
parent 42484043a1
commit fc87fd702c
2 changed files with 200 additions and 88 deletions

View File

@ -22,6 +22,11 @@
width: 100%; width: 100%;
} }
.message .header table td {
border: 0;
padding: 0;
}
.message .headers { .message .headers {
position: relative; position: relative;
width: 100%; width: 100%;

View File

@ -324,21 +324,6 @@ implement_email!(
ShowThreadQueryThreadMessagesFrom ShowThreadQueryThreadMessagesFrom
); );
fn view_address(email: impl Email) -> Node<Msg> {
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<Node<Msg>> {
addrs.into_iter().map(view_address).collect::<Vec<_>>()
}
fn raw_text_message(contents: &str) -> Node<Msg> { fn raw_text_message(contents: &str) -> Node<Msg> {
let (contents, truncated_msg) = if contents.len() > MAX_RAW_MESSAGE_SIZE { 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")) tags.contains(&String::from("unread"))
} }
fn message_render(msg: &ShowThreadQueryThreadMessages, open: bool) -> Node<Msg> { fn render_avatar(avatar: Option<String>, from: &str) -> Node<Msg> {
let id = msg.id.clone();
let expand_id = msg.id.clone();
let is_unread = has_unread(&msg.tags);
let avatar: Option<String> = None;
//let avatar: Option<String> = 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"),
};
let initials: String = from let initials: String = from
.to_lowercase() .to_lowercase()
.split(" ") .split(" ")
@ -377,12 +348,12 @@ fn message_render(msg: &ShowThreadQueryThreadMessages, open: bool) -> Node<Msg>
// Limit to 2 characters because more characters don't fit in the box // Limit to 2 characters because more characters don't fit in the box
.take(2) .take(2)
.collect(); .collect();
let img = if let Some(src) = avatar { if let Some(src) = avatar {
img![attrs! {At::Src=>src}] img![attrs! {At::Src=>src}]
} else { } else {
let w = 64; let w = 64;
let h = 64; let h = 64;
let from_color = compute_color(&from); let from_color = compute_color(from);
svg![ svg![
attrs! {At::ViewBox=>format!("0 0 {w} {h}") }, attrs! {At::ViewBox=>format!("0 0 {w} {h}") },
style! { style! {
@ -416,64 +387,200 @@ fn message_render(msg: &ShowThreadQueryThreadMessages, open: bool) -> Node<Msg>
] ]
] ]
] ]
}
}
fn render_open_header(msg: &ShowThreadQueryThreadMessages) -> Node<Msg> {
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<String> = None;
//let avatar: Option<String> = 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<Msg> {
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<String> = None;
//let avatar: Option<String> = 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::<Vec<_>>()
]),
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::<Vec<_>>()
]),
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<Msg> {
let expand_id = msg.id.clone();
div![ div![
C!["message"], C!["message"],
article![ div![
C!["media"], C!["header"],
figure![C!["media-left"], p![C!["image", "is-64x64"], img],], if open {
div![ render_open_header(&msg)
C!["media-content"], } else {
div![ render_closed_header(&msg)
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::<Vec<_>>()
]),
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::<Vec<_>>()
]),
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)
}),
],
],
],
ev(Ev::Click, move |e| { ev(Ev::Click, move |e| {
e.stop_propagation(); e.stop_propagation();
if open { if open {
@ -481,7 +588,7 @@ fn message_render(msg: &ShowThreadQueryThreadMessages, open: bool) -> Node<Msg>
} else { } else {
Msg::MessageExpand(expand_id) Msg::MessageExpand(expand_id)
} }
}), })
], ],
IF!(open => IF!(open =>
div![ div![