web & server: add support for email photos

This commit is contained in:
2025-01-14 12:05:03 -08:00
parent f27f0deb38
commit 5a997e61da
7 changed files with 255 additions and 45 deletions

View File

@@ -2,6 +2,28 @@
"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": [
{
@@ -27,6 +49,14 @@
],
"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": [
{
@@ -51,6 +81,29 @@
"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": {
@@ -311,6 +364,18 @@
"name": "String",
"ofType": null
}
},
{
"args": [],
"deprecationReason": null,
"description": null,
"isDeprecated": false,
"name": "photoUrl",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
],
"inputFields": null,
@@ -880,22 +945,6 @@
}
}
},
{
"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,
@@ -1839,7 +1888,22 @@
}
},
{
"args": [],
"args": [
{
"defaultValue": "false",
"description": null,
"name": "includeDeprecated",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
}
}
],
"deprecationReason": null,
"description": null,
"isDeprecated": false,
@@ -2110,7 +2174,22 @@
}
},
{
"args": [],
"args": [
{
"defaultValue": "false",
"description": null,
"name": "includeDeprecated",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
}
}
],
"deprecationReason": null,
"description": null,
"isDeprecated": false,
@@ -2243,6 +2322,34 @@
"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,
@@ -2255,6 +2362,22 @@
"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,
@@ -2505,7 +2628,22 @@
}
},
{
"args": [],
"args": [
{
"defaultValue": "false",
"description": null,
"name": "includeDeprecated",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
}
}
],
"deprecationReason": null,
"description": null,
"isDeprecated": false,

View File

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

View File

@@ -375,7 +375,21 @@ fn has_unread(tags: &[String]) -> bool {
tags.contains(&String::from("unread"))
}
fn render_avatar(_avatar: Option<String>, from: &str, big: bool) -> Node<Msg> {
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,
}]
];
}
let initials: String = from
.to_lowercase()
.trim()
@@ -386,11 +400,7 @@ fn render_avatar(_avatar: 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%)]",
@@ -416,22 +426,22 @@ fn copy_text_widget(text: &str) -> Node<Msg> {
]
}
fn render_open_header(msg: &ShowThreadQueryThreadOnEmailThreadMessages) -> Node<Msg> {
let (from, from_detail) = match &msg.from {
let (from, from_detail, photo_url) = match &msg.from {
Some(ShowThreadQueryThreadOnEmailThreadMessagesFrom {
name: Some(name),
addr,
}) => (name.to_string(), addr.clone()),
photo_url,
}) => (name.to_string(), addr.clone(), photo_url.clone()),
Some(ShowThreadQueryThreadOnEmailThreadMessagesFrom {
addr: Some(addr), ..
}) => (addr.to_string(), None),
_ => (String::from("UNKNOWN"), None),
addr: Some(addr),
photo_url,
..
}) => (addr.to_string(), None, photo_url.clone()),
_ => (String::from("UNKNOWN"), None, 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(avatar, &from, true);
let avatar = render_avatar(photo_url, &from, true);
let unknown = "UNKNOWN".to_string();
div![
C!["flex", "p-4"],
@@ -513,22 +523,22 @@ fn render_open_header(msg: &ShowThreadQueryThreadOnEmailThreadMessages) -> Node<
}
fn render_closed_header(msg: &ShowThreadQueryThreadOnEmailThreadMessages) -> Node<Msg> {
let (from, from_detail) = match &msg.from {
let (from, from_detail, photo_url) = match &msg.from {
Some(ShowThreadQueryThreadOnEmailThreadMessagesFrom {
name: Some(name),
addr,
}) => (name.to_string(), addr.clone()),
photo_url,
}) => (name.to_string(), addr.clone(), photo_url.clone()),
Some(ShowThreadQueryThreadOnEmailThreadMessagesFrom {
addr: Some(addr), ..
}) => (addr.to_string(), None),
_ => (String::from("UNKNOWN"), None),
addr: Some(addr),
photo_url,
..
}) => (addr.to_string(), None, photo_url.clone()),
_ => (String::from("UNKNOWN"), None, 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(avatar, &from, false);
let avatar = render_avatar(photo_url, &from, false);
let unknown = "UNKNOWN".to_string();
div![
C!["flex", "p-4"],