The code and data behind xeiaso.net
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

implement pronouns support

Signed-off-by: Xe Iaso <me@christine.website>

Xe Iaso 351069d9 b96a4464

+218 -36
+6 -1
dhall/authors.dhall
··· 21 21 "https://cdn.xeiaso.net/file/christine-static/img/FFVV1InX0AkDX3f_cropped_smol.jpg" 22 22 , inSystem = True 23 23 } 24 - , Author::{ name = "Nicole", handle = "Twi", inSystem = True } 24 + , Author::{ 25 + , name = "Nicole Brennan" 26 + , handle = "Twi" 27 + , url = Some "https://tech.lgbt/@twi" 28 + , inSystem = True 29 + } 25 30 , Author::{ name = "Mai", handle = "Mai", inSystem = True } 26 31 , Author::{ name = "Sephira", handle = "sephiraloveboo", inSystem = True } 27 32 ]
+1 -3
dhall/authors/xe.dhall
··· 1 - let xesite = ../types/package.dhall 2 - 3 - let Author = xesite.Author 1 + let Author = ../types/Author.dhall 4 2 5 3 in Author::{ 6 4 , name = "Xe Iaso"
+4 -2
dhall/package.dhall
··· 23 23 , title = "Aura" 24 24 , description = "PonyvilleFM live DJ recording bot" 25 25 } 26 - , Link::{ 26 + , Link::{ 27 27 , url = "https://h.within.lgbt" 28 28 , title = "The h Programming Language" 29 - , description = "An esoteric programming language that compiles to WebAssembly" 29 + , description = 30 + "An esoteric programming language that compiles to WebAssembly" 30 31 } 31 32 , Link::{ 32 33 , url = "https://github.com/Xe/olin" ··· 80 81 , Link::{ url = "https://t.me/miamorecadenza", title = "Telegram" } 81 82 , Link::{ url = "irc://irc.libera.chat/#xeserv", title = "IRC" } 82 83 ] 84 + , pronouns = ./pronouns.dhall 83 85 }
+15
dhall/pronouns.dhall
··· 1 + let xesite = ./types/package.dhall 2 + 3 + let Pronouns = xesite.PronounSet 4 + 5 + in [ Pronouns::{ 6 + , nominative = "xe" 7 + , accusative = "xer" 8 + , possessiveDeterminer = "xer" 9 + , possessive = "xers" 10 + , reflexive = "xerself" 11 + , singular = True 12 + } 13 + , ./pronouns/they.dhall 14 + , ./pronouns/she.dhall 15 + ]
+10
dhall/pronouns/she.dhall
··· 1 + let Pronouns = ../types/PronounSet.dhall 2 + 3 + in Pronouns::{ 4 + , nominative = "she" 5 + , accusative = "her" 6 + , possessiveDeterminer = "her" 7 + , possessive = "hers" 8 + , reflexive = "herself" 9 + , singular = True 10 + }
+10
dhall/pronouns/they.dhall
··· 1 + let Pronouns = ../types/PronounSet.dhall 2 + 3 + in Pronouns::{ 4 + , nominative = "they" 5 + , accusative = "them" 6 + , possessiveDeterminer = "their" 7 + , possessive = "theirs" 8 + , reflexive = "themselves" 9 + , singular = False 10 + }
+22 -18
dhall/types/Author.dhall
··· 1 - { Type = 2 - { name : Text 3 - , handle : Text 4 - , image : Optional Text 5 - , url : Optional Text 6 - , sameAs : List Text 7 - , jobTitle : Text 8 - , inSystem : Bool 1 + let PronounSet = ./PronounSet.dhall 2 + 3 + in { Type = 4 + { name : Text 5 + , handle : Text 6 + , image : Optional Text 7 + , url : Optional Text 8 + , sameAs : List Text 9 + , jobTitle : Text 10 + , inSystem : Bool 11 + , pronouns : PronounSet.Type 12 + } 13 + , default = 14 + { name = "" 15 + , handle = "" 16 + , image = None Text 17 + , url = None Text 18 + , sameAs = [] : List Text 19 + , jobTitle = "" 20 + , inSystem = False 21 + , pronouns = ../pronouns/she.dhall 22 + } 9 23 } 10 - , default = 11 - { name = "" 12 - , handle = "" 13 - , image = None Text 14 - , url = None Text 15 - , sameAs = [] : List Text 16 - , jobTitle = "" 17 - , inSystem = False 18 - } 19 - }
+4 -2
dhall/types/Config.dhall
··· 10 10 11 11 let SeriesDescription = ./SeriesDescription.dhall 12 12 13 + let PronounSet = ./PronounSet.dhall 14 + 13 15 let Prelude = ../Prelude.dhall 14 16 15 17 let defaultPort = env:PORT ? 3030 ··· 24 26 , authors : Prelude.Map.Type Text Author.Type 25 27 , port : Natural 26 28 , clackSet : List Text 27 - , resumeFname : Text 28 29 , webMentionEndpoint : Text 29 30 , miToken : Text 30 31 , jobHistory : List Job.Type ··· 32 33 , seriesDescMap : Prelude.Map.Type Text Text 33 34 , notableProjects : List Link.Type 34 35 , contactLinks : List Link.Type 36 + , pronouns : List PronounSet.Type 35 37 } 36 38 , default = 37 39 { signalboost = [] : List Person.Type ··· 39 41 , authors = [] : List Author.Type 40 42 , port = defaultPort 41 43 , clackSet = [ "Ashlynn" ] 42 - , resumeFname = "./static/resume/resume.md" 43 44 , webMentionEndpoint = defaultWebMentionEndpoint 44 45 , miToken = "${env:MI_TOKEN as Text ? ""}" 45 46 , jobHistory = [] : List Job.Type ··· 47 48 , seriesDescMap = [] : Prelude.Map.Type Text Text 48 49 , notableProjects = [] : List Link.Type 49 50 , contactLinks = [] : List Link.Type 51 + , pronouns = [] : List PronounSet.Type 50 52 } 51 53 }
+17
dhall/types/PronounSet.dhall
··· 1 + { Type = 2 + { nominative : Text 3 + , accusative : Text 4 + , possessiveDeterminer : Text 5 + , possessive : Text 6 + , reflexive : Text 7 + , singular : Bool 8 + } 9 + , default = 10 + { nominative = "xe" 11 + , accusative = "xer" 12 + , possessiveDeterminer = "xer" 13 + , possessive = "xers" 14 + , reflexive = "xerself" 15 + , singular = True 16 + } 17 + }
+1
dhall/types/package.dhall
··· 6 6 , Location = ./Location.dhall 7 7 , NagMessage = ./NagMessage.dhall 8 8 , Person = ./Person.dhall 9 + , PronounSet = ./PronounSet.dhall 9 10 , Resume = ./Resume.dhall 10 11 , Salary = ./Salary.dhall 11 12 , SeriesDescription = ./SeriesDescription.dhall
+57 -3
src/app/config.rs
··· 4 4 use std::{ 5 5 collections::HashMap, 6 6 fmt::{self, Display}, 7 - path::PathBuf, 8 7 }; 9 8 10 9 #[derive(Clone, Deserialize, Default)] ··· 16 15 pub port: u16, 17 16 #[serde(rename = "clackSet")] 18 17 pub clack_set: Vec<String>, 19 - #[serde(rename = "resumeFname")] 20 - pub resume_fname: PathBuf, 21 18 #[serde(rename = "miToken")] 22 19 pub mi_token: String, 23 20 #[serde(rename = "jobHistory")] ··· 30 27 pub notable_projects: Vec<Link>, 31 28 #[serde(rename = "contactLinks")] 32 29 pub contact_links: Vec<Link>, 30 + pub pronouns: Vec<PronounSet>, 31 + } 32 + 33 + #[derive(Clone, Deserialize, Serialize, Default)] 34 + pub struct PronounSet { 35 + nominative: String, 36 + accusative: String, 37 + #[serde(rename = "possessiveDeterminer")] 38 + possessive_determiner: String, 39 + possessive: String, 40 + reflexive: String, 41 + singular: bool, 42 + } 43 + 44 + impl Render for PronounSet { 45 + fn render(&self) -> Markup { 46 + html! { 47 + big { (self.nominative) "/" (self.accusative) } 48 + table { 49 + tr { 50 + th { "Subject" } 51 + td {(self.nominative)} 52 + } 53 + tr { 54 + th { "Object" } 55 + td {(self.accusative)} 56 + } 57 + tr { 58 + th { "Dependent Possessive" } 59 + td {(self.possessive_determiner)} 60 + } 61 + tr { 62 + th { "Independent Possessive" } 63 + td {(self.possessive)} 64 + } 65 + tr { 66 + th { "Reflexive" } 67 + td {(self.reflexive)} 68 + } 69 + } 70 + p {"Here are some example sentences with these pronouns:"} 71 + ul { 72 + li { i{(self.nominative)} " went to the park." } 73 + li { "I went with " i{(self.accusative)} "." } 74 + li { i{(self.nominative)} " brought " i{(self.possessive_determiner)} " frisbee." } 75 + li { "At least I think it was " i{(self.possessive)} "." } 76 + li { i{(self.nominative)} " threw the frisbee to " i{(self.reflexive)} "." } 77 + } 78 + @if !self.singular { 79 + p { 80 + "Please note that this pronoun is normally a plural pronoun. It is used here to refer to a single person. For more information on this, see " 81 + a href="https://www.merriam-webster.com/words-at-play/singular-nonbinary-they" {"this page from Merriam-Webster"} 82 + " that will explain in more detail." 83 + } 84 + } 85 + } 86 + } 33 87 } 34 88 35 89 #[derive(Clone, Deserialize, Serialize, Default)]
+16 -2
src/handlers/api.rs
··· 1 1 use crate::{ 2 - app::{config::Job, State}, 2 + app::{config::Job, PronounSet, State}, 3 3 handlers::Result, 4 4 post::Post, 5 5 }; ··· 27 27 super::HIT_COUNTER 28 28 .with_label_values(&["salary_transparency_json"]) 29 29 .inc(); 30 + let state = state.clone(); 31 + let cfg = state.cfg.clone(); 30 32 31 - Json(state.clone().cfg.clone().job_history.clone()) 33 + Json(cfg.job_history.clone()) 34 + } 35 + 36 + #[axum_macros::debug_handler] 37 + #[instrument(skip(state))] 38 + pub async fn pronouns(Extension(state): Extension<Arc<State>>) -> Json<Vec<PronounSet>> { 39 + super::HIT_COUNTER 40 + .with_label_values(&["pronouns_json"]) 41 + .inc(); 42 + let state = state.clone(); 43 + let cfg = state.cfg.clone(); 44 + 45 + Json(cfg.pronouns.clone()) 32 46 } 33 47 34 48 #[instrument(skip(state))]
+9
src/handlers/mod.rs
··· 86 86 crate::tmpl::contact(&cfg.contact_links) 87 87 } 88 88 89 + #[instrument(skip(state))] 90 + pub async fn pronouns(Extension(state): Extension<Arc<State>>) -> Markup { 91 + HIT_COUNTER.with_label_values(&["pronouns"]).inc(); 92 + let state = state.clone(); 93 + let cfg = state.cfg.clone(); 94 + 95 + crate::tmpl::pronoun_page(&cfg.pronouns) 96 + } 97 + 89 98 #[instrument] 90 99 pub async fn feeds() -> Markup { 91 100 HIT_COUNTER.with_label_values(&["feeds"]).inc();
+2
src/main.rs
··· 161 161 ), 162 162 ) 163 163 // api 164 + .route("/api/pronouns", get(handlers::api::pronouns)) 164 165 .route("/api/new_post", get(handlers::feeds::new_post)) 165 166 .route( 166 167 "/api/salary_transparency.json", ··· 176 177 .route("/patrons", get(handlers::patrons)) 177 178 .route("/signalboost", get(handlers::signalboost)) 178 179 .route("/salary-transparency", get(handlers::salary_transparency)) 180 + .route("/pronouns", get(handlers::pronouns)) 179 181 // feeds 180 182 .route("/blog.json", get(handlers::feeds::jsonfeed)) 181 183 .route("/blog.atom", get(handlers::feeds::atom))
+24
src/tmpl/mod.rs
··· 582 582 } 583 583 } 584 584 } 585 + 586 + pub fn pronoun_page(pronouns: &Vec<PronounSet>) -> Markup { 587 + base( 588 + Some("Pronouns"), 589 + None, 590 + html! { 591 + h1 {"Pronouns"} 592 + p {"This page lists the pronouns you should use for me. Please try to use one of these sets:"} 593 + .grid { 594 + @for ps in pronouns { 595 + .card.cell."-4of12" { 596 + (ps) 597 + } 598 + } 599 + } 600 + 601 + (xesite_templates::conv("Mara".to_string(), "happy".to_string(), html!{ 602 + "You can access this data with " 603 + a href="/api/pronouns" {"an API call"} 604 + " too!" 605 + })) 606 + }, 607 + ) 608 + }
+20 -5
src/tmpl/nag.rs
··· 5 5 6 6 lazy_static! { 7 7 static ref LOBSTERS: Regex = Regex::new(r#"^https?://lobste.rs"#).unwrap(); 8 + static ref HACKER_NEWS: Regex = Regex::new(r#"^https?://news.ycombinator.com"#).unwrap(); 8 9 } 9 10 10 11 #[cfg(debug_assertions)] 11 12 pub fn referer(_: Option<String>) -> Markup { 12 13 html! { 13 14 .warning { 14 - "This is a development instance of xesite. Things here are probably unfinished or in drafting. Don't take anything here super seriously. If you want to share this to an online aggregator, please don't. Drafts are not finalized yet for a reason." 15 + "This is a development instance of xesite. Things here are probably unfinished or in drafting. Don't take anything here super seriously. If you want to share this to an online aggregator, please don't. Drafts are not finalized yet for a reason. Please don't be the reason I need to implement more advanced security than just obscurity." 15 16 } 16 17 br; 17 18 } ··· 27 28 28 29 let referer = referer.unwrap(); 29 30 31 + if HACKER_NEWS.is_match(&referer) { 32 + return xeblog_conv( 33 + "Mara".into(), 34 + "hacker".into(), 35 + html! { 36 + "Hello. Before commenting about the author, please read " 37 + a href="/pronouns" {"this page"} 38 + " that explains the pronouns that you should be using. tl;dr: the author of this website is NOT male. Please do not use \"he\" or \"him\" when referring to the author." 39 + }, 40 + ); 41 + } 42 + 30 43 if LOBSTERS.is_match(&referer) { 31 - return html! { 32 - (xeblog_conv("Mara".into(), "happy".into(), html!{ 44 + return xeblog_conv( 45 + "Mara".into(), 46 + "happy".into(), 47 + html! { 33 48 "Hey, thanks for reading Lobsters! We've disabled the ads to thank you for choosing to use a more ethical aggregator." 34 - })) 35 - }; 49 + }, 50 + ); 36 51 } 37 52 38 53 xesite_templates::advertiser_nag()