Add refresh button and mobile view.

This commit is contained in:
Bill Thiede 2023-03-04 09:00:18 -08:00
parent c33f901f48
commit 0b4cfadf88
4 changed files with 111 additions and 14 deletions

1
Cargo.lock generated
View File

@ -983,6 +983,7 @@ dependencies = [
"serde_json",
"wasm-bindgen-test",
"wasm-timer",
"web-sys",
]
[[package]]

View File

@ -18,6 +18,10 @@ fn hello() -> &'static str {
"Hello, world!"
}
#[get("/refresh")]
async fn refresh(nm: &State<Notmuch>) -> Result<Json<String>, Debug<NotmuchError>> {
Ok(Json(String::from_utf8_lossy(&nm.new()?).to_string()))
}
#[get("/search")]
async fn search_all(nm: &State<Notmuch>) -> Result<Json<SearchSummary>, Debug<NotmuchError>> {
search(nm, "*").await
@ -123,6 +127,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
original_part,
original,
hello,
refresh,
search_all,
search,
show_pretty,

View File

@ -28,3 +28,10 @@ wasm-timer = "0.2.5"
[package.metadata.wasm-pack.profile.release]
wasm-opt = ['-Os']
[dependencies.web-sys]
version = "0.3.58"
features = [
"MediaQueryList",
"Window"
]

View File

@ -74,6 +74,7 @@ struct Model {
// `Msg` describes the different events you can modify state with.
enum Msg {
Noop,
Refresh,
SearchRequest(String),
SearchResult(fetch::Result<SearchSummary>),
ShowRequest(String),
@ -86,6 +87,11 @@ enum Msg {
fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
match msg {
Msg::Noop => {}
Msg::Refresh => {
orders.skip().perform_cmd(async move {
refresh_request().await;
});
}
Msg::SearchRequest(query) => {
model.query = query.clone();
@ -147,20 +153,35 @@ async fn search_request(query: &str) -> fetch::Result<SearchSummary> {
mod api {
const BASE_URL: &str = "/api";
pub fn refresh() -> String {
format!("{BASE_URL}/refresh")
}
pub fn search(query: &str) -> String {
format!("{}/search/{}", BASE_URL, query)
format!("{BASE_URL}/search/{query}")
}
pub fn show(tid: &str) -> String {
format!("{}/show/{}", BASE_URL, tid)
format!("{BASE_URL}/show/{tid}")
}
pub fn show_pretty(tid: &str) -> String {
format!("{}/show/{}/pretty", BASE_URL, tid)
format!("{BASE_URL}/show/{tid}/pretty")
}
pub fn original(message_id: &str) -> String {
format!("{}/original/{}", BASE_URL, message_id)
format!("{BASE_URL}/original/{message_id}")
}
}
async fn refresh_request() -> fetch::Result<()> {
let t = Request::new(api::refresh())
.method(Method::Get)
.fetch()
.await?
.check_status()?
.text()
.await?;
info!("refresh {t}");
Ok(())
}
async fn show_request(tid: &str) -> fetch::Result<ThreadSet> {
let b = Request::new(api::show(tid))
.method(Method::Get)
@ -346,6 +367,39 @@ fn pretty_authors(authors: &str) -> impl Iterator<Item = Node<Msg>> + '_ {
)
}
fn view_mobile_search_results(query: &str, search_results: &SearchSummary) -> Node<Msg> {
if query.is_empty() {
set_title("all mail");
} else {
set_title(query);
}
let rows = search_results.0.iter().map(|r| {
/*
let tid = r.thread.clone();
tr![
td![
C!["from"],
pretty_authors(&r.authors),
IF!(r.total>1 => small![" ", r.total.to_string()]),
],
td![C!["subject"], tags_chiclet(&r.tags), " ", &r.subject],
td![C!["date"], &r.date_relative],
ev(Ev::Click, move |_| Msg::ShowPrettyRequest(tid)),
]
*/
div![
p![C!["subject"], &r.subject],
div![
span![C!["from"], pretty_authors(&r.authors)],
span![C!["tags"], tags_chiclet(&r.tags)],
],
span![C!["date"], &r.date_relative],
hr![],
]
});
div![h1!["Search results"], rows]
}
fn view_search_results(query: &str, search_results: &SearchSummary) -> Node<Msg> {
if query.is_empty() {
set_title("all mail");
@ -439,7 +493,12 @@ fn view_header(query: &str) -> Node<Msg> {
div![
C!["navbar-start"],
a![
C!["navbar-item", "button",],
C!["navbar-item", "button"],
span![i![C!["fa-solid", "fa-arrow-rotate-right"]]],
ev(Ev::Click, |_| Msg::Refresh),
],
a![
C!["navbar-item", "button"],
"Unread",
ev(Ev::Click, |_| Msg::SearchRequest("is:unread".to_string())),
],
@ -477,10 +536,7 @@ fn view_footer(render_time_ms: u128) -> Node<Msg> {
]
}
// `view` describes what to display.
fn view(model: &Model) -> Node<Msg> {
let start = Instant::now();
info!("view called");
fn view_desktop(model: &Model) -> Node<Msg> {
let content = match &model.context {
Context::None => div![h1!["Loading"]],
Context::Thread(thread_set) => view_thread(thread_set),
@ -488,11 +544,39 @@ fn view(model: &Model) -> Node<Msg> {
};
div![
view_header(&model.query),
section![
C!["section"],
div![C!["container"], content],
view_footer(start.elapsed().as_millis())
section![C!["section"], div![C!["container"], content],]
]
}
fn view_mobile(model: &Model) -> Node<Msg> {
let content = match &model.context {
Context::None => div![h1!["Loading"]],
Context::Thread(thread_set) => view_thread(thread_set),
Context::Search(search_results) => view_mobile_search_results(&model.query, search_results),
};
div![
view_header(&model.query),
section![C!["section"], div![C!["container"], content],]
]
}
// `view` describes what to display.
fn view(model: &Model) -> Node<Msg> {
let is_mobile = seed::window()
.match_media("(max-width: 768px)")
.expect("failed media query")
.map(|mql| mql.matches())
.unwrap_or(false);
let start = Instant::now();
info!("view called");
div![
if is_mobile {
view_mobile(model)
} else {
view_desktop(model)
},
view_footer(start.elapsed().as_millis())
]
}