Add refresh button and mobile view.
This commit is contained in:
parent
c33f901f48
commit
0b4cfadf88
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -983,6 +983,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"wasm-bindgen-test",
|
||||
"wasm-timer",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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"
|
||||
]
|
||||
|
||||
110
web/src/lib.rs
110
web/src/lib.rs
@ -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())
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user