Compare commits

..

No commits in common. "9c56fde0b6bed6f15d43c92bde61ab7c75ecef6c" and "59648a1b25cbf65f66c3dbe94e87ad561fa5edf0" have entirely different histories.

14 changed files with 55 additions and 287 deletions

10
Cargo.lock generated
View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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"
}

View File

@ -1,6 +1,6 @@
[package]
name = "letterbox-server"
version = "0.0.108"
version = "0.0.107"
edition = "2021"
default-run = "letterbox-server"

View File

@ -1,3 +0,0 @@
DROP TABLE IF EXISTS email_address;
DROP TABLE IF EXISTS photo;
DROP TABLE IF EXISTS google_person;

View File

@ -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)
);

View File

@ -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?)
}
}
}

View File

@ -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))
}

View File

@ -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

View File

@ -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>"]

View File

@ -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,

View File

@ -21,7 +21,6 @@ query ShowThreadQuery($threadId: String!) {
from {
name
addr
photoUrl
}
to {
name

View File

@ -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"],