diff --git a/server/src/nm.rs b/server/src/nm.rs
index ed7ce26..f690fd2 100644
--- a/server/src/nm.rs
+++ b/server/src/nm.rs
@@ -223,7 +223,7 @@ pub async fn thread(
}
format!(
- r#"
{}
"#,
+ r#"{}
"#,
// Trim newlines to prevent excessive white space at the beginning/end of
// presenation. Leave tabs and spaces incase plain text attempts to center a
// header on the first line.
@@ -578,7 +578,7 @@ fn flatten_body_parts(parts: &[Body]) -> Body {
.map(|p| match p {
Body::PlainText(PlainText { text, .. }) => {
format!(
- r#"{}
"#,
+ r#"{}
"#,
// Trim newlines to prevent excessive white space at the beginning/end of
// presenation. Leave tabs and spaces incase plain text attempts to center a
// header on the first line.
diff --git a/web/src/view/mod.rs b/web/src/view/mod.rs
index 73e0708..d2d9f04 100644
--- a/web/src/view/mod.rs
+++ b/web/src/view/mod.rs
@@ -30,7 +30,7 @@ mod tw_classes {
];
pub const TAG_X: &[&str] = &[
"rounded-r",
- "bg-slate-800",
+ "bg-neutral-800",
"px-2",
"py-1",
"mr-1",
@@ -38,17 +38,17 @@ mod tw_classes {
"[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]",
];
pub const BUTTON: &[&str] = &[
- "bg-slate-900",
+ "bg-neutral-900",
"rounded-md",
"p-2",
"border",
- "border-slate-700",
+ "border-neutral-700",
"text-center",
"text-sm",
"transition-all",
"shadow-md",
"hover:shadow-lg",
- "hover:bg-slate-700",
+ "hover:bg-neutral-700",
"disabled:pointer-events-none",
"disabled:opacity-50",
"disabled:shadow-none",
@@ -62,7 +62,7 @@ mod tw_classes {
"checked:appearance-auto",
"rounded",
"border",
- "border-gray-500",
+ "border-neutral-500",
];
}
@@ -133,7 +133,7 @@ fn search_results(
"flex-auto",
"py-4",
"border-b",
- "border-gray-800"
+ "border-neutral-800"
],
div![
C!["flex", "items-center", "mr-4"],
@@ -432,14 +432,12 @@ fn search_toolbar(
pager: &FrontPageQuerySearchPageInfo,
show_bulk_edit: bool,
) -> Node {
- info!("pager {pager:#?}");
nav![
C!["p-4", "flex", "w-full", "justify-between"],
div![
C!["gap-2", "flex"],
IF!(show_bulk_edit =>
div![
- div![
button![
C![&tw_classes::BUTTON, "rounded-r-none"],
attrs!{At::Title => "Mark as read"},
@@ -454,11 +452,9 @@ fn search_toolbar(
span![C!["pl-2", "hidden", "md:inline"], "Unread"],
ev(Ev::Click, |_| Msg::SelectionMarkAsUnread)
]
- ]
]),
IF!(show_bulk_edit =>
div![
- div![
button![
C![&tw_classes::BUTTON, "text-red-500"],
attrs!{At::Title => "Mark as spam"},
@@ -470,8 +466,7 @@ fn search_toolbar(
Msg::SelectionMarkAsRead
])
)
- ],
- ],
+ ]
]),
],
div![
@@ -503,7 +498,7 @@ fn raw_text_message(contents: &str) -> Node {
(contents, None)
};
div![
- C!["NOTPORTED", "view-part-text-plain"],
+ C!["view-part-text-plain", "font-mono", "whitespace-pre"],
contents,
truncated_msg,
]
@@ -582,7 +577,7 @@ fn render_open_header(msg: &ShowThreadQueryThreadOnEmailThreadMessages) -> Node<
let avatar = render_avatar(photo_url, &from, true);
let unknown = "UNKNOWN".to_string();
div![
- C!["flex", "p-4"],
+ C!["flex", "p-4", "bg-neutral-800"],
div![avatar],
div![
C!["px-4", "mr-auto"],
@@ -679,7 +674,7 @@ fn render_closed_header(msg: &ShowThreadQueryThreadOnEmailThreadMessages) -> Nod
let avatar = render_avatar(photo_url, &from, false);
let unknown = "UNKNOWN".to_string();
div![
- C!["flex", "p-4"],
+ C!["flex", "p-4", "bg-neutral-800"],
div![avatar],
div![
C!["px-4", "mr-auto"],
@@ -754,6 +749,7 @@ fn render_closed_header(msg: &ShowThreadQueryThreadOnEmailThreadMessages) -> Nod
fn message_render(msg: &ShowThreadQueryThreadOnEmailThreadMessages, open: bool) -> Node {
let expand_id = msg.id.clone();
div![
+ C!["lg:mb-4"],
div![
if open {
render_open_header(&msg)
@@ -771,13 +767,13 @@ fn message_render(msg: &ShowThreadQueryThreadOnEmailThreadMessages, open: bool)
],
IF!(open =>
div![
- C![],
+ C!["bg-white", "text-black", "p-4", "min-w-full", "w-0","overflow-x-auto"],
match &msg.body {
ShowThreadQueryThreadOnEmailThreadMessagesBody::UnhandledContentType(
ShowThreadQueryThreadOnEmailThreadMessagesBodyOnUnhandledContentType { contents ,content_tree},
) => div![
raw_text_message(&contents),
- div![C!["NOTPORTED","error"],
+ div![C!["bg-red-500","p-4", "min-w-full", "w-0", "overflow-x-auto"],
view_content_tree(&content_tree),
]
],
@@ -796,59 +792,9 @@ fn message_render(msg: &ShowThreadQueryThreadOnEmailThreadMessages, open: bool)
content_tree,
},
) => div![
- C!["NOTPORTED","view-part-text-html"],
+ C!["view-part-text-html"],
raw![contents],
- IF!(!msg.attachments.is_empty() =>
- div![
- C!["NOTPORTED","attachments"],
- hr![],
- h2!["NOTPORTED","Attachments"],
- div![C!["NOTPORTED","grid","is-col-min-6"],
- msg.attachments
- .iter()
- .map(|a| {
- let default = "UNKNOWN_FILE".to_string();
- let filename = a.filename.as_ref().unwrap_or(&default);
- let host = seed::window().location().host().expect("couldn't get host");
- let url = shared::urls::download_attachment(Some(&host), &a.id, &a.idx, filename);
- let mut fmtr = Formatter::new();
- fmtr.with_separator(" ");
- fmtr.with_scales(Scales::Binary());
-
- div![
- C!["NOTPORTED","attachment", "card"],
- a.content_type.as_ref().map(|content_type|
- IF!(content_type.starts_with("image/") =>
- div![C!["NOTPORTED","card-image","is-1by1"],
- div![
- C!["NOTPORTED","image","is-1by1"],
- style!{
- St::BackgroundImage=>format!(r#"url("{url}");"#),
- St::BackgroundSize=>"cover",
- St::BackgroundPosition=>"center",
- }
- ]
- ]
- )),
- div![C!["NOTPORTED","card-content"],
- div![C!["NOTPORTED","content"],
- &a.filename, br![],
- small![ fmtr.format(a.size as f64),"B"]
- ]
- ],
- footer![
- C!["NOTPORTED","card-footer"],
- a![C!["NOTPORTED","card-footer-item"],span![C!["NOTPORTED","icon"], i![C!["NOTPORTED","fas", "fa-download"]]],
- ev(Ev::Click, move |_| {
- seed::window().location().set_href(&url
- ).expect("failed to set URL");
- })
- ]
- ]
- ]
- })
- ]
- ]),
+ IF!(!msg.attachments.is_empty() => render_attachements(&msg.attachments)),
view_content_tree(&content_tree),
],
}
@@ -856,14 +802,76 @@ fn message_render(msg: &ShowThreadQueryThreadOnEmailThreadMessages, open: bool)
]
}
+fn render_attachements(
+ attachments: &[ShowThreadQueryThreadOnEmailThreadMessagesAttachments],
+) -> Node {
+ div![
+ C!["border-t", "border-neutral-200", "mt-2", "pt-2"],
+ h2![C!["text-lg"], "Attachments"],
+ div![
+ C!["flex", "flex-wrap", "gap-2"],
+ attachments.iter().map(|a| {
+ let default = "UNKNOWN_FILE".to_string();
+ let filename = a.filename.as_ref().unwrap_or(&default);
+ let host = seed::window().location().host().expect("couldn't get host");
+ let url = shared::urls::download_attachment(Some(&host), &a.id, &a.idx, filename);
+ let mut fmtr = Formatter::new();
+ fmtr.with_separator(" ");
+ fmtr.with_scales(Scales::Binary());
+
+ div![
+ C![
+ "flex",
+ "flex-col",
+ "flex-none",
+ "p-2",
+ "bg-neutral-200",
+ "border",
+ "border-neutral-400",
+ ],
+ a.content_type.as_ref().map(
+ |content_type| IF!(content_type.starts_with("image/") => img![
+ C!["w-32", "h-32", "md:w-64", "md:h-64", "object-cover"],
+ attrs!{At::Src=>url},
+ ])
+ ),
+ div![
+ C!["py-2", "flex", "flex-nowrap", "items-center"],
+ div![
+ C!["flex", "flex-col", "grow", "w-16", "md:w-32"],
+ span![C!["shrink", "truncate"], &a.filename],
+ span![C!["text-xs"], fmtr.format(a.size as f64), "B"]
+ ],
+ a![
+ C![
+ "aspect-square",
+ "px-2",
+ "pt-1",
+ "bg-neutral-300",
+ "border",
+ "border-neutral-400"
+ ],
+ span![i![C!["fas", "fa-download"]]],
+ ev(Ev::Click, move |_| {
+ seed::window()
+ .location()
+ .set_href(&url)
+ .expect("failed to set URL");
+ })
+ ]
+ ]
+ ]
+ })
+ ]
+ ]
+}
+
#[topo::nested]
fn thread(
thread: &ShowThreadQueryThreadOnEmailThread,
open_messages: &HashSet,
content_el: &ElRef,
) -> Node {
- // TODO remove and replace with CSS styling
- let show_icon_text = true;
// TODO(wathiede): show per-message subject if it changes significantly from top-level subject
let subject = if thread.subject.is_empty() {
"(no subject)"
@@ -890,68 +898,49 @@ fn thread(
let spam_add_thread_id = thread.thread_id.clone();
let spam_unread_thread_id = thread.thread_id.clone();
div![
- C!["p-4"],
- h3![C!["text-xl"], subject],
- span![
- C!["NOTPORTED", "tags"],
- removable_tags_chiclet(&thread.thread_id, &tags, false)
- ],
+ C!["lg:p-4"],
div![
- C!["NOTPORTED", "level", "is-mobile"],
+ C!["p-4", "lg:p-0"],
+ h3![C!["text-xl"], subject],
+ span![removable_tags_chiclet(&thread.thread_id, &tags, false)],
div![
- C!["NOTPORTED", "level-item"],
+ C!["pt-4", "gap-2", "flex", "justify-around"],
div![
- C!["NOTPORTED", "buttons", "has-addons"],
button![
- C!["NOTPORTED", "button", "mark-read"],
+ C![&tw_classes::BUTTON, "rounded-r-none"],
attrs! {At::Title => "Mark as read"},
- span![
- C!["NOTPORTED", "icon", "is-small"],
- i![C!["NOTPORTED", "far", "fa-envelope-open"]]
- ],
- IF!(show_icon_text=>span!["Read"]),
+ span![i![C!["far", "fa-envelope-open"]]],
+ span![C!["pl-2", "hidden", "md:inline"], "Read"],
ev(Ev::Click, move |_| Msg::MultiMsg(vec![
Msg::SetUnread(read_thread_id, false),
Msg::GoToSearchResults
])),
],
button![
- C!["NOTPORTED", "button", "mark-unread"],
+ C![&tw_classes::BUTTON, "rounded-l-none"],
attrs! {At::Title => "Mark as unread"},
- span![
- C!["NOTPORTED", "icon", "is-small"],
- i![C!["NOTPORTED", "far", "fa-envelope"]]
- ],
- IF!(show_icon_text=>span!["Unread"]),
+ span![i![C!["far", "fa-envelope"]]],
+ span![C!["pl-2", "hidden", "md:inline"], "Unread"],
ev(Ev::Click, move |_| Msg::MultiMsg(vec![
Msg::SetUnread(unread_thread_id, true),
Msg::GoToSearchResults
])),
],
],
- ],
- div![
- C!["NOTPORTED", "level-item"],
- div![
- C!["NOTPORTED", "buttons", "has-addons"],
- button![
- C!["NOTPORTED", "button", "spam"],
- attrs! {At::Title => "Spam"},
- span![
- C!["NOTPORTED", "icon", "is-small"],
- i![C!["NOTPORTED", "far", "fa-hand"]]
- ],
- IF!(show_icon_text=>span!["Spam"]),
- ev(Ev::Click, move |_| Msg::MultiMsg(vec![
- Msg::AddTag(spam_add_thread_id, "Spam".to_string()),
- Msg::SetUnread(spam_unread_thread_id, false),
- Msg::GoToSearchResults
- ])),
- ],
- ],
+ div![button![
+ C![&tw_classes::BUTTON, "text-red-500"],
+ attrs! {At::Title => "Spam"},
+ span![i![C!["far", "fa-hand"]]],
+ span![C!["pl-2", "hidden", "md:inline"], "Spam"],
+ ev(Ev::Click, move |_| Msg::MultiMsg(vec![
+ Msg::AddTag(spam_add_thread_id, "Spam".to_string()),
+ Msg::SetUnread(spam_unread_thread_id, false),
+ Msg::GoToSearchResults
+ ])),
+ ]]
],
],
- div![el_ref(content_el), messages, click_to_top()],
+ div![C!["lg:mt-4"], el_ref(content_el), messages, click_to_top()],
/* TODO(wathiede): plumb in orignal id
a![
attrs! {At::Href=>api::original(&thread_node.0.as_ref().expect("message missing").id)},
@@ -998,7 +987,7 @@ fn view_header(
};
let query = Url::decode_uri_component(query).unwrap_or("".to_string());
nav![
- C!["flex", "p-4"],
+ C!["flex", "px-4", "pt-4"],
a![
C![IF![is_error => "bg-red-500"], "rounded-r-none"],
C![&tw_classes::BUTTON],
@@ -1081,7 +1070,7 @@ pub fn tags(model: &Model) -> Node {
"self-center",
"justify-self-end",
"text-sm",
- "text-gray-400"
+ "text-neutral-400"
],
IF!(t.unread>0 => format!("{}", t.unread))
],
@@ -1305,7 +1294,7 @@ fn render_news_post_header(post: &ShowThreadQueryThreadOnNewsPost) -> Node
div![
C!["px-4", "mr-auto"],
div![
- span![C!["font-semibold", "text-sm"], from,],
+ span![C!["font-semibold", "text-sm"], from],
div![
C!["text-xs"],
small![a![
@@ -1341,15 +1330,23 @@ fn render_news_post_header(post: &ShowThreadQueryThreadOnNewsPost) -> Node
}
fn reading_progress(ratio: f64) -> Node {
let percent = ratio * 100.;
- // TODO: percent broken with no styles
info!("percent {percent}");
- progress![
- C!["absolute", "w-screen", IF!(percent<1. => "hidden")],
- attrs! {
- At::Value=>percent,
- At::Max=>"100"
- },
- format!("{percent}%")
+ div![
+ C![
+ "fixed",
+ "top-0",
+ "left-0",
+ "w-screen",
+ "h-1",
+ "bg-gray-200",
+ IF!(percent<1. => "hidden")
+ ],
+ div![
+ C!["h-1", "bg-green-500"],
+ style! {
+ St::Width => format!("{}%", percent)
+ }
+ ]
]
}
pub fn versions(versions: &crate::state::Version) -> Node {
@@ -1372,12 +1369,9 @@ pub fn versions(versions: &crate::state::Version) -> Node {
fn click_to_top() -> Node {
button![
- C!["NOTPORTED", "button", "is-danger", "is-small"],
+ C![&tw_classes::BUTTON, "bg-red-500", "lg:m-0", "m-4"],
span!["Top"],
- span![
- C!["NOTPORTED", "icon"],
- i![C!["NOTPORTED", "fas", "fa-arrow-turn-up"]]
- ],
+ span![i![C!["fas", "fa-arrow-turn-up"]]],
ev(Ev::Click, |_| web_sys::window()
.unwrap()
.scroll_to_with_x_and_y(0., 0.))