Compare commits
No commits in common. "9c56fde0b6bed6f15d43c92bde61ab7c75ecef6c" and "59648a1b25cbf65f66c3dbe94e87ad561fa5edf0" have entirely different histories.
9c56fde0b6
...
59648a1b25
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -2907,7 +2907,7 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "letterbox"
|
||||
version = "0.0.108"
|
||||
version = "0.0.107"
|
||||
dependencies = [
|
||||
"build-info",
|
||||
"build-info-build",
|
||||
@ -2933,7 +2933,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "letterbox-server"
|
||||
version = "0.0.108"
|
||||
version = "0.0.107"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"anyhow",
|
||||
@ -3452,7 +3452,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "notmuch"
|
||||
version = "0.0.108"
|
||||
version = "0.0.107"
|
||||
dependencies = [
|
||||
"itertools 0.10.5",
|
||||
"log",
|
||||
@ -4247,7 +4247,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "procmail2notmuch"
|
||||
version = "0.0.108"
|
||||
version = "0.0.107"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
]
|
||||
@ -5326,7 +5326,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "shared"
|
||||
version = "0.0.108"
|
||||
version = "0.0.107"
|
||||
dependencies = [
|
||||
"build-info",
|
||||
"notmuch",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "notmuch"
|
||||
version = "0.0.108"
|
||||
version = "0.0.107"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "procmail2notmuch"
|
||||
version = "0.0.108"
|
||||
version = "0.0.107"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\nSELECT\n url\nFROM email_photo ep\nJOIN email_address ea\nON ep.id = ea.email_photo_id\nWHERE\n address = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "url",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "126e16a4675e8d79f330b235f9e1b8614ab1e1526e4e69691c5ebc70d54a42ef"
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "letterbox-server"
|
||||
version = "0.0.108"
|
||||
version = "0.0.107"
|
||||
edition = "2021"
|
||||
default-run = "letterbox-server"
|
||||
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
DROP TABLE IF EXISTS email_address;
|
||||
DROP TABLE IF EXISTS photo;
|
||||
DROP TABLE IF EXISTS google_person;
|
||||
@ -1,19 +0,0 @@
|
||||
-- Add up migration script here
|
||||
CREATE TABLE IF NOT EXISTS google_person (
|
||||
id SERIAL PRIMARY KEY,
|
||||
resource_name TEXT NOT NULL UNIQUE,
|
||||
display_name TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS email_photo (
|
||||
id SERIAL PRIMARY KEY,
|
||||
google_person_id INTEGER REFERENCES google_person (id) UNIQUE,
|
||||
url TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS email_address (
|
||||
id SERIAL PRIMARY KEY,
|
||||
address TEXT NOT NULL UNIQUE,
|
||||
email_photo_id INTEGER REFERENCES email_photo (id),
|
||||
google_person_id INTEGER REFERENCES google_person (id)
|
||||
);
|
||||
@ -237,7 +237,6 @@ impl Body {
|
||||
pub struct Email {
|
||||
pub name: Option<String>,
|
||||
pub addr: Option<String>,
|
||||
pub photo_url: Option<String>,
|
||||
}
|
||||
|
||||
impl fmt::Display for Email {
|
||||
@ -423,7 +422,6 @@ impl QueryRoot {
|
||||
}
|
||||
|
||||
let mut connection = Connection::new(has_previous_page, has_next_page);
|
||||
// Set starting offset as the value from cursor to preserve state if no results from a corpus survived the truncation
|
||||
let mut newsreader_offset =
|
||||
after.as_ref().map(|sc| sc.newsreader_offset).unwrap_or(0);
|
||||
let mut notmuch_offset = after.as_ref().map(|sc| sc.notmuch_offset).unwrap_or(0);
|
||||
@ -489,7 +487,7 @@ impl QueryRoot {
|
||||
if newsreader::is_newsreader_thread(&thread_id) {
|
||||
Ok(newsreader::thread(config, pool, thread_id).await?)
|
||||
} else {
|
||||
Ok(nm::thread(nm, pool, thread_id, debug_content_tree).await?)
|
||||
Ok(nm::thread(nm, thread_id, debug_content_tree).await?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,8 +9,6 @@ use log::{error, info, warn};
|
||||
use mailparse::{parse_content_type, parse_mail, MailHeader, MailHeaderMap, ParsedMail};
|
||||
use memmap::MmapOptions;
|
||||
use notmuch::Notmuch;
|
||||
use rocket::http::uri::error::PathError;
|
||||
use sqlx::PgPool;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{
|
||||
@ -149,7 +147,6 @@ pub fn tags(nm: &Notmuch, needs_unread: bool) -> Result<Vec<Tag>, ServerError> {
|
||||
#[instrument(name="nm::thread", skip_all, fields(thread_id=thread_id))]
|
||||
pub async fn thread(
|
||||
nm: &Notmuch,
|
||||
pool: &PgPool,
|
||||
thread_id: String,
|
||||
debug_content_tree: bool,
|
||||
) -> Result<Thread, ServerError> {
|
||||
@ -162,7 +159,7 @@ pub async fn thread(
|
||||
let mmap = unsafe { MmapOptions::new().map(&file)? };
|
||||
let m = parse_mail(&mmap)?;
|
||||
let from = email_addresses(&path, &m, "from")?;
|
||||
let mut from = match from.len() {
|
||||
let from = match from.len() {
|
||||
0 => None,
|
||||
1 => from.into_iter().next(),
|
||||
_ => {
|
||||
@ -174,16 +171,6 @@ pub async fn thread(
|
||||
from.into_iter().next()
|
||||
}
|
||||
};
|
||||
match from.as_mut() {
|
||||
Some(from) => {
|
||||
if let Some(addr) = from.addr.as_mut() {
|
||||
let photo_url = photo_url_for_email_address(&pool, &addr).await?;
|
||||
from.photo_url = photo_url;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let to = email_addresses(&path, &m, "to")?;
|
||||
let cc = email_addresses(&path, &m, "cc")?;
|
||||
let subject = m.headers.get_first_value("subject");
|
||||
@ -342,7 +329,6 @@ fn email_addresses(
|
||||
mailparse::MailAddr::Single(s) => addrs.push(Email {
|
||||
name: s.display_name,
|
||||
addr: Some(s.addr),
|
||||
photo_url: None,
|
||||
}), //println!("Single: {s}"),
|
||||
}
|
||||
}
|
||||
@ -357,14 +343,12 @@ fn email_addresses(
|
||||
addrs.push(Email {
|
||||
name: Some(name.to_string()),
|
||||
addr: Some(addr.to_string()),
|
||||
photo_url: None,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
addrs.push(Email {
|
||||
name: Some(v),
|
||||
addr: None,
|
||||
photo_url: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -893,24 +877,3 @@ pub async fn set_read_status<'ctx>(
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn photo_url_for_email_address(
|
||||
pool: &PgPool,
|
||||
addr: &str,
|
||||
) -> Result<Option<String>, ServerError> {
|
||||
let row = sqlx::query!(
|
||||
r#"
|
||||
SELECT
|
||||
url
|
||||
FROM email_photo ep
|
||||
JOIN email_address ea
|
||||
ON ep.id = ea.email_photo_id
|
||||
WHERE
|
||||
address = $1
|
||||
"#,
|
||||
addr
|
||||
)
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
Ok(row.map(|r| r.url))
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "shared"
|
||||
version = "0.0.108"
|
||||
version = "0.0.107"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
version = "0.0.108"
|
||||
version = "0.0.107"
|
||||
name = "letterbox"
|
||||
repository = "https://github.com/seed-rs/seed-quickstart"
|
||||
authors = ["Bill Thiede <git@xinu.tv>"]
|
||||
|
||||
@ -2,28 +2,6 @@
|
||||
"data": {
|
||||
"__schema": {
|
||||
"directives": [
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
"defaultValue": "\"No longer supported\"",
|
||||
"description": "A reason for why it is deprecated, formatted using Markdown syntax",
|
||||
"name": "reason",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Marks an element of a GraphQL schema as no longer supported.",
|
||||
"locations": [
|
||||
"FIELD_DEFINITION",
|
||||
"ARGUMENT_DEFINITION",
|
||||
"INPUT_FIELD_DEFINITION",
|
||||
"ENUM_VALUE"
|
||||
],
|
||||
"name": "deprecated"
|
||||
},
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
@ -49,14 +27,6 @@
|
||||
],
|
||||
"name": "include"
|
||||
},
|
||||
{
|
||||
"args": [],
|
||||
"description": "Indicates that an Input Object is a OneOf Input Object (and thus requires\n exactly one of its field be provided)",
|
||||
"locations": [
|
||||
"INPUT_OBJECT"
|
||||
],
|
||||
"name": "oneOf"
|
||||
},
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
@ -81,29 +51,6 @@
|
||||
"INLINE_FRAGMENT"
|
||||
],
|
||||
"name": "skip"
|
||||
},
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"description": "URL that specifies the behavior of this scalar.",
|
||||
"name": "url",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Provides a scalar specification URL for specifying the behavior of custom scalar types.",
|
||||
"locations": [
|
||||
"SCALAR"
|
||||
],
|
||||
"name": "specifiedBy"
|
||||
}
|
||||
],
|
||||
"mutationType": {
|
||||
@ -364,18 +311,6 @@
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"args": [],
|
||||
"deprecationReason": null,
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
"name": "photoUrl",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
@ -945,6 +880,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"args": [],
|
||||
"deprecationReason": null,
|
||||
"description": "Drop and recreate tantivy index. Warning this is slow",
|
||||
"isDeprecated": false,
|
||||
"name": "dropAndLoadIndex",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"args": [],
|
||||
"deprecationReason": null,
|
||||
@ -1888,22 +1839,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
"defaultValue": "false",
|
||||
"description": null,
|
||||
"name": "includeDeprecated",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"args": [],
|
||||
"deprecationReason": null,
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
@ -2174,22 +2110,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
"defaultValue": "false",
|
||||
"description": null,
|
||||
"name": "includeDeprecated",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"args": [],
|
||||
"deprecationReason": null,
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
@ -2322,34 +2243,6 @@
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"args": [],
|
||||
"deprecationReason": null,
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
"name": "isDeprecated",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"args": [],
|
||||
"deprecationReason": null,
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
"name": "deprecationReason",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
@ -2362,22 +2255,6 @@
|
||||
"description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes\nall available types and directives on the server, as well as the entry\npoints for query, mutation, and subscription operations.",
|
||||
"enumValues": null,
|
||||
"fields": [
|
||||
{
|
||||
"args": [],
|
||||
"deprecationReason": null,
|
||||
"description": "description of __Schema for newer graphiql introspection schema\nrequirements",
|
||||
"isDeprecated": false,
|
||||
"name": "description",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"args": [],
|
||||
"deprecationReason": null,
|
||||
@ -2628,22 +2505,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
"defaultValue": "false",
|
||||
"description": null,
|
||||
"name": "includeDeprecated",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"args": [],
|
||||
"deprecationReason": null,
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
|
||||
@ -21,7 +21,6 @@ query ShowThreadQuery($threadId: String!) {
|
||||
from {
|
||||
name
|
||||
addr
|
||||
photoUrl
|
||||
}
|
||||
to {
|
||||
name
|
||||
|
||||
@ -375,21 +375,7 @@ fn has_unread(tags: &[String]) -> bool {
|
||||
tags.contains(&String::from("unread"))
|
||||
}
|
||||
|
||||
fn render_avatar(photo_url: Option<String>, from: &str, big: bool) -> Node<Msg> {
|
||||
let size = if big {
|
||||
C!["w-16", "h-16", "text-4xl"]
|
||||
} else {
|
||||
C!["w-8", "h-8", "text-l"]
|
||||
};
|
||||
if let Some(photo_url) = photo_url {
|
||||
return div![
|
||||
size,
|
||||
img![attrs! {
|
||||
At::Src => photo_url,
|
||||
}]
|
||||
];
|
||||
}
|
||||
|
||||
fn render_avatar(_avatar: Option<String>, from: &str, big: bool) -> Node<Msg> {
|
||||
let initials: String = from
|
||||
.to_lowercase()
|
||||
.trim()
|
||||
@ -400,7 +386,11 @@ fn render_avatar(photo_url: Option<String>, from: &str, big: bool) -> Node<Msg>
|
||||
.take(2)
|
||||
.collect();
|
||||
let from_color = compute_color(from);
|
||||
|
||||
let size = if big {
|
||||
C!["w-16", "h-16", "text-4xl"]
|
||||
} else {
|
||||
C!["w-8", "h-8", "text-l"]
|
||||
};
|
||||
div![
|
||||
C![
|
||||
"[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]",
|
||||
@ -426,22 +416,22 @@ fn copy_text_widget(text: &str) -> Node<Msg> {
|
||||
]
|
||||
}
|
||||
fn render_open_header(msg: &ShowThreadQueryThreadOnEmailThreadMessages) -> Node<Msg> {
|
||||
let (from, from_detail, photo_url) = match &msg.from {
|
||||
let (from, from_detail) = match &msg.from {
|
||||
Some(ShowThreadQueryThreadOnEmailThreadMessagesFrom {
|
||||
name: Some(name),
|
||||
addr,
|
||||
photo_url,
|
||||
}) => (name.to_string(), addr.clone(), photo_url.clone()),
|
||||
}) => (name.to_string(), addr.clone()),
|
||||
Some(ShowThreadQueryThreadOnEmailThreadMessagesFrom {
|
||||
addr: Some(addr),
|
||||
photo_url,
|
||||
..
|
||||
}) => (addr.to_string(), None, photo_url.clone()),
|
||||
_ => (String::from("UNKNOWN"), None, None),
|
||||
addr: Some(addr), ..
|
||||
}) => (addr.to_string(), None),
|
||||
_ => (String::from("UNKNOWN"), None),
|
||||
};
|
||||
// TODO(wathiede): get this from server
|
||||
let avatar: Option<String> = None;
|
||||
//let avatar: Option<String> = Some(String::from("https://bulma.io/images/placeholders/64x64.png"));
|
||||
let id = msg.id.clone();
|
||||
let is_unread = has_unread(&msg.tags);
|
||||
let avatar = render_avatar(photo_url, &from, true);
|
||||
let avatar = render_avatar(avatar, &from, true);
|
||||
let unknown = "UNKNOWN".to_string();
|
||||
div![
|
||||
C!["flex", "p-4"],
|
||||
@ -523,22 +513,22 @@ fn render_open_header(msg: &ShowThreadQueryThreadOnEmailThreadMessages) -> Node<
|
||||
}
|
||||
|
||||
fn render_closed_header(msg: &ShowThreadQueryThreadOnEmailThreadMessages) -> Node<Msg> {
|
||||
let (from, from_detail, photo_url) = match &msg.from {
|
||||
let (from, from_detail) = match &msg.from {
|
||||
Some(ShowThreadQueryThreadOnEmailThreadMessagesFrom {
|
||||
name: Some(name),
|
||||
addr,
|
||||
photo_url,
|
||||
}) => (name.to_string(), addr.clone(), photo_url.clone()),
|
||||
}) => (name.to_string(), addr.clone()),
|
||||
Some(ShowThreadQueryThreadOnEmailThreadMessagesFrom {
|
||||
addr: Some(addr),
|
||||
photo_url,
|
||||
..
|
||||
}) => (addr.to_string(), None, photo_url.clone()),
|
||||
_ => (String::from("UNKNOWN"), None, None),
|
||||
addr: Some(addr), ..
|
||||
}) => (addr.to_string(), None),
|
||||
_ => (String::from("UNKNOWN"), None),
|
||||
};
|
||||
// TODO(wathiede): get this from server
|
||||
let avatar: Option<String> = None;
|
||||
//let avatar: Option<String> = Some(String::from("https://bulma.io/images/placeholders/64x64.png"));
|
||||
let id = msg.id.clone();
|
||||
let is_unread = has_unread(&msg.tags);
|
||||
let avatar = render_avatar(photo_url, &from, false);
|
||||
let avatar = render_avatar(avatar, &from, false);
|
||||
let unknown = "UNKNOWN".to_string();
|
||||
div![
|
||||
C!["flex", "p-4"],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user