web: remove a ton of legacy deprecated code
This commit is contained in:
parent
04592ddcc4
commit
02c0d36f90
@ -1,22 +1,10 @@
|
|||||||
use gloo_net::{http::Request, Error};
|
use gloo_net::{http::Request, Error};
|
||||||
use log::info;
|
use log::info;
|
||||||
use notmuch::ThreadSet;
|
|
||||||
use seed::Url;
|
|
||||||
|
|
||||||
const BASE_URL: &str = "/api";
|
const BASE_URL: &str = "/api";
|
||||||
pub fn refresh() -> String {
|
pub fn refresh() -> String {
|
||||||
format!("{BASE_URL}/refresh")
|
format!("{BASE_URL}/refresh")
|
||||||
}
|
}
|
||||||
pub fn search(query: &str, page: usize, results_per_page: usize) -> String {
|
|
||||||
let query = Url::encode_uri_component(query);
|
|
||||||
format!("{BASE_URL}/search/{query}?page={page}&results_per_page={results_per_page}")
|
|
||||||
}
|
|
||||||
pub fn show_pretty(tid: &str) -> String {
|
|
||||||
format!("{BASE_URL}/show/{tid}/pretty")
|
|
||||||
}
|
|
||||||
pub fn original(message_id: &str) -> String {
|
|
||||||
format!("{BASE_URL}/original/{message_id}")
|
|
||||||
}
|
|
||||||
pub mod urls {
|
pub mod urls {
|
||||||
use seed::Url;
|
use seed::Url;
|
||||||
pub fn search(query: &str, page: usize) -> Url {
|
pub fn search(query: &str, page: usize) -> Url {
|
||||||
@ -32,24 +20,8 @@ pub mod urls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn search_request(
|
|
||||||
query: &str,
|
|
||||||
page: usize,
|
|
||||||
results_per_page: usize,
|
|
||||||
) -> Result<shared::SearchResult, Error> {
|
|
||||||
Request::get(&search(query, page, results_per_page))
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
.json()
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn refresh_request() -> Result<(), Error> {
|
pub async fn refresh_request() -> Result<(), Error> {
|
||||||
let t = Request::get(&refresh()).send().await?.text().await?;
|
let t = Request::get(&refresh()).send().await?.text().await?;
|
||||||
info!("refresh {t}");
|
info!("refresh {t}");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn show_pretty_request(tid: &str) -> Result<ThreadSet, Error> {
|
|
||||||
Request::get(&show_pretty(tid)).send().await?.json().await
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,2 +1 @@
|
|||||||
pub const SEARCH_RESULTS_PER_PAGE: usize = 20;
|
pub const SEARCH_RESULTS_PER_PAGE: usize = 20;
|
||||||
pub const USE_GRAPHQL: bool = true;
|
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
use graphql_client::GraphQLQuery;
|
use graphql_client::GraphQLQuery;
|
||||||
use log::{debug, error, info};
|
use log::{error, info};
|
||||||
use notmuch::ThreadSet;
|
|
||||||
use seed::{app::subs, prelude::*, *};
|
use seed::{app::subs, prelude::*, *};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api,
|
api,
|
||||||
api::urls,
|
api::urls,
|
||||||
consts::{SEARCH_RESULTS_PER_PAGE, USE_GRAPHQL},
|
consts::SEARCH_RESULTS_PER_PAGE,
|
||||||
graphql,
|
graphql,
|
||||||
graphql::{front_page_query::*, send_graphql, show_thread_query::*},
|
graphql::{front_page_query::*, send_graphql, show_thread_query::*},
|
||||||
};
|
};
|
||||||
@ -43,18 +42,11 @@ fn on_url_changed(uc: subs::UrlChanged) -> Msg {
|
|||||||
);
|
);
|
||||||
let hpp = url.remaining_hash_path_parts();
|
let hpp = url.remaining_hash_path_parts();
|
||||||
match hpp.as_slice() {
|
match hpp.as_slice() {
|
||||||
["t", tid] => {
|
["t", tid] => Msg::ShowThreadRequest {
|
||||||
if USE_GRAPHQL {
|
|
||||||
Msg::ShowThreadRequest {
|
|
||||||
thread_id: tid.to_string(),
|
thread_id: tid.to_string(),
|
||||||
}
|
},
|
||||||
} else {
|
|
||||||
Msg::ShowPrettyRequest(tid.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
["s", query] => {
|
["s", query] => {
|
||||||
let query = Url::decode_uri_component(query).unwrap_or("".to_string());
|
let query = Url::decode_uri_component(query).unwrap_or("".to_string());
|
||||||
if USE_GRAPHQL {
|
|
||||||
Msg::FrontPageRequest {
|
Msg::FrontPageRequest {
|
||||||
query,
|
query,
|
||||||
after: None,
|
after: None,
|
||||||
@ -62,18 +54,10 @@ fn on_url_changed(uc: subs::UrlChanged) -> Msg {
|
|||||||
first: None,
|
first: None,
|
||||||
last: None,
|
last: None,
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Msg::SearchRequest {
|
|
||||||
query,
|
|
||||||
page: 0,
|
|
||||||
results_per_page: SEARCH_RESULTS_PER_PAGE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
["s", query, page] => {
|
["s", query, page] => {
|
||||||
let query = Url::decode_uri_component(query).unwrap_or("".to_string());
|
let query = Url::decode_uri_component(query).unwrap_or("".to_string());
|
||||||
let page = page[1..].parse().unwrap_or(0);
|
let page = page[1..].parse().unwrap_or(0);
|
||||||
if USE_GRAPHQL {
|
|
||||||
Msg::FrontPageRequest {
|
Msg::FrontPageRequest {
|
||||||
query,
|
query,
|
||||||
after: Some(page.to_string()),
|
after: Some(page.to_string()),
|
||||||
@ -81,19 +65,11 @@ fn on_url_changed(uc: subs::UrlChanged) -> Msg {
|
|||||||
first: None,
|
first: None,
|
||||||
last: None,
|
last: None,
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Msg::SearchRequest {
|
|
||||||
query,
|
|
||||||
page,
|
|
||||||
results_per_page: SEARCH_RESULTS_PER_PAGE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
p => {
|
p => {
|
||||||
if !p.is_empty() {
|
if !p.is_empty() {
|
||||||
info!("Unhandled path '{p:?}'");
|
info!("Unhandled path '{p:?}'");
|
||||||
}
|
}
|
||||||
if USE_GRAPHQL {
|
|
||||||
Msg::FrontPageRequest {
|
Msg::FrontPageRequest {
|
||||||
query: "".to_string(),
|
query: "".to_string(),
|
||||||
after: None,
|
after: None,
|
||||||
@ -101,13 +77,6 @@ fn on_url_changed(uc: subs::UrlChanged) -> Msg {
|
|||||||
first: None,
|
first: None,
|
||||||
last: None,
|
last: None,
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Msg::SearchRequest {
|
|
||||||
query: "".to_string(),
|
|
||||||
page: 0,
|
|
||||||
results_per_page: SEARCH_RESULTS_PER_PAGE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,42 +102,8 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
Msg::OnResize => (),
|
Msg::OnResize => (),
|
||||||
|
|
||||||
Msg::SearchRequest {
|
|
||||||
query,
|
|
||||||
page,
|
|
||||||
results_per_page,
|
|
||||||
} => {
|
|
||||||
info!("searching for '{query}' pg {page} # / pg {results_per_page}");
|
|
||||||
model.query = query.clone();
|
|
||||||
orders.skip().perform_cmd(async move {
|
|
||||||
Msg::SearchResult(api::search_request(&query, page, results_per_page).await)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Msg::SearchResult(Ok(response_data)) => {
|
|
||||||
debug!("fetch ok {:#?}", response_data);
|
|
||||||
model.context = Context::Search(response_data);
|
|
||||||
}
|
|
||||||
Msg::SearchResult(Err(fetch_error)) => {
|
|
||||||
error!("fetch failed {:?}", fetch_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
Msg::ShowPrettyRequest(tid) => {
|
|
||||||
orders.skip().perform_cmd(async move {
|
|
||||||
Msg::ShowPrettyResult(api::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);
|
|
||||||
}
|
|
||||||
Msg::NextPage => {
|
Msg::NextPage => {
|
||||||
match &model.context {
|
match &model.context {
|
||||||
Context::Search(sr) => {
|
|
||||||
orders.request_url(urls::search(&sr.query, sr.page + 1));
|
|
||||||
}
|
|
||||||
Context::SearchResult { query, pager, .. } => {
|
Context::SearchResult { query, pager, .. } => {
|
||||||
let query = query.to_string();
|
let query = query.to_string();
|
||||||
let after = pager.end_cursor.clone();
|
let after = pager.end_cursor.clone();
|
||||||
@ -182,16 +117,12 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Context::Thread(_) => (), // do nothing (yet?)
|
|
||||||
Context::ThreadResult(_) => (), // do nothing (yet?)
|
Context::ThreadResult(_) => (), // do nothing (yet?)
|
||||||
Context::None => (), // do nothing (yet?)
|
Context::None => (), // do nothing (yet?)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Msg::PreviousPage => {
|
Msg::PreviousPage => {
|
||||||
match &model.context {
|
match &model.context {
|
||||||
Context::Search(sr) => {
|
|
||||||
orders.request_url(urls::search(&sr.query, sr.page.saturating_sub(1)));
|
|
||||||
}
|
|
||||||
Context::SearchResult { query, pager, .. } => {
|
Context::SearchResult { query, pager, .. } => {
|
||||||
let query = query.to_string();
|
let query = query.to_string();
|
||||||
let before = pager.start_cursor.clone();
|
let before = pager.start_cursor.clone();
|
||||||
@ -206,7 +137,6 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::Thread(_) => (), // do nothing (yet?)
|
|
||||||
Context::ThreadResult(_) => (), // do nothing (yet?)
|
Context::ThreadResult(_) => (), // do nothing (yet?)
|
||||||
Context::None => (), // do nothing (yet?)
|
Context::None => (), // do nothing (yet?)
|
||||||
};
|
};
|
||||||
@ -352,14 +282,12 @@ pub enum UIError {
|
|||||||
|
|
||||||
pub enum Context {
|
pub enum Context {
|
||||||
None,
|
None,
|
||||||
Search(shared::SearchResult),
|
|
||||||
SearchResult {
|
SearchResult {
|
||||||
query: String,
|
query: String,
|
||||||
results: Vec<FrontPageQuerySearchNodes>,
|
results: Vec<FrontPageQuerySearchNodes>,
|
||||||
count: usize,
|
count: usize,
|
||||||
pager: FrontPageQuerySearchPageInfo,
|
pager: FrontPageQuerySearchPageInfo,
|
||||||
},
|
},
|
||||||
Thread(ThreadSet),
|
|
||||||
ThreadResult(ShowThreadQueryThread),
|
ThreadResult(ShowThreadQueryThread),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,14 +314,6 @@ pub enum Msg {
|
|||||||
// Tell the server to update state
|
// Tell the server to update state
|
||||||
RefreshStart,
|
RefreshStart,
|
||||||
RefreshDone(Option<gloo_net::Error>),
|
RefreshDone(Option<gloo_net::Error>),
|
||||||
SearchRequest {
|
|
||||||
query: String,
|
|
||||||
page: usize,
|
|
||||||
results_per_page: usize,
|
|
||||||
},
|
|
||||||
SearchResult(Result<shared::SearchResult, gloo_net::Error>),
|
|
||||||
ShowPrettyRequest(String),
|
|
||||||
ShowPrettyResult(Result<ThreadSet, gloo_net::Error>),
|
|
||||||
NextPage,
|
NextPage,
|
||||||
PreviousPage,
|
PreviousPage,
|
||||||
UpdateQuery(String),
|
UpdateQuery(String),
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use seed_hooks::{state_access::CloneState, topo, use_state};
|
|||||||
use crate::{
|
use crate::{
|
||||||
api::urls,
|
api::urls,
|
||||||
state::{Context, Model, Msg, Tag},
|
state::{Context, Model, Msg, Tag},
|
||||||
view::{self, legacy, view_header, view_search_results},
|
view::{self, view_header, view_search_results},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[topo::nested]
|
#[topo::nested]
|
||||||
@ -12,9 +12,7 @@ pub(super) fn view(model: &Model) -> Node<Msg> {
|
|||||||
// Do two queries, one without `unread` so it loads fast, then a second with unread.
|
// Do two queries, one without `unread` so it loads fast, then a second with unread.
|
||||||
let content = match &model.context {
|
let content = match &model.context {
|
||||||
Context::None => div![h1!["Loading"]],
|
Context::None => div![h1!["Loading"]],
|
||||||
Context::Thread(thread_set) => legacy::thread(thread_set),
|
|
||||||
Context::ThreadResult(thread) => view::thread(thread),
|
Context::ThreadResult(thread) => view::thread(thread),
|
||||||
Context::Search(search_results) => legacy::search_results(&model.query, search_results),
|
|
||||||
Context::SearchResult {
|
Context::SearchResult {
|
||||||
query,
|
query,
|
||||||
results,
|
results,
|
||||||
|
|||||||
@ -1,270 +0,0 @@
|
|||||||
use notmuch::{Content, Part, ThreadNode, ThreadSet};
|
|
||||||
use seed::{prelude::*, *};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
api,
|
|
||||||
api::urls,
|
|
||||||
consts::SEARCH_RESULTS_PER_PAGE,
|
|
||||||
state::Msg,
|
|
||||||
view::{human_age, pretty_authors, set_title, tags_chiclet},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(super) fn search_results(query: &str, search_results: &shared::SearchResult) -> Node<Msg> {
|
|
||||||
if query.is_empty() {
|
|
||||||
set_title("all mail");
|
|
||||||
} else {
|
|
||||||
set_title(query);
|
|
||||||
}
|
|
||||||
let summaries = &search_results.summary.0;
|
|
||||||
let rows = summaries.iter().map(|r| {
|
|
||||||
let tid = r.thread.clone();
|
|
||||||
let datetime = human_age(r.timestamp as i64);
|
|
||||||
tr![
|
|
||||||
td![
|
|
||||||
C!["from"],
|
|
||||||
pretty_authors(&r.authors),
|
|
||||||
IF!(r.total>1 => small![" ", r.total.to_string()]),
|
|
||||||
],
|
|
||||||
td![
|
|
||||||
C!["subject"],
|
|
||||||
tags_chiclet(&r.tags, false),
|
|
||||||
" ",
|
|
||||||
a![
|
|
||||||
C!["has-text-light"],
|
|
||||||
attrs! {
|
|
||||||
At::Href => urls::thread(&tid)
|
|
||||||
},
|
|
||||||
&r.subject,
|
|
||||||
]
|
|
||||||
],
|
|
||||||
td![C!["date"], datetime]
|
|
||||||
]
|
|
||||||
});
|
|
||||||
let first = search_results.page * search_results.results_per_page;
|
|
||||||
div![
|
|
||||||
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]
|
|
||||||
],
|
|
||||||
search_pager(first, summaries.len(), search_results.total)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_pager(start: usize, count: usize, total: usize) -> Node<Msg> {
|
|
||||||
let is_first = start <= 0;
|
|
||||||
let is_last = (start + SEARCH_RESULTS_PER_PAGE) >= total;
|
|
||||||
nav![
|
|
||||||
C!["pagination"],
|
|
||||||
a![
|
|
||||||
C!["pagination-previous", "button",],
|
|
||||||
IF!(is_first => attrs!{ At::Disabled=>true }),
|
|
||||||
"<",
|
|
||||||
ev(Ev::Click, |_| Msg::PreviousPage)
|
|
||||||
],
|
|
||||||
a![
|
|
||||||
C!["pagination-next", "button", IF!(is_last => "is-static")],
|
|
||||||
IF!(is_last => attrs!{ At::Disabled=>true }),
|
|
||||||
">",
|
|
||||||
ev(Ev::Click, |_| Msg::NextPage)
|
|
||||||
],
|
|
||||||
ul![
|
|
||||||
C!["pagination-list"],
|
|
||||||
li![format!("{} - {} of {}", start, start + count, total)],
|
|
||||||
],
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn thread(thread_set: &ThreadSet) -> Node<Msg> {
|
|
||||||
assert_eq!(thread_set.0.len(), 1);
|
|
||||||
let thread = &thread_set.0[0];
|
|
||||||
assert_eq!(thread.0.len(), 1);
|
|
||||||
let thread_node = &thread.0[0];
|
|
||||||
let subject = first_subject(&thread_node).unwrap_or("<No subject>".to_string());
|
|
||||||
set_title(&subject);
|
|
||||||
div![
|
|
||||||
C!["container"],
|
|
||||||
h1![C!["title"], subject],
|
|
||||||
view_message(&thread_node),
|
|
||||||
a![
|
|
||||||
attrs! {At::Href=>api::original(&thread_node.0.as_ref().expect("message missing").id)},
|
|
||||||
"Original"
|
|
||||||
],
|
|
||||||
]
|
|
||||||
}
|
|
||||||
pub(super) fn mobile_search_results(
|
|
||||||
query: &str,
|
|
||||||
search_results: &shared::SearchResult,
|
|
||||||
) -> Node<Msg> {
|
|
||||||
if query.is_empty() {
|
|
||||||
set_title("all mail");
|
|
||||||
} else {
|
|
||||||
set_title(query);
|
|
||||||
}
|
|
||||||
let summaries = &search_results.summary.0;
|
|
||||||
let rows = summaries.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)),
|
|
||||||
]
|
|
||||||
*/
|
|
||||||
let tid = r.thread.clone();
|
|
||||||
let datetime = human_age(r.timestamp as i64);
|
|
||||||
a![
|
|
||||||
C!["has-text-light"],
|
|
||||||
attrs! {
|
|
||||||
At::Href => urls::thread(&tid)
|
|
||||||
},
|
|
||||||
div![
|
|
||||||
C!["row"],
|
|
||||||
div![C!["subject"], &r.subject],
|
|
||||||
span![C!["from", "is-size-7"], pretty_authors(&r.authors)],
|
|
||||||
div![
|
|
||||||
span![C!["is-size-7"], tags_chiclet(&r.tags, true)],
|
|
||||||
span![C!["is-size-7", "float-right", "date"], datetime]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
});
|
|
||||||
let first = search_results.page * search_results.results_per_page;
|
|
||||||
div![
|
|
||||||
C!["search-results"],
|
|
||||||
search_pager(first, summaries.len(), search_results.total),
|
|
||||||
rows,
|
|
||||||
search_pager(first, summaries.len(), search_results.total)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
fn view_message(thread: &ThreadNode) -> Node<Msg> {
|
|
||||||
let message = thread.0.as_ref().expect("ThreadNode missing Message");
|
|
||||||
let children = &thread.1;
|
|
||||||
div![
|
|
||||||
C!["message"],
|
|
||||||
/* TODO(wathiede): collect all the tags and show them here. */
|
|
||||||
/* TODO(wathiede): collect all the attachments from all the subparts */
|
|
||||||
div![C!["header"], "From: ", &message.headers.from],
|
|
||||||
div![C!["header"], "Date: ", &message.headers.date],
|
|
||||||
div![C!["header"], "To: ", &message.headers.to],
|
|
||||||
div![
|
|
||||||
C!["body"],
|
|
||||||
match &message.body {
|
|
||||||
Some(body) => view_body(body.as_slice()),
|
|
||||||
None => div!["<no body>"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
children.iter().map(view_message)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn view_body(body: &[Part]) -> Node<Msg> {
|
|
||||||
div![body.iter().map(view_part)]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn view_text_plain(content: &Option<Content>) -> Node<Msg> {
|
|
||||||
match &content {
|
|
||||||
Some(Content::String(content)) => p![C!["view-part-text-plain"], content],
|
|
||||||
_ => div![
|
|
||||||
C!["error"],
|
|
||||||
format!("Unhandled content enum for text/plain"),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn view_part(part: &Part) -> Node<Msg> {
|
|
||||||
match part.content_type.as_str() {
|
|
||||||
"text/plain" => view_text_plain(&part.content),
|
|
||||||
"text/html" => {
|
|
||||||
if let Some(Content::String(html)) = &part.content {
|
|
||||||
/* Build problems w/ css_inline. TODO(wathiede): move to server
|
|
||||||
let inliner = css_inline::CSSInliner::options()
|
|
||||||
.load_remote_stylesheets(false)
|
|
||||||
.remove_style_tags(true)
|
|
||||||
.build();
|
|
||||||
let inlined = inliner.inline(html).expect("failed to inline CSS");
|
|
||||||
*/
|
|
||||||
|
|
||||||
return div![C!["view-part-text-html"], raw![&html]];
|
|
||||||
} else {
|
|
||||||
div![
|
|
||||||
C!["error"],
|
|
||||||
format!("Unhandled content enum for multipart/mixed"),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/MIME#alternative
|
|
||||||
// RFC1341 states: In general, user agents that compose multipart/alternative entities
|
|
||||||
// should place the body parts in increasing order of preference, that is, with the
|
|
||||||
// preferred format last.
|
|
||||||
"multipart/alternative" => {
|
|
||||||
if let Some(Content::Multipart(parts)) = &part.content {
|
|
||||||
for part in parts.iter().rev() {
|
|
||||||
if part.content_type == "text/html" {
|
|
||||||
if let Some(Content::String(html)) = &part.content {
|
|
||||||
/*
|
|
||||||
let inliner = css_inline::CSSInliner::options()
|
|
||||||
.load_remote_stylesheets(false)
|
|
||||||
.remove_style_tags(true)
|
|
||||||
.build();
|
|
||||||
let inlined = inliner.inline(html).expect("failed to inline CSS");
|
|
||||||
*/
|
|
||||||
return div![Node::from_html(None, &html)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if part.content_type == "text/plain" {
|
|
||||||
return view_text_plain(&part.content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div!["No known multipart/alternative parts"]
|
|
||||||
} else {
|
|
||||||
div![
|
|
||||||
C!["error"],
|
|
||||||
format!("multipart/alternative with non-multipart content"),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"multipart/mixed" => match &part.content {
|
|
||||||
Some(Content::Multipart(parts)) => div![parts.iter().map(view_part)],
|
|
||||||
_ => div![
|
|
||||||
C!["error"],
|
|
||||||
format!("Unhandled content enum for multipart/mixed"),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
_ => div![
|
|
||||||
C!["error"],
|
|
||||||
format!("Unhandled content type: {}", part.content_type)
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn first_subject(thread: &ThreadNode) -> Option<String> {
|
|
||||||
if let Some(msg) = &thread.0 {
|
|
||||||
return Some(msg.headers.subject.clone());
|
|
||||||
} else {
|
|
||||||
for tn in &thread.1 {
|
|
||||||
if let Some(s) = first_subject(&tn) {
|
|
||||||
return Some(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
@ -5,19 +5,14 @@ use crate::{
|
|||||||
graphql::front_page_query::*,
|
graphql::front_page_query::*,
|
||||||
state::{Context, Model, Msg},
|
state::{Context, Model, Msg},
|
||||||
view::{
|
view::{
|
||||||
self, human_age, legacy, pretty_authors, set_title, tags_chiclet, view_header,
|
self, human_age, pretty_authors, set_title, tags_chiclet, view_header, view_search_pager,
|
||||||
view_search_pager,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) fn view(model: &Model) -> Node<Msg> {
|
pub(super) fn view(model: &Model) -> Node<Msg> {
|
||||||
let content = match &model.context {
|
let content = match &model.context {
|
||||||
Context::None => div![h1!["Loading"]],
|
Context::None => div![h1!["Loading"]],
|
||||||
Context::Thread(thread_set) => legacy::thread(thread_set),
|
|
||||||
Context::ThreadResult(thread) => view::thread(thread),
|
Context::ThreadResult(thread) => view::thread(thread),
|
||||||
Context::Search(search_results) => {
|
|
||||||
legacy::mobile_search_results(&model.query, search_results)
|
|
||||||
}
|
|
||||||
Context::SearchResult {
|
Context::SearchResult {
|
||||||
query,
|
query,
|
||||||
results,
|
results,
|
||||||
|
|||||||
@ -15,13 +15,12 @@ use wasm_timer::Instant;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::urls,
|
api::urls,
|
||||||
consts::{SEARCH_RESULTS_PER_PAGE, USE_GRAPHQL},
|
consts::SEARCH_RESULTS_PER_PAGE,
|
||||||
graphql::{front_page_query::*, show_thread_query::*},
|
graphql::{front_page_query::*, show_thread_query::*},
|
||||||
state::{Model, Msg, RefreshingState},
|
state::{Model, Msg, RefreshingState},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod desktop;
|
mod desktop;
|
||||||
mod legacy;
|
|
||||||
mod mobile;
|
mod mobile;
|
||||||
mod tablet;
|
mod tablet;
|
||||||
|
|
||||||
@ -47,10 +46,12 @@ fn tags_chiclet(tags: &[String], is_mobile: bool) -> impl Iterator<Item = Node<M
|
|||||||
"replied" => span![classes, style, i![C!["fa-solid", "fa-reply"]]],
|
"replied" => span![classes, style, i![C!["fa-solid", "fa-reply"]]],
|
||||||
_ => span![classes, style, &tag],
|
_ => span![classes, style, &tag],
|
||||||
},
|
},
|
||||||
ev(Ev::Click, move |_| Msg::SearchRequest {
|
ev(Ev::Click, move |_| Msg::FrontPageRequest {
|
||||||
query: format!("tag:{tag}"),
|
query: format!("tag:{tag}"),
|
||||||
page: 0,
|
after: None,
|
||||||
results_per_page: SEARCH_RESULTS_PER_PAGE,
|
before: None,
|
||||||
|
first: None,
|
||||||
|
last: None,
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@ -532,30 +533,10 @@ fn view_header(query: &str, refresh_request: &RefreshingState) -> Node<Msg> {
|
|||||||
At::AutoFocus => true.as_at_value();
|
At::AutoFocus => true.as_at_value();
|
||||||
At::Value => query,
|
At::Value => query,
|
||||||
},
|
},
|
||||||
input_ev(Ev::Input, |q| if USE_GRAPHQL {
|
input_ev(Ev::Input, |q| Msg::UpdateQuery(q)),
|
||||||
Msg::UpdateQuery(q)
|
|
||||||
} else {
|
|
||||||
Msg::SearchRequest {
|
|
||||||
query: Url::encode_uri_component(if q.is_empty() {
|
|
||||||
"*".to_string()
|
|
||||||
} else {
|
|
||||||
q
|
|
||||||
}),
|
|
||||||
page: 0,
|
|
||||||
results_per_page: SEARCH_RESULTS_PER_PAGE,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
// Send search on enter.
|
// Send search on enter.
|
||||||
keyboard_ev(Ev::KeyUp, move |e| if e.key_code() == 0x0d {
|
keyboard_ev(Ev::KeyUp, move |e| if e.key_code() == 0x0d {
|
||||||
if USE_GRAPHQL {
|
|
||||||
Msg::SearchQuery(query)
|
Msg::SearchQuery(query)
|
||||||
} else {
|
|
||||||
Msg::SearchRequest {
|
|
||||||
query: Url::encode_uri_component(query),
|
|
||||||
page: 0,
|
|
||||||
results_per_page: SEARCH_RESULTS_PER_PAGE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Msg::Noop
|
Msg::Noop
|
||||||
}),
|
}),
|
||||||
@ -584,7 +565,7 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
.expect("window width")
|
.expect("window width")
|
||||||
.as_f64()
|
.as_f64()
|
||||||
.expect("window width f64");
|
.expect("window width f64");
|
||||||
let h = win
|
let _h = win
|
||||||
.inner_height()
|
.inner_height()
|
||||||
.expect("window height")
|
.expect("window height")
|
||||||
.as_f64()
|
.as_f64()
|
||||||
|
|||||||
@ -9,9 +9,7 @@ pub(super) fn view(model: &Model) -> Node<Msg> {
|
|||||||
// Do two queries, one without `unread` so it loads fast, then a second with unread.
|
// Do two queries, one without `unread` so it loads fast, then a second with unread.
|
||||||
let content = match &model.context {
|
let content = match &model.context {
|
||||||
Context::None => div![h1!["Loading"]],
|
Context::None => div![h1!["Loading"]],
|
||||||
Context::Thread(_) => unimplemented!("tablet legacy thread view"),
|
|
||||||
Context::ThreadResult(thread) => view::thread(thread),
|
Context::ThreadResult(thread) => view::thread(thread),
|
||||||
Context::Search(_) => unimplemented!("tablet legacy search results view"),
|
|
||||||
Context::SearchResult {
|
Context::SearchResult {
|
||||||
query,
|
query,
|
||||||
results,
|
results,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user