Compare commits
4 Commits
42ce88d931
...
fc87fd702c
| Author | SHA1 | Date | |
|---|---|---|---|
| fc87fd702c | |||
| 42484043a1 | |||
| 3f268415e9 | |||
| c2a5fe19e3 |
@ -11,13 +11,22 @@
|
|||||||
<link rel="icon" href="https://static.xinu.tv/favicon/letterbox.svg" />
|
<link rel="icon" href="https://static.xinu.tv/favicon/letterbox.svg" />
|
||||||
<!-- Pretty checkboxes from https://justboil.github.io/bulma-checkbox/ -->
|
<!-- Pretty checkboxes from https://justboil.github.io/bulma-checkbox/ -->
|
||||||
<link data-trunk rel="css" href="static/main.css" />
|
<link data-trunk rel="css" href="static/main.css" />
|
||||||
|
<!-- tall thin font for user icon -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@700&display=swap" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
.message {
|
<style>.message {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message .header table td {
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.message .headers {
|
.message .headers {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -38,11 +47,11 @@
|
|||||||
.body {
|
.body {
|
||||||
background: white;
|
background: white;
|
||||||
color: black;
|
color: black;
|
||||||
margin-left: -0.5em;
|
|
||||||
margin-right: -0.5em;
|
|
||||||
margin-top: 0.5em;
|
margin-top: 0.5em;
|
||||||
|
padding: 1em;
|
||||||
width: 0;
|
width: 0;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
|
overflow-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
@ -146,11 +155,19 @@
|
|||||||
color: #555;
|
color: #555;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile .search-results,
|
.mobile .search-results {
|
||||||
.mobile .thread {
|
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mobile .thread h3 {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
padding: 1em 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile .thread .tags {
|
||||||
|
padding: 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.search-results .row {
|
.search-results .row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@ -150,6 +150,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Msg::SetUnread(query, unread) => {
|
Msg::SetUnread(query, unread) => {
|
||||||
|
let search_url = urls::search(&model.query, 0).to_string();
|
||||||
orders.skip().perform_cmd(async move {
|
orders.skip().perform_cmd(async move {
|
||||||
let res: Result<
|
let res: Result<
|
||||||
graphql_client::Response<graphql::mark_read_mutation::ResponseData>,
|
graphql_client::Response<graphql::mark_read_mutation::ResponseData>,
|
||||||
@ -164,7 +165,11 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
error!("Failed to set read for {query} to {unread}: {e}");
|
error!("Failed to set read for {query} to {unread}: {e}");
|
||||||
}
|
}
|
||||||
Msg::RefreshStart
|
seed::window()
|
||||||
|
.location()
|
||||||
|
.set_href(&search_url)
|
||||||
|
.expect("failed to change location");
|
||||||
|
Msg::Noop
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +180,6 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
first,
|
first,
|
||||||
last,
|
last,
|
||||||
} => {
|
} => {
|
||||||
info!("making FrontPageRequest: {query} after:{after:?} before:{before:?} first:{first:?} last:{last:?}");
|
|
||||||
model.query = query.clone();
|
model.query = query.clone();
|
||||||
orders.skip().perform_cmd(async move {
|
orders.skip().perform_cmd(async move {
|
||||||
Msg::FrontPageResult(
|
Msg::FrontPageResult(
|
||||||
@ -331,7 +335,6 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
} = &mut model.context
|
} = &mut model.context
|
||||||
{
|
{
|
||||||
selected_threads.insert(tid);
|
selected_threads.insert(tid);
|
||||||
info!("selected threads {selected_threads:?}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Msg::SelectionRemoveThread(tid) => {
|
Msg::SelectionRemoveThread(tid) => {
|
||||||
@ -340,19 +343,16 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
} = &mut model.context
|
} = &mut model.context
|
||||||
{
|
{
|
||||||
selected_threads.remove(&tid);
|
selected_threads.remove(&tid);
|
||||||
info!("selected threads {selected_threads:?}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Msg::MessageCollapse(id) => {
|
Msg::MessageCollapse(id) => {
|
||||||
if let Context::ThreadResult { open_messages, .. } = &mut model.context {
|
if let Context::ThreadResult { open_messages, .. } = &mut model.context {
|
||||||
open_messages.remove(&id);
|
open_messages.remove(&id);
|
||||||
info!("open_messages threads {open_messages:?}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Msg::MessageExpand(id) => {
|
Msg::MessageExpand(id) => {
|
||||||
if let Context::ThreadResult { open_messages, .. } = &mut model.context {
|
if let Context::ThreadResult { open_messages, .. } = &mut model.context {
|
||||||
open_messages.insert(id);
|
open_messages.insert(id);
|
||||||
info!("open_messages threads {open_messages:?}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use std::{
|
|||||||
|
|
||||||
use chrono::{DateTime, Datelike, Duration, Local, Utc};
|
use chrono::{DateTime, Datelike, Duration, Local, Utc};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use log::error;
|
use log::{error, info};
|
||||||
use seed::{prelude::*, *};
|
use seed::{prelude::*, *};
|
||||||
use seed_hooks::{state_access::CloneState, topo, use_state};
|
use seed_hooks::{state_access::CloneState, topo, use_state};
|
||||||
use wasm_timer::Instant;
|
use wasm_timer::Instant;
|
||||||
@ -26,11 +26,15 @@ fn set_title(title: &str) {
|
|||||||
seed::document().set_title(&format!("lb: {}", title));
|
seed::document().set_title(&format!("lb: {}", title));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compute_color(data: &str) -> String {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
data.hash(&mut hasher);
|
||||||
|
format!("#{:06x}", hasher.finish() % (1 << 24))
|
||||||
|
}
|
||||||
|
|
||||||
fn tags_chiclet(tags: &[String], is_mobile: bool) -> impl Iterator<Item = Node<Msg>> + '_ {
|
fn tags_chiclet(tags: &[String], is_mobile: bool) -> impl Iterator<Item = Node<Msg>> + '_ {
|
||||||
tags.iter().map(move |tag| {
|
tags.iter().map(move |tag| {
|
||||||
let mut hasher = DefaultHasher::new();
|
let hex = compute_color(tag);
|
||||||
tag.hash(&mut hasher);
|
|
||||||
let hex = format!("#{:06x}", hasher.finish() % (1 << 24));
|
|
||||||
let style = style! {St::BackgroundColor=>hex};
|
let style = style! {St::BackgroundColor=>hex};
|
||||||
let classes = C!["tag", IF!(is_mobile => "is-small")];
|
let classes = C!["tag", IF!(is_mobile => "is-small")];
|
||||||
let tag = tag.clone();
|
let tag = tag.clone();
|
||||||
@ -320,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 {
|
||||||
(
|
(
|
||||||
@ -351,66 +340,144 @@ fn has_unread(tags: &[String]) -> bool {
|
|||||||
tags.contains(&String::from("unread"))
|
tags.contains(&String::from("unread"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_message_render(msg: &ShowThreadQueryThreadMessages, open: bool) -> Node<Msg> {
|
fn render_avatar(avatar: Option<String>, from: &str) -> Node<Msg> {
|
||||||
let id = msg.id.clone();
|
let initials: String = from
|
||||||
let expand_id = msg.id.clone();
|
.to_lowercase()
|
||||||
let is_unread = has_unread(&msg.tags);
|
.split(" ")
|
||||||
div![
|
.map(|word| word.chars().next().unwrap())
|
||||||
C!["message"],
|
// Limit to 2 characters because more characters don't fit in the box
|
||||||
div![
|
.take(2)
|
||||||
C!["headers"],
|
.collect();
|
||||||
span![
|
if let Some(src) = avatar {
|
||||||
C!["read-status"],
|
img![attrs! {At::Src=>src}]
|
||||||
i![
|
} else {
|
||||||
|
let w = 64;
|
||||||
|
let h = 64;
|
||||||
|
let from_color = compute_color(from);
|
||||||
|
svg![
|
||||||
|
attrs! {At::ViewBox=>format!("0 0 {w} {h}") },
|
||||||
style! {
|
style! {
|
||||||
St::Color => "gold"
|
St::Display => "block",
|
||||||
|
St::FontFamily => "Poppins",
|
||||||
|
St::FontSize => pt(32),
|
||||||
},
|
},
|
||||||
C![
|
g![
|
||||||
"far",
|
rect![attrs! {
|
||||||
if is_unread {
|
At::Fill=>from_color,
|
||||||
"fa-envelope"
|
At::Stroke=>"black",
|
||||||
} else {
|
At::StrokeWidth=>"1",
|
||||||
"fa-envelope-open"
|
|
||||||
|
// Round corners
|
||||||
|
//At::Rx => px(10),
|
||||||
|
At::X => 0,
|
||||||
|
At::Y => 0,
|
||||||
|
At::Width => h,
|
||||||
|
At::Height => h,
|
||||||
|
}],
|
||||||
|
text![
|
||||||
|
attrs! {
|
||||||
|
At::Fill => "white",
|
||||||
|
|
||||||
|
At::X => percent(50),
|
||||||
|
At::Y => percent(50),
|
||||||
|
At::DominantBaseline => "middle",
|
||||||
|
At::TextAnchor => "middle"
|
||||||
},
|
},
|
||||||
],
|
initials
|
||||||
ev(Ev::Click, move |e| {
|
]
|
||||||
e.stop_propagation();
|
]
|
||||||
Msg::SetUnread(format!("id:{id}"), !is_unread)
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
" ",
|
|
||||||
msg.from
|
|
||||||
.as_ref()
|
|
||||||
.map(|from| span![C!["header"], view_address(&from)]),
|
|
||||||
" ",
|
|
||||||
msg.timestamp.map(|ts| span![C!["header"], human_age(ts)]),
|
|
||||||
// TODO(wathiede): add first line of message body
|
|
||||||
],
|
|
||||||
ev(Ev::Click, move |e| {
|
|
||||||
e.stop_propagation();
|
|
||||||
if open {
|
|
||||||
Msg::MessageCollapse(expand_id)
|
|
||||||
} else {
|
|
||||||
Msg::MessageExpand(expand_id)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
fn unread_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 id = msg.id.clone();
|
||||||
let expand_id = msg.id.clone();
|
|
||||||
let is_unread = has_unread(&msg.tags);
|
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![
|
div![
|
||||||
C!["message"],
|
C!["media-content"],
|
||||||
div![
|
div![
|
||||||
C!["headers"],
|
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![
|
span![
|
||||||
C!["read-status"],
|
C!["read-status"],
|
||||||
i![
|
i![
|
||||||
style! {
|
|
||||||
St::Color => "gold"
|
|
||||||
},
|
|
||||||
C![
|
C![
|
||||||
"far",
|
"far",
|
||||||
if is_unread {
|
if is_unread {
|
||||||
@ -422,20 +489,98 @@ fn unread_message_render(msg: &ShowThreadQueryThreadMessages, open: bool) -> Nod
|
|||||||
ev(Ev::Click, move |e| {
|
ev(Ev::Click, move |e| {
|
||||||
e.stop_propagation();
|
e.stop_propagation();
|
||||||
Msg::SetUnread(format!("id:{id}"), !is_unread)
|
Msg::SetUnread(format!("id:{id}"), !is_unread)
|
||||||
}),
|
})
|
||||||
],
|
]
|
||||||
],
|
]
|
||||||
msg.from
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
.as_ref()
|
||||||
.map(|from| div![C!["header"], "From: ", view_address(&from)]),
|
.unwrap_or(&to.addr().unwrap_or("(UNKNOWN)"))
|
||||||
msg.timestamp
|
]).collect::<Vec<_>>()
|
||||||
.map(|ts| div![C!["header"], "Date: ", human_age(ts)]),
|
]),
|
||||||
div![C!["header"], "Message-ID: ", &msg.id],
|
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![
|
||||||
|
C!["message"],
|
||||||
div![
|
div![
|
||||||
C!["header"],
|
C!["header"],
|
||||||
IF!(!msg.to.is_empty() => span!["To: ", view_addresses(&msg.to)]),
|
if open {
|
||||||
IF!(!msg.cc.is_empty() => span!["CC: ", view_addresses(&msg.cc)])
|
render_open_header(&msg)
|
||||||
],
|
} else {
|
||||||
|
render_closed_header(&msg)
|
||||||
|
},
|
||||||
ev(Ev::Click, move |e| {
|
ev(Ev::Click, move |e| {
|
||||||
e.stop_propagation();
|
e.stop_propagation();
|
||||||
if open {
|
if open {
|
||||||
@ -443,8 +588,9 @@ fn unread_message_render(msg: &ShowThreadQueryThreadMessages, open: bool) -> Nod
|
|||||||
} else {
|
} else {
|
||||||
Msg::MessageExpand(expand_id)
|
Msg::MessageExpand(expand_id)
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
],
|
],
|
||||||
|
IF!(open =>
|
||||||
div![
|
div![
|
||||||
C!["body"],
|
C!["body"],
|
||||||
match &msg.body {
|
match &msg.body {
|
||||||
@ -480,7 +626,7 @@ fn unread_message_render(msg: &ShowThreadQueryThreadMessages, open: bool) -> Nod
|
|||||||
view_content_tree(&content_tree),
|
view_content_tree(&content_tree),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
],
|
])
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,18 +646,14 @@ fn thread(thread: &ShowThreadQueryThread, open_messages: &HashSet<String>) -> No
|
|||||||
tags.sort();
|
tags.sort();
|
||||||
let messages = thread.messages.iter().map(|msg| {
|
let messages = thread.messages.iter().map(|msg| {
|
||||||
let open = open_messages.contains(&msg.id);
|
let open = open_messages.contains(&msg.id);
|
||||||
if open {
|
message_render(&msg, open)
|
||||||
unread_message_render(&msg, open)
|
|
||||||
} else {
|
|
||||||
read_message_render(&msg, open)
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
let read_thread_id = thread.thread_id.clone();
|
let read_thread_id = thread.thread_id.clone();
|
||||||
let unread_thread_id = thread.thread_id.clone();
|
let unread_thread_id = thread.thread_id.clone();
|
||||||
div![
|
div![
|
||||||
C!["thread"],
|
C!["thread"],
|
||||||
h3![C!["is-size-5"], &thread.subject,],
|
h3![C!["is-size-5"], &thread.subject],
|
||||||
tags_chiclet(&tags, false),
|
span![C!["tags"], tags_chiclet(&tags, false)],
|
||||||
span![
|
span![
|
||||||
C!["level-item", "buttons", "has-addons"],
|
C!["level-item", "buttons", "has-addons"],
|
||||||
button![
|
button![
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user