Skip to content

Commit

Permalink
feat(term): render annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
ymgyt committed Apr 13, 2024
1 parent ddb1c0a commit 1f41872
Show file tree
Hide file tree
Showing 16 changed files with 297 additions and 18 deletions.
51 changes: 49 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions categories.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[categories]

[categories.rust]
# nf-md-language_rust 󱘗
# icon = "󱘗"

# nf-linux-ferris
icon = { symbol = "", color = { rgb = 0xF74C00 } }

[categories.database]
# nf-dev-database 
icon = { symbol = "\ue706" }

[categories.default]
# nf-md-note_text
icon = { symbol = "󰎞", color = { name = "dark gray" } }
1 change: 1 addition & 0 deletions crates/synd_term/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ serde = { workspace = true, features = ["derive"] }
serde_json = "1.0.111"
thiserror = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync", "time"] }
toml = { version = "0.8.12", default-features = true }
tracing = { workspace = true }
tracing-appender = "0.2.3"
tracing-subscriber = { workspace = true }
Expand Down
2 changes: 2 additions & 0 deletions crates/synd_term/gql/query.gql
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ fragment Entry on Entry {
fragment FeedMeta on FeedMeta {
title
url
requirement
category
}

fragment PageInfo on PageInfo {
Expand Down
24 changes: 24 additions & 0 deletions crates/synd_term/gql/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,30 @@
"ofType": null
}
}
},
{
"args": [],
"deprecationReason": null,
"description": "Requirement Level for the feed",
"isDeprecated": false,
"name": "requirement",
"type": {
"kind": "ENUM",
"name": "Requirement",
"ofType": null
}
},
{
"args": [],
"deprecationReason": null,
"description": null,
"isDeprecated": false,
"name": "category",
"type": {
"kind": "SCALAR",
"name": "Category",
"ofType": null
}
}
],
"inputFields": null,
Expand Down
5 changes: 4 additions & 1 deletion crates/synd_term/src/application/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
auth::{AuthenticationProvider, Credential},
client::{mutation::subscribe_feed::SubscribeFeedInput, Client},
command::Command,
config,
config::{self, Categories},
interact::Interactor,
job::Jobs,
keymap::{KeymapId, Keymaps},
Expand Down Expand Up @@ -79,6 +79,7 @@ pub struct Application {
idle_timer: Pin<Box<Sleep>>,
config: Config,
keymaps: Keymaps,
categories: Categories,

screen: Screen,
should_render: bool,
Expand Down Expand Up @@ -108,6 +109,7 @@ impl Application {
screen: Screen::Login,
config,
keymaps,
categories: Categories::default_toml(),
should_quit: false,
should_render: false,
}
Expand Down Expand Up @@ -433,6 +435,7 @@ impl Application {
let cx = ui::Context {
theme: &self.theme,
in_flight: &self.in_flight,
categories: &self.categories,
};
let root = Root::new(&self.components, cx);

Expand Down
37 changes: 34 additions & 3 deletions crates/synd_term/src/client/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub mod subscription {
#![allow(dead_code)]
use std::result::Result;
pub const OPERATION_NAME: &str = "Subscription";
pub const QUERY : & str = "query Subscription($after: String, $first: Int) {\n output: subscription {\n feeds(after: $after, first: $first) {\n nodes {\n ...Feed\n }\n pageInfo {\n ...PageInfo\n }\n }\n }\n}\n\nfragment Feed on Feed {\n id\n type\n title\n url\n updated\n websiteUrl\n description\n generator\n requirement\n category\n entries(first: 20) {\n nodes {\n ...EntryMeta\n }\n }\n links {\n nodes {\n ...Link\n }\n }\n authors {\n nodes\n }\n}\n\nfragment EntryMeta on Entry {\n title,\n published,\n updated,\n summary,\n}\n\nfragment Link on Link {\n href\n rel\n mediaType\n title \n}\n\nquery Entries($after: String, $first: Int!) {\n output: subscription {\n entries(after: $after, first: $first) {\n nodes {\n ...Entry\n }\n pageInfo {\n ...PageInfo\n }\n }\n }\n}\n\nfragment Entry on Entry {\n title\n published\n updated\n summary\n websiteUrl\n feed {\n ...FeedMeta\n }\n}\n\nfragment FeedMeta on FeedMeta {\n title\n url\n}\n\nfragment PageInfo on PageInfo {\n hasNextPage\n endCursor\n}\n\nquery ExportSubscription($after: String, $first: Int!) {\n output: subscription {\n feeds(after: $after, first: $first) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n title\n url\n }\n }\n }\n}\n" ;
pub const QUERY : & str = "query Subscription($after: String, $first: Int) {\n output: subscription {\n feeds(after: $after, first: $first) {\n nodes {\n ...Feed\n }\n pageInfo {\n ...PageInfo\n }\n }\n }\n}\n\nfragment Feed on Feed {\n id\n type\n title\n url\n updated\n websiteUrl\n description\n generator\n requirement\n category\n entries(first: 20) {\n nodes {\n ...EntryMeta\n }\n }\n links {\n nodes {\n ...Link\n }\n }\n authors {\n nodes\n }\n}\n\nfragment EntryMeta on Entry {\n title,\n published,\n updated,\n summary,\n}\n\nfragment Link on Link {\n href\n rel\n mediaType\n title \n}\n\nquery Entries($after: String, $first: Int!) {\n output: subscription {\n entries(after: $after, first: $first) {\n nodes {\n ...Entry\n }\n pageInfo {\n ...PageInfo\n }\n }\n }\n}\n\nfragment Entry on Entry {\n title\n published\n updated\n summary\n websiteUrl\n feed {\n ...FeedMeta\n }\n}\n\nfragment FeedMeta on FeedMeta {\n title\n url\n requirement\n category\n}\n\nfragment PageInfo on PageInfo {\n hasNextPage\n endCursor\n}\n\nquery ExportSubscription($after: String, $first: Int!) {\n output: subscription {\n feeds(after: $after, first: $first) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n title\n url\n }\n }\n }\n}\n" ;
use super::*;
use serde::{Deserialize, Serialize};
#[allow(dead_code)]
Expand Down Expand Up @@ -172,7 +172,7 @@ pub mod entries {
#![allow(dead_code)]
use std::result::Result;
pub const OPERATION_NAME: &str = "Entries";
pub const QUERY : & str = "query Subscription($after: String, $first: Int) {\n output: subscription {\n feeds(after: $after, first: $first) {\n nodes {\n ...Feed\n }\n pageInfo {\n ...PageInfo\n }\n }\n }\n}\n\nfragment Feed on Feed {\n id\n type\n title\n url\n updated\n websiteUrl\n description\n generator\n requirement\n category\n entries(first: 20) {\n nodes {\n ...EntryMeta\n }\n }\n links {\n nodes {\n ...Link\n }\n }\n authors {\n nodes\n }\n}\n\nfragment EntryMeta on Entry {\n title,\n published,\n updated,\n summary,\n}\n\nfragment Link on Link {\n href\n rel\n mediaType\n title \n}\n\nquery Entries($after: String, $first: Int!) {\n output: subscription {\n entries(after: $after, first: $first) {\n nodes {\n ...Entry\n }\n pageInfo {\n ...PageInfo\n }\n }\n }\n}\n\nfragment Entry on Entry {\n title\n published\n updated\n summary\n websiteUrl\n feed {\n ...FeedMeta\n }\n}\n\nfragment FeedMeta on FeedMeta {\n title\n url\n}\n\nfragment PageInfo on PageInfo {\n hasNextPage\n endCursor\n}\n\nquery ExportSubscription($after: String, $first: Int!) {\n output: subscription {\n feeds(after: $after, first: $first) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n title\n url\n }\n }\n }\n}\n" ;
pub const QUERY : & str = "query Subscription($after: String, $first: Int) {\n output: subscription {\n feeds(after: $after, first: $first) {\n nodes {\n ...Feed\n }\n pageInfo {\n ...PageInfo\n }\n }\n }\n}\n\nfragment Feed on Feed {\n id\n type\n title\n url\n updated\n websiteUrl\n description\n generator\n requirement\n category\n entries(first: 20) {\n nodes {\n ...EntryMeta\n }\n }\n links {\n nodes {\n ...Link\n }\n }\n authors {\n nodes\n }\n}\n\nfragment EntryMeta on Entry {\n title,\n published,\n updated,\n summary,\n}\n\nfragment Link on Link {\n href\n rel\n mediaType\n title \n}\n\nquery Entries($after: String, $first: Int!) {\n output: subscription {\n entries(after: $after, first: $first) {\n nodes {\n ...Entry\n }\n pageInfo {\n ...PageInfo\n }\n }\n }\n}\n\nfragment Entry on Entry {\n title\n published\n updated\n summary\n websiteUrl\n feed {\n ...FeedMeta\n }\n}\n\nfragment FeedMeta on FeedMeta {\n title\n url\n requirement\n category\n}\n\nfragment PageInfo on PageInfo {\n hasNextPage\n endCursor\n}\n\nquery ExportSubscription($after: String, $first: Int!) {\n output: subscription {\n feeds(after: $after, first: $first) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n title\n url\n }\n }\n }\n}\n" ;
use super::*;
use serde::{Deserialize, Serialize};
#[allow(dead_code)]
Expand All @@ -183,7 +183,36 @@ pub mod entries {
type Int = i64;
#[allow(dead_code)]
type ID = String;
type Category = crate::client::scalar::Category;
type Rfc3339Time = crate::client::scalar::Rfc3339Time;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Requirement {
MUST,
SHOULD,
MAY,
Other(String),
}
impl ::serde::Serialize for Requirement {
fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
ser.serialize_str(match *self {
Requirement::MUST => "MUST",
Requirement::SHOULD => "SHOULD",
Requirement::MAY => "MAY",
Requirement::Other(ref s) => &s,
})
}
}
impl<'de> ::serde::Deserialize<'de> for Requirement {
fn deserialize<D: ::serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s: String = ::serde::Deserialize::deserialize(deserializer)?;
match s.as_str() {
"MUST" => Ok(Requirement::MUST),
"SHOULD" => Ok(Requirement::SHOULD),
"MAY" => Ok(Requirement::MAY),
_ => Ok(Requirement::Other(s)),
}
}
}
#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
pub struct Variables {
pub after: Option<String>,
Expand All @@ -205,6 +234,8 @@ pub mod entries {
pub struct FeedMeta {
pub title: Option<String>,
pub url: String,
pub requirement: Option<Requirement>,
pub category: Option<Category>,
}
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct PageInfo {
Expand Down Expand Up @@ -246,7 +277,7 @@ pub mod export_subscription {
#![allow(dead_code)]
use std::result::Result;
pub const OPERATION_NAME: &str = "ExportSubscription";
pub const QUERY : & str = "query Subscription($after: String, $first: Int) {\n output: subscription {\n feeds(after: $after, first: $first) {\n nodes {\n ...Feed\n }\n pageInfo {\n ...PageInfo\n }\n }\n }\n}\n\nfragment Feed on Feed {\n id\n type\n title\n url\n updated\n websiteUrl\n description\n generator\n requirement\n category\n entries(first: 20) {\n nodes {\n ...EntryMeta\n }\n }\n links {\n nodes {\n ...Link\n }\n }\n authors {\n nodes\n }\n}\n\nfragment EntryMeta on Entry {\n title,\n published,\n updated,\n summary,\n}\n\nfragment Link on Link {\n href\n rel\n mediaType\n title \n}\n\nquery Entries($after: String, $first: Int!) {\n output: subscription {\n entries(after: $after, first: $first) {\n nodes {\n ...Entry\n }\n pageInfo {\n ...PageInfo\n }\n }\n }\n}\n\nfragment Entry on Entry {\n title\n published\n updated\n summary\n websiteUrl\n feed {\n ...FeedMeta\n }\n}\n\nfragment FeedMeta on FeedMeta {\n title\n url\n}\n\nfragment PageInfo on PageInfo {\n hasNextPage\n endCursor\n}\n\nquery ExportSubscription($after: String, $first: Int!) {\n output: subscription {\n feeds(after: $after, first: $first) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n title\n url\n }\n }\n }\n}\n" ;
pub const QUERY : & str = "query Subscription($after: String, $first: Int) {\n output: subscription {\n feeds(after: $after, first: $first) {\n nodes {\n ...Feed\n }\n pageInfo {\n ...PageInfo\n }\n }\n }\n}\n\nfragment Feed on Feed {\n id\n type\n title\n url\n updated\n websiteUrl\n description\n generator\n requirement\n category\n entries(first: 20) {\n nodes {\n ...EntryMeta\n }\n }\n links {\n nodes {\n ...Link\n }\n }\n authors {\n nodes\n }\n}\n\nfragment EntryMeta on Entry {\n title,\n published,\n updated,\n summary,\n}\n\nfragment Link on Link {\n href\n rel\n mediaType\n title \n}\n\nquery Entries($after: String, $first: Int!) {\n output: subscription {\n entries(after: $after, first: $first) {\n nodes {\n ...Entry\n }\n pageInfo {\n ...PageInfo\n }\n }\n }\n}\n\nfragment Entry on Entry {\n title\n published\n updated\n summary\n websiteUrl\n feed {\n ...FeedMeta\n }\n}\n\nfragment FeedMeta on FeedMeta {\n title\n url\n requirement\n category\n}\n\nfragment PageInfo on PageInfo {\n hasNextPage\n endCursor\n}\n\nquery ExportSubscription($after: String, $first: Int!) {\n output: subscription {\n feeds(after: $after, first: $first) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n title\n url\n }\n }\n }\n}\n" ;
use super::*;
use serde::{Deserialize, Serialize};
#[allow(dead_code)]
Expand Down
73 changes: 73 additions & 0 deletions crates/synd_term/src/config/categories.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use std::collections::HashMap;

use ratatui::style::Color;
use serde::Deserialize;
use synd_feed::types::Category;

#[derive(Deserialize)]
pub struct Categories {
categories: HashMap<String, Entry>,
}

impl Categories {
pub fn default_toml() -> Self {
let s = include_str!("../../../../categories.toml");
toml::from_str(s).unwrap()
}

pub fn icon(&self, category: &Category<'_>) -> Option<&Icon> {
self.categories
.get(category.as_str())
.map(|entry| &entry.icon)
}
}

#[derive(Deserialize)]
struct Entry {
icon: Icon,
}

#[derive(Deserialize)]
pub struct Icon {
symbol: String,
color: Option<IconColor>,
}

impl Icon {
pub fn symbol(&self) -> &str {
self.symbol.as_str()
}
pub fn color(&self) -> Option<Color> {
self.color.as_ref().and_then(IconColor::color)
}
}

#[derive(Deserialize, Default)]
struct IconColor {
rgb: Option<u32>,
// https://docs.rs/ratatui/latest/ratatui/style/enum.Color.html#variant.Red
name: Option<String>,
}

impl IconColor {
fn color(&self) -> Option<Color> {
self.rgb
.as_ref()
.map(|rgb| Color::from_u32(*rgb))
.or(self.name.as_ref().and_then(|s| s.parse().ok()))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn should_parse_default_toml() {
let c = Categories::default_toml();
let icon = c.icon(&Category::new("rust").unwrap()).unwrap();

assert_eq!(icon.symbol(), "");
assert_eq!(icon.color(), Some(Color::Rgb(247, 76, 0)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use std::{

use directories::ProjectDirs;

mod categories;
pub use categories::Categories;

pub mod api {
pub const ENDPOINT: &str = "https://api.syndicationd.ymgyt.io:6100";
}
Expand Down
12 changes: 12 additions & 0 deletions crates/synd_term/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ pub use time::{Time, TimeExt};
mod page_info;
pub use page_info::PageInfo;

mod requirement_ext;
pub use requirement_ext::RequirementExt;

#[derive(Debug, Clone)]
pub struct Link {
pub href: String,
Expand Down Expand Up @@ -169,6 +172,8 @@ pub struct Entry {
pub summary: Option<String>,
pub feed_title: Option<String>,
pub feed_url: String,
pub requirement: Option<Requirement>,
pub category: Option<Category<'static>>,
}

impl Entry {
Expand All @@ -189,6 +194,13 @@ impl From<query::entries::Entry> for Entry {
feed_title: v.feed.title,
feed_url: v.feed.url,
summary: v.summary,
requirement: match v.feed.requirement {
Some(query::entries::Requirement::MUST) => Some(Requirement::Must),
Some(query::entries::Requirement::SHOULD) => Some(Requirement::Should),
Some(query::entries::Requirement::MAY) => Some(Requirement::May),
_ => None,
},
category: v.feed.category,
}
}
}
Expand Down

0 comments on commit 1f41872

Please sign in to comment.