Compare commits
No commits in common. "831466ddda06e047a7b1c4490c0a607e808f5b08" and "331fb4f11be3535604b9862b8b0a4aa7410b5aa8" have entirely different histories.
831466ddda
...
331fb4f11b
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"db_name": "PostgreSQL",
|
|
||||||
"query": "UPDATE\n post\nSET\n is_read = $1\nWHERE\n uid = $2\n",
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Bool",
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nullable": []
|
|
||||||
},
|
|
||||||
"hash": "b39147b9d06171cb742141eda4675688cb702fb284758b1224ed3aa2d7f3b3d9"
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
UPDATE
|
|
||||||
post
|
|
||||||
SET
|
|
||||||
is_read = $1
|
|
||||||
WHERE
|
|
||||||
uid = $2
|
|
||||||
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
use async_graphql::{
|
use async_graphql::{
|
||||||
connection::Connection, Context, EmptySubscription, Enum, Error, FieldResult, Object, Schema,
|
connection::{Connection},
|
||||||
SimpleObject, Union,
|
Context, EmptySubscription, Enum, Error, FieldResult, Object, Schema, SimpleObject, Union,
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
use notmuch::Notmuch;
|
use notmuch::Notmuch;
|
||||||
@ -272,14 +273,11 @@ impl Mutation {
|
|||||||
unread: bool,
|
unread: bool,
|
||||||
) -> Result<bool, Error> {
|
) -> Result<bool, Error> {
|
||||||
let nm = ctx.data_unchecked::<Notmuch>();
|
let nm = ctx.data_unchecked::<Notmuch>();
|
||||||
let pool = ctx.data_unchecked::<PgPool>();
|
info!("set_read_status({unread})");
|
||||||
|
if unread {
|
||||||
for q in query.split_whitespace() {
|
nm.tag_add("unread", &format!("{query}"))?;
|
||||||
if newsreader::is_newsreader_thread(&q) {
|
} else {
|
||||||
newsreader::set_read_status(pool, &q, unread).await?;
|
nm.tag_remove("unread", &format!("{query}"))?;
|
||||||
} else {
|
|
||||||
nm::set_read_status(nm, q, unread).await?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,6 +44,7 @@ pub async fn search(
|
|||||||
query: String,
|
query: String,
|
||||||
) -> Result<Connection<usize, ThreadSummary>, async_graphql::Error> {
|
) -> Result<Connection<usize, ThreadSummary>, async_graphql::Error> {
|
||||||
let query: Query = query.parse()?;
|
let query: Query = query.parse()?;
|
||||||
|
info!("news search query {query:?}");
|
||||||
let site = query.site.expect("search has no site");
|
let site = query.site.expect("search has no site");
|
||||||
connection::query(
|
connection::query(
|
||||||
after,
|
after,
|
||||||
@ -51,6 +52,7 @@ pub async fn search(
|
|||||||
first,
|
first,
|
||||||
last,
|
last,
|
||||||
|after: Option<usize>, before: Option<usize>, first, last| async move {
|
|after: Option<usize>, before: Option<usize>, first, last| async move {
|
||||||
|
info!("search page info {after:#?}, {before:#?}, {first:#?}, {last:#?}");
|
||||||
let default_page_size = 100;
|
let default_page_size = 100;
|
||||||
let (offset, limit) = match (after, before, first, last) {
|
let (offset, limit) = match (after, before, first, last) {
|
||||||
// Reasonable defaults
|
// Reasonable defaults
|
||||||
@ -84,6 +86,7 @@ pub async fn search(
|
|||||||
// The +1 is to see if there are more pages of data available.
|
// The +1 is to see if there are more pages of data available.
|
||||||
let limit = limit + 1;
|
let limit = limit + 1;
|
||||||
|
|
||||||
|
info!("search page offset {offset} limit {limit}");
|
||||||
let rows = sqlx::query_file!(
|
let rows = sqlx::query_file!(
|
||||||
"sql/threads.sql",
|
"sql/threads.sql",
|
||||||
site,
|
site,
|
||||||
@ -211,7 +214,7 @@ pub async fn thread(pool: &PgPool, thread_id: String) -> Result<Thread, ServerEr
|
|||||||
let html = r.summary.unwrap_or("NO SUMMARY".to_string());
|
let html = r.summary.unwrap_or("NO SUMMARY".to_string());
|
||||||
// TODO: add site specific cleanups. For example:
|
// TODO: add site specific cleanups. For example:
|
||||||
// * Grafana does <div class="image-wrapp"><img class="lazyload>"<img src="/media/...>"</img></div>
|
// * Grafana does <div class="image-wrapp"><img class="lazyload>"<img src="/media/...>"</img></div>
|
||||||
// * Some sites appear to be HTML encoded, unencode them, i.e. imperialviolent
|
// * Some sites appear to be HTML encoded, unencode them, i.e. imperialviolet
|
||||||
let html = sanitize_html(&html, "", &link)?;
|
let html = sanitize_html(&html, "", &link)?;
|
||||||
let body = Body::Html(Html {
|
let body = Body::Html(Html {
|
||||||
html,
|
html,
|
||||||
@ -250,7 +253,6 @@ pub async fn thread(pool: &PgPool, thread_id: String) -> Result<Thread, ServerEr
|
|||||||
struct Query {
|
struct Query {
|
||||||
unread_only: bool,
|
unread_only: bool,
|
||||||
site: Option<String>,
|
site: Option<String>,
|
||||||
uid: Option<String>,
|
|
||||||
remainder: Vec<String>,
|
remainder: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +261,6 @@ impl FromStr for Query {
|
|||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let mut unread_only = false;
|
let mut unread_only = false;
|
||||||
let mut site = None;
|
let mut site = None;
|
||||||
let mut uid = None;
|
|
||||||
let mut remainder = Vec::new();
|
let mut remainder = Vec::new();
|
||||||
let site_prefix = format!("tag:{TAG_PREFIX}");
|
let site_prefix = format!("tag:{TAG_PREFIX}");
|
||||||
for word in s.split_whitespace() {
|
for word in s.split_whitespace() {
|
||||||
@ -267,8 +268,6 @@ impl FromStr for Query {
|
|||||||
unread_only = true
|
unread_only = true
|
||||||
} else if word.starts_with(&site_prefix) {
|
} else if word.starts_with(&site_prefix) {
|
||||||
site = Some(word[site_prefix.len()..].to_string())
|
site = Some(word[site_prefix.len()..].to_string())
|
||||||
} else if word.starts_with(THREAD_PREFIX) {
|
|
||||||
uid = Some(word[THREAD_PREFIX.len()..].to_string())
|
|
||||||
} else {
|
} else {
|
||||||
remainder.push(word.to_string());
|
remainder.push(word.to_string());
|
||||||
}
|
}
|
||||||
@ -276,20 +275,7 @@ impl FromStr for Query {
|
|||||||
Ok(Query {
|
Ok(Query {
|
||||||
unread_only,
|
unread_only,
|
||||||
site,
|
site,
|
||||||
uid,
|
|
||||||
remainder,
|
remainder,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_read_status<'ctx>(
|
|
||||||
pool: &PgPool,
|
|
||||||
query: &str,
|
|
||||||
unread: bool,
|
|
||||||
) -> Result<bool, ServerError> {
|
|
||||||
let query: Query = query.parse()?;
|
|
||||||
sqlx::query_file!("sql/set_unread.sql", !unread, query.uid)
|
|
||||||
.execute(pool)
|
|
||||||
.await?;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -80,7 +80,7 @@ pub async fn search(
|
|||||||
.0
|
.0
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|ts| ThreadSummary {
|
.map(|ts| ThreadSummary {
|
||||||
thread: format!("thread:{}", ts.thread),
|
thread: ts.thread,
|
||||||
timestamp: ts.timestamp,
|
timestamp: ts.timestamp,
|
||||||
date_relative: ts.date_relative,
|
date_relative: ts.date_relative,
|
||||||
matched: ts.matched,
|
matched: ts.matched,
|
||||||
@ -248,7 +248,7 @@ pub async fn thread(
|
|||||||
// TODO(wathiede): parse message and fill out attachments
|
// TODO(wathiede): parse message and fill out attachments
|
||||||
let attachments = extract_attachments(&m, &id)?;
|
let attachments = extract_attachments(&m, &id)?;
|
||||||
messages.push(Message {
|
messages.push(Message {
|
||||||
id: format!("id:{id}"),
|
id,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
cc,
|
cc,
|
||||||
@ -752,16 +752,3 @@ fn render_content_type_tree(m: &ParsedMail) -> String {
|
|||||||
SKIP_HEADERS.join("\n ")
|
SKIP_HEADERS.join("\n ")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_read_status<'ctx>(
|
|
||||||
nm: &Notmuch,
|
|
||||||
query: &str,
|
|
||||||
unread: bool,
|
|
||||||
) -> Result<bool, ServerError> {
|
|
||||||
if unread {
|
|
||||||
nm.tag_add("unread", &format!("{query}"))?;
|
|
||||||
} else {
|
|
||||||
nm.tag_remove("unread", &format!("{query}"))?;
|
|
||||||
}
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -372,7 +372,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
{
|
{
|
||||||
let threads = selected_threads
|
let threads = selected_threads
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tid| tid.to_string())
|
.map(|tid| format!("thread:{tid}"))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" ");
|
.join(" ");
|
||||||
orders
|
orders
|
||||||
@ -387,7 +387,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
{
|
{
|
||||||
let threads = selected_threads
|
let threads = selected_threads
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tid| tid.to_string())
|
.map(|tid| format!("thread:{tid}"))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" ");
|
.join(" ");
|
||||||
orders
|
orders
|
||||||
@ -402,7 +402,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
{
|
{
|
||||||
let threads = selected_threads
|
let threads = selected_threads
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tid| tid.to_string())
|
.map(|tid| format!("thread:{tid}"))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" ");
|
.join(" ");
|
||||||
orders
|
orders
|
||||||
@ -417,7 +417,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
{
|
{
|
||||||
let threads = selected_threads
|
let threads = selected_threads
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tid| tid.to_string())
|
.map(|tid| format!("thread:{tid}"))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" ");
|
.join(" ");
|
||||||
orders
|
orders
|
||||||
|
|||||||
@ -73,7 +73,6 @@ fn removable_tags_chiclet<'a>(
|
|||||||
"is-grouped-multiline"
|
"is-grouped-multiline"
|
||||||
],
|
],
|
||||||
tags.iter().map(move |tag| {
|
tags.iter().map(move |tag| {
|
||||||
let thread_id = thread_id.to_string();
|
|
||||||
let hex = compute_color(tag);
|
let hex = compute_color(tag);
|
||||||
let style = style! {St::BackgroundColor=>hex};
|
let style = style! {St::BackgroundColor=>hex};
|
||||||
let classes = C!["tag", IF!(is_mobile => "is-small")];
|
let classes = C!["tag", IF!(is_mobile => "is-small")];
|
||||||
@ -82,6 +81,7 @@ fn removable_tags_chiclet<'a>(
|
|||||||
};
|
};
|
||||||
let tag = tag.clone();
|
let tag = tag.clone();
|
||||||
let rm_tag = tag.clone();
|
let rm_tag = tag.clone();
|
||||||
|
let thread_id = format!("thread:{thread_id}");
|
||||||
div![
|
div![
|
||||||
C!["control"],
|
C!["control"],
|
||||||
div![
|
div![
|
||||||
@ -592,7 +592,7 @@ fn render_open_header(msg: &ShowThreadQueryThreadMessages) -> Node<Msg> {
|
|||||||
],
|
],
|
||||||
ev(Ev::Click, move |e| {
|
ev(Ev::Click, move |e| {
|
||||||
e.stop_propagation();
|
e.stop_propagation();
|
||||||
Msg::SetUnread(id, !is_unread)
|
Msg::SetUnread(format!("id:{id}"), !is_unread)
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@ -664,7 +664,7 @@ fn render_closed_header(msg: &ShowThreadQueryThreadMessages) -> Node<Msg> {
|
|||||||
],
|
],
|
||||||
ev(Ev::Click, move |e| {
|
ev(Ev::Click, move |e| {
|
||||||
e.stop_propagation();
|
e.stop_propagation();
|
||||||
Msg::SetUnread(id, !is_unread)
|
Msg::SetUnread(format!("id:{id}"), !is_unread)
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@ -808,8 +808,7 @@ fn thread(
|
|||||||
});
|
});
|
||||||
let read_thread_id = thread.thread_id.clone();
|
let read_thread_id = thread.thread_id.clone();
|
||||||
let unread_thread_id = thread.thread_id.clone();
|
let unread_thread_id = thread.thread_id.clone();
|
||||||
let spam_add_thread_id = thread.thread_id.clone();
|
let spam_thread_id = thread.thread_id.clone();
|
||||||
let spam_unread_thread_id = thread.thread_id.clone();
|
|
||||||
div![
|
div![
|
||||||
C!["thread"],
|
C!["thread"],
|
||||||
h3![C!["is-size-5"], subject],
|
h3![C!["is-size-5"], subject],
|
||||||
@ -828,14 +827,20 @@ fn thread(
|
|||||||
attrs! {At::Title => "Mark as read"},
|
attrs! {At::Title => "Mark as read"},
|
||||||
span![C!["icon", "is-small"], i![C!["far", "fa-envelope-open"]]],
|
span![C!["icon", "is-small"], i![C!["far", "fa-envelope-open"]]],
|
||||||
IF!(show_icon_text=>span!["Read"]),
|
IF!(show_icon_text=>span!["Read"]),
|
||||||
ev(Ev::Click, move |_| Msg::SetUnread(read_thread_id, false)),
|
ev(Ev::Click, move |_| Msg::SetUnread(
|
||||||
|
format!("thread:{read_thread_id}"),
|
||||||
|
false
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
button![
|
button![
|
||||||
C!["button", "mark-unread"],
|
C!["button", "mark-unread"],
|
||||||
attrs! {At::Title => "Mark as unread"},
|
attrs! {At::Title => "Mark as unread"},
|
||||||
span![C!["icon", "is-small"], i![C!["far", "fa-envelope"]]],
|
span![C!["icon", "is-small"], i![C!["far", "fa-envelope"]]],
|
||||||
IF!(show_icon_text=>span!["Unread"]),
|
IF!(show_icon_text=>span!["Unread"]),
|
||||||
ev(Ev::Click, move |_| Msg::SetUnread(unread_thread_id, true)),
|
ev(Ev::Click, move |_| Msg::SetUnread(
|
||||||
|
format!("thread:{unread_thread_id}"),
|
||||||
|
true
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@ -849,8 +854,8 @@ fn thread(
|
|||||||
span![C!["icon", "is-small"], i![C!["far", "fa-hand"]]],
|
span![C!["icon", "is-small"], i![C!["far", "fa-hand"]]],
|
||||||
IF!(show_icon_text=>span!["Spam"]),
|
IF!(show_icon_text=>span!["Spam"]),
|
||||||
ev(Ev::Click, move |_| Msg::MultiMsg(vec![
|
ev(Ev::Click, move |_| Msg::MultiMsg(vec![
|
||||||
Msg::AddTag(spam_add_thread_id, "Spam".to_string()),
|
Msg::AddTag(format!("thread:{spam_thread_id}"), "Spam".to_string()),
|
||||||
Msg::SetUnread(spam_unread_thread_id, false)
|
Msg::SetUnread(format!("thread:{spam_thread_id}"), false)
|
||||||
])),
|
])),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user