Add pagination to search results.
Move to shared definition of json requests between client/server.
This commit is contained in:
@@ -22,6 +22,7 @@ seed = "0.9.2"
|
||||
console_log = {git = "http://git-private.h.xinu.tv/wathiede/console_log.git"}
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
notmuch = {path = "../notmuch"}
|
||||
shared = {path = "../shared"}
|
||||
itertools = "0.10.5"
|
||||
serde_json = { version = "1.0.93", features = ["unbounded_depth"] }
|
||||
wasm-timer = "0.2.5"
|
||||
|
||||
@@ -37,6 +37,7 @@ iframe {
|
||||
}
|
||||
.footer {
|
||||
background-color: #eee;
|
||||
color: #222;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
@@ -71,6 +72,12 @@ iframe {
|
||||
padding: 1.5em;
|
||||
}
|
||||
}
|
||||
input, .input {
|
||||
color: #000;
|
||||
}
|
||||
input::placeholder, .input::placeholder{
|
||||
color: #555;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
117
web/src/lib.rs
117
web/src/lib.rs
@@ -9,7 +9,7 @@ use std::{
|
||||
|
||||
use itertools::Itertools;
|
||||
use log::{debug, error, info, warn, Level};
|
||||
use notmuch::{Content, Part, SearchSummary, Thread, ThreadNode, ThreadSet};
|
||||
use notmuch::{Content, Part, Thread, ThreadNode, ThreadSet};
|
||||
use seed::{prelude::*, *};
|
||||
use serde::de::Deserialize;
|
||||
use wasm_timer::Instant;
|
||||
@@ -57,7 +57,7 @@ fn init(url: Url, orders: &mut impl Orders<Msg>) -> Model {
|
||||
// ------ ------
|
||||
enum Context {
|
||||
None,
|
||||
Search(SearchSummary),
|
||||
Search(shared::SearchResult),
|
||||
Thread(ThreadSet),
|
||||
}
|
||||
|
||||
@@ -86,11 +86,13 @@ enum Msg {
|
||||
RefreshStart,
|
||||
RefreshDone(Option<FetchError>),
|
||||
SearchRequest(String),
|
||||
SearchResult(fetch::Result<SearchSummary>),
|
||||
SearchResult(fetch::Result<shared::SearchResult>),
|
||||
ShowRequest(String),
|
||||
ShowResult(fetch::Result<ThreadSet>),
|
||||
ShowPrettyRequest(String),
|
||||
ShowPrettyResult(fetch::Result<ThreadSet>),
|
||||
NextPage,
|
||||
PreviousPage,
|
||||
}
|
||||
|
||||
// `update` describes how to handle each `Msg`.
|
||||
@@ -155,10 +157,38 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
Msg::ShowPrettyResult(Err(fetch_error)) => {
|
||||
error!("fetch failed {:?}", fetch_error);
|
||||
}
|
||||
Msg::NextPage => {
|
||||
match &model.context {
|
||||
Context::Search(sr) => {
|
||||
orders.send_msg(Msg::SearchRequest(format!(
|
||||
"{}?page={}&results_per_page={}",
|
||||
Url::encode_uri_component(&sr.query),
|
||||
sr.page + 1,
|
||||
sr.results_per_page
|
||||
)));
|
||||
}
|
||||
Context::Thread(_) => (), // do nothing (yet?)
|
||||
Context::None => (), // do nothing (yet?)
|
||||
};
|
||||
}
|
||||
Msg::PreviousPage => {
|
||||
match &model.context {
|
||||
Context::Search(sr) => {
|
||||
orders.send_msg(Msg::SearchRequest(format!(
|
||||
"{}?page={}&results_per_page={}",
|
||||
Url::encode_uri_component(&sr.query),
|
||||
sr.page.saturating_sub(1),
|
||||
sr.results_per_page
|
||||
)));
|
||||
}
|
||||
Context::Thread(_) => (), // do nothing (yet?)
|
||||
Context::None => (), // do nothing (yet?)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn search_request(query: &str) -> fetch::Result<SearchSummary> {
|
||||
async fn search_request(query: &str) -> fetch::Result<shared::SearchResult> {
|
||||
Request::new(api::search(query))
|
||||
.method(Method::Get)
|
||||
.fetch()
|
||||
@@ -402,13 +432,14 @@ fn pretty_authors(authors: &str) -> impl Iterator<Item = Node<Msg>> + '_ {
|
||||
)
|
||||
}
|
||||
|
||||
fn view_mobile_search_results(query: &str, search_results: &SearchSummary) -> Node<Msg> {
|
||||
fn view_mobile_search_results(query: &str, search_results: &shared::SearchResult) -> Node<Msg> {
|
||||
if query.is_empty() {
|
||||
set_title("all mail");
|
||||
} else {
|
||||
set_title(query);
|
||||
}
|
||||
let rows = search_results.0.iter().map(|r| {
|
||||
let summaries = &search_results.summary.0;
|
||||
let rows = summaries.iter().map(|r| {
|
||||
/*
|
||||
let tid = r.thread.clone();
|
||||
tr![
|
||||
@@ -437,16 +468,22 @@ fn view_mobile_search_results(query: &str, search_results: &SearchSummary) -> No
|
||||
hr![],
|
||||
]
|
||||
});
|
||||
div![h1!["Search results"], rows]
|
||||
let first = search_results.page * search_results.results_per_page;
|
||||
div![
|
||||
h1!["Search results"],
|
||||
view_search_pager(first, summaries.len(), search_results.total),
|
||||
rows
|
||||
]
|
||||
}
|
||||
|
||||
fn view_search_results(query: &str, search_results: &SearchSummary) -> Node<Msg> {
|
||||
fn view_search_results(query: &str, search_results: &shared::SearchResult) -> Node<Msg> {
|
||||
if query.is_empty() {
|
||||
set_title("all mail");
|
||||
} else {
|
||||
set_title(query);
|
||||
}
|
||||
let rows = search_results.0.iter().map(|r| {
|
||||
let summaries = &search_results.summary.0;
|
||||
let rows = summaries.iter().map(|r| {
|
||||
let tid = r.thread.clone();
|
||||
tr![
|
||||
td![
|
||||
@@ -466,22 +503,46 @@ fn view_search_results(query: &str, search_results: &SearchSummary) -> Node<Msg>
|
||||
td![C!["date"], &r.date_relative]
|
||||
]
|
||||
});
|
||||
div![table![
|
||||
C![
|
||||
"table",
|
||||
"index",
|
||||
"is-fullwidth",
|
||||
"is-hoverable",
|
||||
"is-narrow",
|
||||
"is-striped",
|
||||
let first = search_results.page * search_results.results_per_page;
|
||||
div![
|
||||
view_search_pager(first, summaries.len(), search_results.total),
|
||||
table![
|
||||
C![
|
||||
"table",
|
||||
"index",
|
||||
"is-fullwidth",
|
||||
"is-hoverable",
|
||||
"is-narrow",
|
||||
"is-striped",
|
||||
],
|
||||
thead![tr![
|
||||
th![C!["from"], "From"],
|
||||
th![C!["subject"], "Subject"],
|
||||
th![C!["date"], "Date"]
|
||||
]],
|
||||
tbody![rows]
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
fn view_search_pager(page: usize, count: usize, total: usize) -> Node<Msg> {
|
||||
nav![
|
||||
C!["pagination"],
|
||||
a![
|
||||
C!["pagination-previous", "button"],
|
||||
"<",
|
||||
ev(Ev::Click, |_| Msg::PreviousPage)
|
||||
],
|
||||
thead![tr![
|
||||
th![C!["from"], "From"],
|
||||
th![C!["subject"], "Subject"],
|
||||
th![C!["date"], "Date"]
|
||||
]],
|
||||
tbody![rows]
|
||||
]]
|
||||
a![
|
||||
C!["pagination-next", "button"],
|
||||
">",
|
||||
ev(Ev::Click, |_| Msg::NextPage)
|
||||
],
|
||||
ul![
|
||||
C!["pagination-list"],
|
||||
li![format!("{} - {} of {}", page, page + count, total)],
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
fn view_thread(thread_set: &ThreadSet) -> Node<Msg> {
|
||||
@@ -541,7 +602,7 @@ fn view_header(query: &str, refresh_request: &RefreshingState) -> Node<Msg> {
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let query = query.to_string();
|
||||
let query = Url::decode_uri_component(query).unwrap_or("".to_string());
|
||||
nav![
|
||||
C!["navbar"],
|
||||
attrs! {At::Role=>"navigation"},
|
||||
@@ -574,10 +635,12 @@ fn view_header(query: &str, refresh_request: &RefreshingState) -> Node<Msg> {
|
||||
At::AutoFocus => true.as_at_value();
|
||||
At::Value => query,
|
||||
},
|
||||
input_ev(Ev::Input, Msg::SearchRequest),
|
||||
input_ev(Ev::Input, |q| Msg::SearchRequest(
|
||||
Url::encode_uri_component(q)
|
||||
)),
|
||||
// Resend search on enter.
|
||||
keyboard_ev(Ev::KeyUp, move |e| if e.key_code() == 0x0d {
|
||||
Msg::SearchRequest(query)
|
||||
Msg::SearchRequest(Url::encode_uri_component(query))
|
||||
} else {
|
||||
Msg::Noop
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user