diff --git a/builder/src/components.rs b/builder/src/components.rs index c0e46f11..6cf5cf60 100644 --- a/builder/src/components.rs +++ b/builder/src/components.rs @@ -3,6 +3,7 @@ mod calendar; pub(crate) mod home_page; pub(crate) mod mob_page; pub(crate) mod page_base; +pub(crate) mod redirect_page; pub(crate) mod schema; pub(crate) use calendar::Calendar; diff --git a/builder/src/components/add_page.rs b/builder/src/components/add_page.rs index 138a046d..8c0c3122 100644 --- a/builder/src/components/add_page.rs +++ b/builder/src/components/add_page.rs @@ -61,6 +61,7 @@ impl Render for AddPage { .clone() .into_page( Some("Add".to_owned().into()), + None, content, classes!("flex", "flex-col", VERTICAL_GAP_CLASS), components::page_base::PageDescription::from(format!( diff --git a/builder/src/components/home_page.rs b/builder/src/components/home_page.rs index 45ad6fb5..cdba84cf 100644 --- a/builder/src/components/home_page.rs +++ b/builder/src/components/home_page.rs @@ -88,6 +88,7 @@ impl Render for HomePage { }; let page = self.base.clone().into_page( + None, None, content, classes!("flex", "flex-col", "gap-1"), diff --git a/builder/src/components/mob_page.rs b/builder/src/components/mob_page.rs index d396590a..0b24cc98 100644 --- a/builder/src/components/mob_page.rs +++ b/builder/src/components/mob_page.rs @@ -57,6 +57,7 @@ impl Render for MobPage { } mob::Status::Full(join_content) => join_content.clone(), mob::Status::Public(join_content) => Some(join_content.clone()), + mob::Status::Renamed(_id) => None, mob::Status::Terminated(content) => content.clone(), }; @@ -135,6 +136,7 @@ impl Render for MobPage { .clone() .into_page( Some(self.mob.title().as_str().to_owned().into()), + None, content, classes!("flex", "flex-col", "gap-6"), components::page_base::PageDescription::from(format!( diff --git a/builder/src/components/mob_page/status.rs b/builder/src/components/mob_page/status.rs index 5a9ba4e7..f0895917 100644 --- a/builder/src/components/mob_page/status.rs +++ b/builder/src/components/mob_page/status.rs @@ -56,6 +56,9 @@ impl Render for Status { status_wrapper_true, status_wrapper_false, ), + mob::Status::Renamed(_) => { + unreachable!() + } mob::Status::Terminated(_) => ( status_wrapper_false, status_wrapper_false, diff --git a/builder/src/components/page_base.rs b/builder/src/components/page_base.rs index f289854f..1ef7ba2a 100644 --- a/builder/src/components/page_base.rs +++ b/builder/src/components/page_base.rs @@ -72,6 +72,7 @@ impl PageBase { pub(crate) fn into_page( self, title: Option, + head_content: Option, content: Markup, content_classes: Classes, description: PageDescription, @@ -79,6 +80,7 @@ impl PageBase { Page { base: self, title, + head_content, content, content_classes, description, @@ -89,11 +91,13 @@ impl PageBase { pub(crate) struct Page { base: PageBase, title: Option, + head_content: Option, content: Markup, content_classes: Classes, description: PageDescription, } +#[allow(clippy::too_many_lines)] impl Render for Page { fn render(&self) -> Markup { const NAV_ICON_SIZE: u8 = 32; @@ -146,6 +150,9 @@ impl Render for Page { meta charset="utf-8"; meta name="description" content=(self.description); meta name="viewport" content="width=device-width, initial-scale=1.0"; + @if let Some(head_content) = &self.head_content { + (head_content) + } link rel="stylesheet" href={ "/index.css?v=" (version) }; style { @for font in fonts::ALL.as_slice() { (font) } diff --git a/builder/src/components/redirect_page.rs b/builder/src/components/redirect_page.rs new file mode 100644 index 00000000..ec8b5563 --- /dev/null +++ b/builder/src/components/redirect_page.rs @@ -0,0 +1,44 @@ +use maud::{html, Render}; + +use super::PageBase; + +#[derive(Debug, Clone)] +pub(crate) struct RedirectPage { + base: PageBase, + target: String, +} + +impl RedirectPage { + pub(crate) fn new(base: PageBase, target: String) -> Self { + Self { base, target } + } +} + +impl Render for RedirectPage { + fn render(&self) -> maud::Markup { + let title = Some("Redirecting...".to_owned().into()); + + let head_content = Some(html! { + meta http-equiv="refresh" content=(format!("5; url={}", self.target)); + }); + + let content = html! { + p { + "Redirecting to"; + code { (self.target) }; + "..."; + } + }; + + let content_classes = classes!("flex", "justify-center", "items-center"); + + let description = format!("Redirect to {}...", self.target).to_owned().into(); + + let page = + self.base + .clone() + .into_page(title, head_content, content, content_classes, description); + + page.render() + } +} diff --git a/builder/src/mob.rs b/builder/src/mob.rs index 8f18d93e..8cf48de6 100644 --- a/builder/src/mob.rs +++ b/builder/src/mob.rs @@ -154,19 +154,32 @@ impl Mob { components::mob_page::event_content_template, ); - let base = components::PageBase::new(&mut expected_files, path.clone()); - - let page = components::mob_page::MobPage::new( - self, - links, - events, - base, - expected_files.insert_("/fullcalendar.js"), - expected_files.insert_("/rrule.js"), - expected_files.insert_("/fullcalendar_rrule.js"), - ); - - let bytes = page.render().0.into_bytes(); + let markup = if let status::Status::Renamed(renamed_id) = self.status() { + let base = components::PageBase::new(&mut expected_files, path.clone()); + + let page = components::redirect_page::RedirectPage::new( + base, + format!("/mobs/{renamed_id}.html"), + ); + + page.render() + } else { + let base = components::PageBase::new(&mut expected_files, path.clone()); + + let page = components::mob_page::MobPage::new( + self, + links, + events, + base, + expected_files.insert_("/fullcalendar.js"), + expected_files.insert_("/rrule.js"), + expected_files.insert_("/fullcalendar_rrule.js"), + ); + + page.render() + }; + + let bytes = markup.render().0.into_bytes(); FileSpec::new(path, BytesSource::new(bytes, Some(expected_files))) } diff --git a/builder/src/mob/id.rs b/builder/src/mob/id.rs index 028dd9cf..b6f5192c 100644 --- a/builder/src/mob/id.rs +++ b/builder/src/mob/id.rs @@ -1,4 +1,6 @@ -#[derive(Debug, Clone, derive_more::Display)] +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, derive_more::Display, Serialize, Deserialize)] pub(crate) struct Id(String); impl Id { diff --git a/builder/src/mob/status.rs b/builder/src/mob/status.rs index 1753366b..57dfab11 100644 --- a/builder/src/mob/status.rs +++ b/builder/src/mob/status.rs @@ -10,6 +10,8 @@ use crate::{markdown::Markdown, syn_helpers::Attribute}; pub(crate) use self::legend::Legend; +use super::id::Id; + #[derive(Debug, Clone, Serialize, Deserialize, Schema, AsRefStr, EnumVariantNames, CustomAttrs)] #[attr(indicator: Option)] /// A mob's status @@ -61,6 +63,16 @@ pub(crate) enum Status { /// ``` #[attr(indicator = '⛲')] Public(Markdown), + /// This mob has been renamed. + /// + /// The value is the new name. + /// + /// Example: + /// + /// ```yaml + /// !Renamed "love" + /// ``` + Renamed(Id), /// This mob has been terminated. /// /// The value may explain why. diff --git a/builder/src/pages/index.rs b/builder/src/pages/index.rs index 9a42b706..24a5171d 100644 --- a/builder/src/pages/index.rs +++ b/builder/src/pages/index.rs @@ -18,7 +18,12 @@ pub fn page() -> FileSpec { let events = MOBS .iter() - .filter(|mob| !matches!(mob.status(), mob::Status::Terminated(_))) + .filter(|mob| { + !matches!( + mob.status(), + mob::Status::Terminated(_) | mob::Status::Renamed(_) + ) + }) .map(|mob| mob.events(&mut expected_files, event_content_template)) .collect::>() .into_iter() diff --git a/mobs/love.yaml b/mobs/love.yaml new file mode 100644 index 00000000..17827801 --- /dev/null +++ b/mobs/love.yaml @@ -0,0 +1,30 @@ +title: Mob Love +subtitle: A mob for eslint-config-love +participants: + - !Public + name: Shahar “Dawn” Or + social_url: https://twitter.com/mightyiam + avatar_url: https://avatars.githubusercontent.com/u/635591?v=4 + - !Public + name: Rostislav Simonik + social_url: https://github.com/rostislav-simonik + avatar_url: https://avatars.githubusercontent.com/u/25525736?v=4 +schedule: + - frequency: FREQ=WEEKLY;BYDAY=SA + timezone: Etc/UTC + start_date: 2024-06-01 + start_time: 05:00 + duration: 180 +background_color: fuchsia +text_color: navy +freeform_copy: | + ## What we do + + We work on [eslint-config-love](https://github.com/mightyiam/eslint-config-love). + +status: !Open | + ## Join us + + If you're interested in JavaScript, TypeScript and open source, consider applying to our mob. + + For more details and application, see [this gist](https://gist.github.com/mightyiam/6618d6ae649dc26ef485a21ccfe1eb3e). diff --git a/mobs/standard.yaml b/mobs/standard.yaml index 57c8a460..da8ddb81 100644 --- a/mobs/standard.yaml +++ b/mobs/standard.yaml @@ -24,9 +24,4 @@ freeform_copy: | We develop and maintain [JavaScript Standard Style](https://standardjs.com/). -status: !Open | - ## Join us - - If you're interested in JavaScript, TypeScript and open source project maintenance, consider applying to our mob. - - For more details and application, see [this gist](https://gist.github.com/mightyiam/6618d6ae649dc26ef485a21ccfe1eb3e). +status: !Renamed "love"