Compare commits

..

No commits in common. "c33f901f48712d5a3acc04f193a348d3171b2599" and "d8275debdc544cf3641abe3508a9c209068b7f2f" have entirely different histories.

5 changed files with 58 additions and 177 deletions

45
Cargo.lock generated
View File

@ -982,7 +982,6 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"wasm-bindgen-test", "wasm-bindgen-test",
"wasm-timer",
] ]
[[package]] [[package]]
@ -1243,17 +1242,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core 0.8.6",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.1" version = "0.12.1"
@ -1261,21 +1249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [ dependencies = [
"lock_api", "lock_api",
"parking_lot_core 0.9.4", "parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi 0.3.9",
] ]
[[package]] [[package]]
@ -1598,7 +1572,7 @@ dependencies = [
"memchr", "memchr",
"multer", "multer",
"num_cpus", "num_cpus",
"parking_lot 0.12.1", "parking_lot",
"pin-project-lite", "pin-project-lite",
"rand", "rand",
"ref-cast", "ref-cast",
@ -2469,21 +2443,6 @@ dependencies = [
"quote 1.0.21", "quote 1.0.21",
] ]
[[package]]
name = "wasm-timer"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f"
dependencies = [
"futures",
"js-sys",
"parking_lot 0.11.2",
"pin-utils",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.60" version = "0.3.60"

View File

@ -32,15 +32,6 @@ async fn search(
Ok(Json(res)) Ok(Json(res))
} }
#[get("/show/<query>/pretty")]
async fn show_pretty(
nm: &State<Notmuch>,
query: &str,
) -> Result<Json<ThreadSet>, Debug<NotmuchError>> {
let res = nm.show(query)?;
Ok(Json(res))
}
#[get("/show/<query>")] #[get("/show/<query>")]
async fn show(nm: &State<Notmuch>, query: &str) -> Result<Json<ThreadSet>, Debug<NotmuchError>> { async fn show(nm: &State<Notmuch>, query: &str) -> Result<Json<ThreadSet>, Debug<NotmuchError>> {
let res = nm.show(query)?; let res = nm.show(query)?;
@ -119,15 +110,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
let _ = rocket::build() let _ = rocket::build()
.mount( .mount(
"/", "/",
routes![ routes![original_part, original, hello, search_all, search, show],
original_part,
original,
hello,
search_all,
search,
show_pretty,
show
],
) )
.attach(cors) .attach(cors)
.manage(Notmuch::default()) .manage(Notmuch::default())

View File

@ -24,7 +24,6 @@ serde = { version = "1.0.147", features = ["derive"] }
notmuch = {path = "../notmuch"} notmuch = {path = "../notmuch"}
itertools = "0.10.5" itertools = "0.10.5"
serde_json = { version = "1.0.93", features = ["unbounded_depth"] } serde_json = { version = "1.0.93", features = ["unbounded_depth"] }
wasm-timer = "0.2.5"
[package.metadata.wasm-pack.profile.release] [package.metadata.wasm-pack.profile.release]
wasm-opt = ['-Os'] wasm-opt = ['-Os']

View File

@ -14,6 +14,7 @@
} }
.body { .body {
padding-bottom: 1em; padding-bottom: 1em;
border: 1px red solid;
} }
.error { .error {
background-color: red; background-color: red;
@ -33,18 +34,6 @@ iframe {
.index .date { .index .date {
white-space: nowrap; white-space: nowrap;
} }
.footer {
background-color: #eee;
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 3em;
padding: 1em;
}
.tag {
margin-right: 2px;
}
</style> </style>
</head> </head>

View File

@ -2,17 +2,12 @@
// - it's useful when you want to check your code with `cargo make verify` // - it's useful when you want to check your code with `cargo make verify`
// but some rules are too "annoying" or are not applicable for your case.) // but some rules are too "annoying" or are not applicable for your case.)
#![allow(clippy::wildcard_imports)] #![allow(clippy::wildcard_imports)]
use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
};
use itertools::Itertools; use itertools::Itertools;
use log::{debug, error, info, warn, Level}; use log::{debug, error, info, warn, Level};
use notmuch::{Content, Part, SearchSummary, Thread, ThreadNode, ThreadSet}; use notmuch::{Content, Part, SearchSummary, Thread, ThreadNode, ThreadSet};
use seed::{prelude::*, *}; use seed::{prelude::*, *};
use serde::de::Deserialize; use serde::de::Deserialize;
use wasm_timer::Instant;
// ------ ------ // ------ ------
// Init // Init
@ -29,7 +24,7 @@ fn init(url: Url, orders: &mut impl Orders<Msg>) -> Model {
match hpp { match hpp {
Some("t") => { Some("t") => {
let tid = url.next_hash_path_part().unwrap_or("").to_string(); let tid = url.next_hash_path_part().unwrap_or("").to_string();
orders.send_msg(Msg::ShowPrettyRequest(tid)); orders.send_msg(Msg::ShowRequest(tid));
} }
Some("s") => { Some("s") => {
query = url.next_hash_path_part().unwrap_or("").to_string(); query = url.next_hash_path_part().unwrap_or("").to_string();
@ -78,8 +73,6 @@ enum Msg {
SearchResult(fetch::Result<SearchSummary>), SearchResult(fetch::Result<SearchSummary>),
ShowRequest(String), ShowRequest(String),
ShowResult(fetch::Result<ThreadSet>), ShowResult(fetch::Result<ThreadSet>),
ShowPrettyRequest(String),
ShowPrettyResult(fetch::Result<ThreadSet>),
} }
// `update` describes how to handle each `Msg`. // `update` describes how to handle each `Msg`.
@ -117,21 +110,6 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
Msg::ShowResult(Err(fetch_error)) => { Msg::ShowResult(Err(fetch_error)) => {
error!("fetch failed {:?}", fetch_error); error!("fetch failed {:?}", fetch_error);
} }
Msg::ShowPrettyRequest(tid) => {
let url = Url::new().set_hash_path(["t", &tid]);
orders.request_url(url);
orders
.skip()
.perform_cmd(async move { Msg::ShowPrettyResult(show_pretty_request(&tid).await) });
}
Msg::ShowPrettyResult(Ok(response_data)) => {
debug!("fetch ok {:#?}", response_data);
model.context = Context::Thread(response_data);
}
Msg::ShowPrettyResult(Err(fetch_error)) => {
error!("fetch failed {:?}", fetch_error);
}
} }
} }
@ -153,9 +131,6 @@ mod api {
pub fn show(tid: &str) -> String { pub fn show(tid: &str) -> String {
format!("{}/show/{}", BASE_URL, tid) format!("{}/show/{}", BASE_URL, tid)
} }
pub fn show_pretty(tid: &str) -> String {
format!("{}/show/{}/pretty", BASE_URL, tid)
}
pub fn original(message_id: &str) -> String { pub fn original(message_id: &str) -> String {
format!("{}/original/{}", BASE_URL, message_id) format!("{}/original/{}", BASE_URL, message_id)
} }
@ -175,16 +150,6 @@ async fn show_request(tid: &str) -> fetch::Result<ThreadSet> {
.map_err(|_| FetchError::JsonError(fetch::JsonError::Serde(JsValue::NULL)))?) .map_err(|_| FetchError::JsonError(fetch::JsonError::Serde(JsValue::NULL)))?)
} }
async fn show_pretty_request(tid: &str) -> fetch::Result<ThreadSet> {
Request::new(api::show_pretty(tid))
.method(Method::Get)
.fetch()
.await?
.check_status()?
.json()
.await
}
// ------ ------ // ------ ------
// View // View
// ------ ------ // ------ ------
@ -308,32 +273,30 @@ fn set_title(title: &str) {
} }
fn tags_chiclet(tags: &[String]) -> impl Iterator<Item = Node<Msg>> + '_ { fn tags_chiclet(tags: &[String]) -> impl Iterator<Item = Node<Msg>> + '_ {
tags.iter().map(|tag| { tags.iter().map(|tag| match tag.as_str() {
let mut hasher = DefaultHasher::new(); "attachment" => span![C!["tag"], "📎"],
tag.hash(&mut hasher); "replied" => span![C!["tag"], i![C!["fa-solid", "fa-reply"]]],
let hex = format!("#{:06x}", hasher.finish() % (1 << 24)); _ => span![C!["tag"], tag],
let style = style! {St::BackgroundColor=>hex};
match tag.as_str() {
"attachment" => span![C!["tag"], style, "📎"],
"replied" => span![C!["tag"], style, i![C!["fa-solid", "fa-reply"]]],
_ => span![C!["tag"], style, tag],
}
}) })
} }
fn pretty_authors(authors: &str) -> impl Iterator<Item = Node<Msg>> + '_ { fn pretty_authors(authors: &str) -> impl Iterator<Item = Node<Msg>> + '_ {
let one_person = authors.matches(',').count() == 0;
let authors = authors.split(','); let authors = authors.split(',');
Itertools::intersperse( /*
authors.filter_map(move |author| { if authors.len() == 1 {
if one_person { return authors.iter().filter_map(|author| {
return Some(span![ Some(span![
attrs! { attrs! {
At::Title => author.trim()}, At::Title => author.trim()},
author author
]); ])
});
} }
*/
authors
.filter_map(|author| {
author.split_whitespace().nth(0).map(|first| { author.split_whitespace().nth(0).map(|first| {
span![ span![
attrs! { attrs! {
@ -341,9 +304,8 @@ fn pretty_authors(authors: &str) -> impl Iterator<Item = Node<Msg>> + '_ {
first first
] ]
}) })
}), })
span![", "], .intersperse(span![", "])
)
} }
fn view_search_results(query: &str, search_results: &SearchSummary) -> Node<Msg> { fn view_search_results(query: &str, search_results: &SearchSummary) -> Node<Msg> {
@ -362,7 +324,7 @@ fn view_search_results(query: &str, search_results: &SearchSummary) -> Node<Msg>
], ],
td![C!["subject"], tags_chiclet(&r.tags), " ", &r.subject], td![C!["subject"], tags_chiclet(&r.tags), " ", &r.subject],
td![C!["date"], &r.date_relative], td![C!["date"], &r.date_relative],
ev(Ev::Click, move |_| Msg::ShowPrettyRequest(tid)), ev(Ev::Click, move |_| Msg::ShowRequest(tid)),
] ]
}); });
div![table![ div![table![
@ -436,6 +398,8 @@ fn view_header(query: &str) -> Node<Msg> {
nav![ nav![
C!["navbar"], C!["navbar"],
attrs! {At::Role=>"navigation"}, attrs! {At::Role=>"navigation"},
div![
C!["navbar-menu"],
div![ div![
C!["navbar-start"], C!["navbar-start"],
a![ a![
@ -465,35 +429,22 @@ fn view_header(query: &str) -> Node<Msg> {
] ]
] ]
] ]
}
fn view_footer(render_time_ms: u128) -> Node<Msg> {
footer![
C!["footer"],
div![
C!["content", "has-text-right", "is-size-7"],
format!("Render time {} ms", render_time_ms)
]
] ]
} }
// `view` describes what to display. // `view` describes what to display.
fn view(model: &Model) -> Node<Msg> { fn view(model: &Model) -> Node<Msg> {
let start = Instant::now();
info!("view called"); info!("view called");
let content = match &model.context { let content = match &model.context {
Context::None => div![h1!["Loading"]], Context::None => div![h1!["Loading"]],
Context::Thread(thread_set) => view_thread(thread_set), Context::Thread(thread_set) => view_thread(thread_set),
Context::Search(search_results) => view_search_results(&model.query, search_results), Context::Search(search_results) => view_search_results(&model.query, search_results),
}; };
div![ div![section![
view_header(&model.query),
section![
C!["section"], C!["section"],
div![C!["container"], content], view_header(&model.query),
view_footer(start.elapsed().as_millis()) div![C!["container"], content]
] ]]
]
} }
// ------ ------ // ------ ------