Compare commits
No commits in common. "d8275debdc544cf3641abe3508a9c209068b7f2f" and "19ee6f338dd0ef96c3671609517692af60177b63" have entirely different histories.
d8275debdc
...
19ee6f338d
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -975,12 +975,10 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"itertools",
|
||||
"log 0.4.17",
|
||||
"notmuch",
|
||||
"seed",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wasm-bindgen-test",
|
||||
]
|
||||
|
||||
@ -1783,9 +1781,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.93"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
|
||||
checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
||||
@ -425,14 +425,14 @@ pub struct SearchTags(pub Vec<String>);
|
||||
pub struct ThreadSummary {
|
||||
pub thread: ThreadId,
|
||||
pub timestamp: UnixTime,
|
||||
/// user-friendly timestamp
|
||||
pub date_relative: String,
|
||||
/// number of matched messages
|
||||
/// user-friendly timestamp
|
||||
pub matched: isize,
|
||||
/// total messages in thread
|
||||
/// number of matched messages
|
||||
pub total: isize,
|
||||
/// comma-separated names with | between matched and unmatched
|
||||
/// total messages in thread
|
||||
pub authors: String,
|
||||
/// comma-separated names with | between matched and unmatched
|
||||
pub subject: String,
|
||||
pub tags: Vec<String>,
|
||||
|
||||
@ -541,8 +541,6 @@ impl Notmuch {
|
||||
Ok(BufReader::new(child.stdout.take().unwrap()).lines())
|
||||
}
|
||||
|
||||
// TODO(wathiede): implement tags() based on "notmuch search --output=tags '*'"
|
||||
|
||||
fn run_notmuch<I, S>(&self, args: I) -> Result<Vec<u8>, NotmuchError>
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
|
||||
@ -22,8 +22,6 @@ 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"}
|
||||
itertools = "0.10.5"
|
||||
serde_json = { version = "1.0.93", features = ["unbounded_depth"] }
|
||||
|
||||
[package.metadata.wasm-pack.profile.release]
|
||||
wasm-opt = ['-Os']
|
||||
|
||||
@ -6,15 +6,12 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="modulepreload" href="/pkg/package.js" as="script" type="text/javascript">
|
||||
<link rel="preload" href="/pkg/package_bg.wasm" as="fetch" type="application/wasm" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css" integrity="sha512-SzlrxWUlpfuzQ+pcUCosxcglQRNAq/DZjVsC0lE40xsADsfeQoEypE+enwcOiGjk/bSuGGKHEyjSoQ1zVisanQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
<style>
|
||||
.message {
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
.body {
|
||||
padding-bottom: 1em;
|
||||
border: 1px red solid;
|
||||
}
|
||||
.error {
|
||||
background-color: red;
|
||||
@ -26,14 +23,6 @@ iframe {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.index .from {
|
||||
width: 200px;
|
||||
}
|
||||
.index .subject {
|
||||
}
|
||||
.index .date {
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
101
web/src/lib.rs
101
web/src/lib.rs
@ -3,11 +3,9 @@
|
||||
// but some rules are too "annoying" or are not applicable for your case.)
|
||||
#![allow(clippy::wildcard_imports)]
|
||||
|
||||
use itertools::Itertools;
|
||||
use log::{debug, error, info, warn, Level};
|
||||
use notmuch::{Content, Part, SearchSummary, Thread, ThreadNode, ThreadSet};
|
||||
use seed::{prelude::*, *};
|
||||
use serde::de::Deserialize;
|
||||
|
||||
// ------ ------
|
||||
// Init
|
||||
@ -114,6 +112,7 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
|
||||
async fn search_request(query: &str) -> fetch::Result<SearchSummary> {
|
||||
info!("making search request for '{}'", query);
|
||||
Request::new(api::search(query))
|
||||
.method(Method::Get)
|
||||
.fetch()
|
||||
@ -137,17 +136,13 @@ mod api {
|
||||
}
|
||||
|
||||
async fn show_request(tid: &str) -> fetch::Result<ThreadSet> {
|
||||
let b = Request::new(api::show(tid))
|
||||
Request::new(api::show(tid))
|
||||
.method(Method::Get)
|
||||
.fetch()
|
||||
.await?
|
||||
.check_status()?
|
||||
.bytes()
|
||||
.await?;
|
||||
let mut deserializer = serde_json::Deserializer::from_slice(&b);
|
||||
deserializer.disable_recursion_limit();
|
||||
Ok(ThreadSet::deserialize(&mut deserializer)
|
||||
.map_err(|_| FetchError::JsonError(fetch::JsonError::Serde(JsValue::NULL)))?)
|
||||
.json()
|
||||
.await
|
||||
}
|
||||
|
||||
// ------ ------
|
||||
@ -272,42 +267,6 @@ fn set_title(title: &str) {
|
||||
seed::document().set_title(&format!("lb: {}", title));
|
||||
}
|
||||
|
||||
fn tags_chiclet(tags: &[String]) -> impl Iterator<Item = Node<Msg>> + '_ {
|
||||
tags.iter().map(|tag| match tag.as_str() {
|
||||
"attachment" => span![C!["tag"], "📎"],
|
||||
"replied" => span![C!["tag"], i![C!["fa-solid", "fa-reply"]]],
|
||||
_ => span![C!["tag"], tag],
|
||||
})
|
||||
}
|
||||
|
||||
fn pretty_authors(authors: &str) -> impl Iterator<Item = Node<Msg>> + '_ {
|
||||
let authors = authors.split(',');
|
||||
|
||||
/*
|
||||
if authors.len() == 1 {
|
||||
return authors.iter().filter_map(|author| {
|
||||
Some(span![
|
||||
attrs! {
|
||||
At::Title => author.trim()},
|
||||
author
|
||||
])
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
authors
|
||||
.filter_map(|author| {
|
||||
author.split_whitespace().nth(0).map(|first| {
|
||||
span![
|
||||
attrs! {
|
||||
At::Title => author.trim()},
|
||||
first
|
||||
]
|
||||
})
|
||||
})
|
||||
.intersperse(span![", "])
|
||||
}
|
||||
|
||||
fn view_search_results(query: &str, search_results: &SearchSummary) -> Node<Msg> {
|
||||
if query.is_empty() {
|
||||
set_title("all mail");
|
||||
@ -317,31 +276,20 @@ fn view_search_results(query: &str, search_results: &SearchSummary) -> Node<Msg>
|
||||
let rows = search_results.0.iter().map(|r| {
|
||||
let tid = r.thread.clone();
|
||||
tr![
|
||||
td![],
|
||||
td![
|
||||
C!["from"],
|
||||
pretty_authors(&r.authors),
|
||||
&r.authors,
|
||||
IF!(r.total>1 => small![" ", r.total.to_string()]),
|
||||
IF!(r.tags.contains(&"attachment".to_string()) => "📎"),
|
||||
],
|
||||
td![C!["subject"], tags_chiclet(&r.tags), " ", &r.subject],
|
||||
td![C!["date"], &r.date_relative],
|
||||
td![&r.subject],
|
||||
td![&r.date_relative],
|
||||
ev(Ev::Click, move |_| Msg::ShowRequest(tid)),
|
||||
]
|
||||
});
|
||||
div![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]
|
||||
tr![th!["tid"], th!["From"], th!["Subject"], th!["Date"]],
|
||||
rows
|
||||
]]
|
||||
}
|
||||
|
||||
@ -395,25 +343,13 @@ fn view_debug_thread_node(thread_node: &ThreadNode) -> Node<Msg> {
|
||||
|
||||
fn view_header(query: &str) -> Node<Msg> {
|
||||
let query = query.to_string();
|
||||
nav![
|
||||
C!["navbar"],
|
||||
attrs! {At::Role=>"navigation"},
|
||||
div![
|
||||
C!["navbar-menu"],
|
||||
div![
|
||||
C!["navbar-start"],
|
||||
a![
|
||||
C!["navbar-item", "button",],
|
||||
button![
|
||||
"Unread",
|
||||
ev(Ev::Click, |_| Msg::SearchRequest("is:unread".to_string())),
|
||||
],
|
||||
a![
|
||||
C!["navbar-item", "button"],
|
||||
"All",
|
||||
ev(Ev::Click, |_| Msg::SearchRequest("".to_string())),
|
||||
],
|
||||
button!["All", ev(Ev::Click, |_| Msg::SearchRequest("".to_string())),],
|
||||
input![
|
||||
C!["navbar-item", "input"],
|
||||
attrs! {
|
||||
At::Placeholder => "Search";
|
||||
At::AutoFocus => true.as_at_value();
|
||||
@ -426,25 +362,18 @@ fn view_header(query: &str) -> Node<Msg> {
|
||||
} else {
|
||||
Msg::Noop
|
||||
}),
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
// `view` describes what to display.
|
||||
fn view(model: &Model) -> Node<Msg> {
|
||||
info!("view called");
|
||||
let content = match &model.context {
|
||||
Context::None => div![h1!["Loading"]],
|
||||
Context::Thread(thread_set) => view_thread(thread_set),
|
||||
Context::Search(search_results) => view_search_results(&model.query, search_results),
|
||||
};
|
||||
div![section![
|
||||
C!["section"],
|
||||
view_header(&model.query),
|
||||
div![C!["container"], content]
|
||||
]]
|
||||
div![view_header(&model.query), content]
|
||||
}
|
||||
|
||||
// ------ ------
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user