Compare commits
No commits in common. "c33f901f48712d5a3acc04f193a348d3171b2599" and "d8275debdc544cf3641abe3508a9c209068b7f2f" have entirely different histories.
c33f901f48
...
d8275debdc
45
Cargo.lock
generated
45
Cargo.lock
generated
@ -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"
|
||||||
|
|||||||
@ -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())
|
||||||
|
|||||||
@ -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']
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
157
web/src/lib.rs
157
web/src/lib.rs
@ -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![
|
||||||
@ -437,63 +399,52 @@ fn view_header(query: &str) -> Node<Msg> {
|
|||||||
C!["navbar"],
|
C!["navbar"],
|
||||||
attrs! {At::Role=>"navigation"},
|
attrs! {At::Role=>"navigation"},
|
||||||
div![
|
div![
|
||||||
C!["navbar-start"],
|
C!["navbar-menu"],
|
||||||
a![
|
div![
|
||||||
C!["navbar-item", "button",],
|
C!["navbar-start"],
|
||||||
"Unread",
|
a![
|
||||||
ev(Ev::Click, |_| Msg::SearchRequest("is:unread".to_string())),
|
C!["navbar-item", "button",],
|
||||||
],
|
"Unread",
|
||||||
a![
|
ev(Ev::Click, |_| Msg::SearchRequest("is:unread".to_string())),
|
||||||
C!["navbar-item", "button"],
|
],
|
||||||
"All",
|
a![
|
||||||
ev(Ev::Click, |_| Msg::SearchRequest("".to_string())),
|
C!["navbar-item", "button"],
|
||||||
],
|
"All",
|
||||||
input![
|
ev(Ev::Click, |_| Msg::SearchRequest("".to_string())),
|
||||||
C!["navbar-item", "input"],
|
],
|
||||||
attrs! {
|
input![
|
||||||
At::Placeholder => "Search";
|
C!["navbar-item", "input"],
|
||||||
At::AutoFocus => true.as_at_value();
|
attrs! {
|
||||||
At::Value => query,
|
At::Placeholder => "Search";
|
||||||
},
|
At::AutoFocus => true.as_at_value();
|
||||||
input_ev(Ev::Input, Msg::SearchRequest),
|
At::Value => query,
|
||||||
// Resend search on enter.
|
},
|
||||||
keyboard_ev(Ev::KeyUp, move |e| if e.key_code() == 0x0d {
|
input_ev(Ev::Input, Msg::SearchRequest),
|
||||||
Msg::SearchRequest(query)
|
// Resend search on enter.
|
||||||
} else {
|
keyboard_ev(Ev::KeyUp, move |e| if e.key_code() == 0x0d {
|
||||||
Msg::Noop
|
Msg::SearchRequest(query)
|
||||||
}),
|
} else {
|
||||||
|
Msg::Noop
|
||||||
|
}),
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
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![
|
||||||
|
C!["section"],
|
||||||
view_header(&model.query),
|
view_header(&model.query),
|
||||||
section![
|
div![C!["container"], content]
|
||||||
C!["section"],
|
]]
|
||||||
div![C!["container"], content],
|
|
||||||
view_footer(start.elapsed().as_millis())
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------ ------
|
// ------ ------
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user