diff --git a/pointercrate-core-pages/static/js/modules/form.js b/pointercrate-core-pages/static/js/modules/form.js index 3504e7d3..40189565 100644 --- a/pointercrate-core-pages/static/js/modules/form.js +++ b/pointercrate-core-pages/static/js/modules/form.js @@ -133,6 +133,9 @@ export class DynamicSuggestionDropdown extends Dropdown { constructor(html) { super(html); + if (this.input.dataset.default !== undefined) { + this.input.value = this.input.dataset.default; + } this.endpoint = html.dataset.endpoint; this.field = html.dataset.field; diff --git a/pointercrate-demonlist-api/src/pages.rs b/pointercrate-demonlist-api/src/pages.rs index 5ce32a87..f12a2232 100644 --- a/pointercrate-demonlist-api/src/pages.rs +++ b/pointercrate-demonlist-api/src/pages.rs @@ -9,7 +9,7 @@ use pointercrate_core_api::{ error::Result, response::{Page, Response2}, }; -use pointercrate_demonlist::player::claim::PlayerClaim; +use pointercrate_demonlist::player::{claim::PlayerClaim, DatabasePlayer}; use pointercrate_demonlist::player::{FullPlayer, Player}; use pointercrate_demonlist::{ demon::{audit::audit_log_for_demon, current_list, list_at, FullDemon, MinimalDemon}, @@ -110,11 +110,18 @@ pub async fn demon_permalink(demon_id: i32, pool: &State) -> R #[localized] #[rocket::get("//")] -pub async fn demon_page(position: i16, pool: &State, gd: &State) -> Result { +pub async fn demon_page( + position: i16, auth: Option>, pool: &State, gd: &State, +) -> Result { let mut connection = pool.connection().await?; let full_demon = FullDemon::by_position(position, &mut connection).await?; + let claimed_player = match auth { + Some(auth) => DatabasePlayer::by_user(auth.user.user().id, &mut connection).await?, + None => None, + }; + let audit_log = audit_log_for_demon(full_demon.demon.base.id, &mut connection).await?; let mut addition_time = None; @@ -161,6 +168,7 @@ pub async fn demon_page(position: i16, pool: &State, gd: &Stat movements: modifications, integration: gd.load_level_for_demon(&full_demon.demon).await, data: full_demon, + claimed_player, })) } diff --git a/pointercrate-demonlist-pages/src/account/demons.rs b/pointercrate-demonlist-pages/src/account/demons.rs index dc928a45..b6c2bb2e 100644 --- a/pointercrate-demonlist-pages/src/account/demons.rs +++ b/pointercrate-demonlist-pages/src/account/demons.rs @@ -303,6 +303,7 @@ fn change_verifier_dialog() -> Markup { &tr("demon-verifier-dialog.info"), &tr("demon-verifier-dialog.submit"), "verifier", + None, ) } @@ -314,6 +315,7 @@ fn change_publisher_dialog() -> Markup { &tr("demon-publisher-dialog.info"), &tr("demon-publisher-dialog.submit"), "publisher", + None, ) } @@ -325,6 +327,7 @@ fn add_creator_dialog() -> Markup { &tr("demon-creator-dialog.info"), &tr("demon-creator-dialog.submit"), "creator", + None, ) } @@ -370,13 +373,13 @@ fn demon_submitter() -> Markup { span.form-input.flex.col data-type = "dropdown" { label{(tr("demon-add-form.verifier-field")) } br; - (player_selection_dropdown("demon-add-verifier", "/api/v1/players/", "name", "verifier")) + (player_selection_dropdown("demon-add-verifier", "/api/v1/players/", "name", "verifier", None)) p.error {} } span.form-input.flex.col data-type = "dropdown" { label {(tr("demon-add-form.publisher-field")) } br; - (player_selection_dropdown("demon-add-publisher", "/api/v1/players/", "name", "publisher")) + (player_selection_dropdown("demon-add-publisher", "/api/v1/players/", "name", "publisher", None)) p.error {} } span.form-input.flex.col #demon-add-video { diff --git a/pointercrate-demonlist-pages/src/account/records.rs b/pointercrate-demonlist-pages/src/account/records.rs index 64b92ee2..8f5ad441 100644 --- a/pointercrate-demonlist-pages/src/account/records.rs +++ b/pointercrate-demonlist-pages/src/account/records.rs @@ -10,6 +10,7 @@ use pointercrate_core_pages::{ }; use pointercrate_demonlist::{ demon::{current_list, Demon}, + player::DatabasePlayer, LIST_HELPER, }; use pointercrate_user::auth::{AuthenticatedUser, NonMutating}; @@ -43,7 +44,7 @@ impl AccountPageTab for RecordsPage { } async fn content( - &self, _user: &AuthenticatedUser, _permissions: &PermissionsManager, connection: &mut PgConnection, + &self, user: &AuthenticatedUser, _permissions: &PermissionsManager, connection: &mut PgConnection, ) -> Markup { let demons = match current_list(connection).await { Ok(demons) => demons, @@ -57,9 +58,11 @@ impl AccountPageTab for RecordsPage { }, }; + let player = DatabasePlayer::by_user(user.user().id, connection).await.unwrap_or(None); + html! { div.left { - (RecordSubmitter::new(false, &demons[..])) + (RecordSubmitter::new(false, &demons[..], player.as_ref(), None)) (record_manager(&demons[..])) (note_adder()) div.panel.fade #record-notes-container style = "display:none" { @@ -386,6 +389,7 @@ fn change_holder_dialog() -> Markup { &tr("record-holder-dialog.info"), &tr("record-holder-dialog.submit"), "player", + None, ) } @@ -401,7 +405,7 @@ fn change_demon_dialog(demons: &[Demon]) -> Markup { p { (tr("record-videolink-dialog.info")) } - (demon_dropdown("edit-demon-record", demons.iter())) + (demon_dropdown("edit-demon-record", demons.iter(), None)) } } } diff --git a/pointercrate-demonlist-pages/src/components/mod.rs b/pointercrate-demonlist-pages/src/components/mod.rs index 3623bc10..8a8a43cf 100644 --- a/pointercrate-demonlist-pages/src/components/mod.rs +++ b/pointercrate-demonlist-pages/src/components/mod.rs @@ -9,11 +9,11 @@ pub mod submitter; pub mod team; pub mod time_machine; -pub fn demon_dropdown<'a>(dropdown_id: &str, demons: impl Iterator) -> Markup { +pub fn demon_dropdown<'a>(dropdown_id: &str, demons: impl Iterator, initial_demon: Option) -> Markup { html! { div.dropdown-menu.js-search #(dropdown_id) { div { - input type = "text" name = "demon" required="" autocomplete="off"; + input type = "text" name = "demon" required="" autocomplete="off" data-default=[initial_demon]; } div.menu { ul { @@ -26,11 +26,13 @@ pub fn demon_dropdown<'a>(dropdown_id: &str, demons: impl Iterator Markup { +pub fn player_selection_dropdown( + dropdown_id: &str, endpoint: &str, field: &str, form_field: &str, initial_player: Option<&DatabasePlayer>, +) -> Markup { html! { div.dropdown-menu #(dropdown_id) data-endpoint = (endpoint) data-field = (field) { div { - input type = "text" name = (form_field) required="" autocomplete="off" placeholder = (tr("record-submission.holder-input-placeholder")); + input type = "text" name = (form_field) required="" autocomplete="off" placeholder = (tr("record-submission.holder-input-placeholder")) data-default=[initial_player.map(|p| &p.name)]; } div.menu { // dynamically populated once the user starts typing @@ -42,6 +44,7 @@ pub fn player_selection_dropdown(dropdown_id: &str, endpoint: &str, field: &str, pub fn player_selection_dialog( dialog_id: &str, dropdown_id: &str, headline: &str, description: &str, button_text: &str, form_field: &str, + initial_player: Option<&DatabasePlayer>, ) -> Markup { html! { div.overlay.closable { @@ -55,7 +58,7 @@ pub fn player_selection_dialog( (description) } span.form-input.flex.col data-type = "dropdown" { - (player_selection_dropdown(dropdown_id, "/api/v1/players/", "name", form_field)) + (player_selection_dropdown(dropdown_id, "/api/v1/players/", "name", form_field, initial_player)) p.error {} } input.button.blue.hover type = "submit" style = "margin: 15px auto 0px;" value = (button_text); diff --git a/pointercrate-demonlist-pages/src/components/submitter.rs b/pointercrate-demonlist-pages/src/components/submitter.rs index e78525cc..c5ffa97e 100644 --- a/pointercrate-demonlist-pages/src/components/submitter.rs +++ b/pointercrate-demonlist-pages/src/components/submitter.rs @@ -2,23 +2,31 @@ use crate::components::{demon_dropdown, player_selection_dropdown}; use maud::{html, Markup, Render}; use pointercrate_core::{localization::tr, trp}; use pointercrate_core_pages::trp_html; -use pointercrate_demonlist::{config, demon::Demon}; +use pointercrate_demonlist::{config, demon::Demon, player::DatabasePlayer}; -pub struct RecordSubmitter<'a> { +pub struct RecordSubmitter<'d, 'p> { initially_visible: bool, - demons: &'a [Demon], + demons: &'d [Demon], + initial_demon: Option, + initial_holder: Option<&'p DatabasePlayer>, } -impl<'a> RecordSubmitter<'a> { - pub fn new(visible: bool, demons: &'a [Demon]) -> RecordSubmitter<'a> { +impl<'d, 'p> RecordSubmitter<'d, 'p> { + /// * `visible` - Show the record submitter. + /// * `demons` - The Demonlist. + /// * `holder` - Player to preselect as the record holder. `None` to not preselect a player. + /// * `demon` - Position of the demon in the demonlist to preselect. `None` to not preselect a demon. + pub fn new(visible: bool, demons: &'d [Demon], holder: Option<&'p DatabasePlayer>, demon: Option) -> RecordSubmitter<'d, 'p> { RecordSubmitter { initially_visible: visible, demons, + initial_demon: demon, + initial_holder: holder, } } } -impl Render for RecordSubmitter<'_> { +impl Render for RecordSubmitter<'_, '_> { fn render(&self) -> Markup { html! { section.panel.fade.closable #submitter style=(if !self.initially_visible {"display:none"} else {""}) { @@ -36,7 +44,7 @@ impl Render for RecordSubmitter<'_> { (trp!("record-submission.demon-info", "list-size" = config::extended_list_size())) } span.form-input data-type = "dropdown" { - (demon_dropdown("id_demon", self.demons.iter().filter(|demon| demon.base.position <= config::extended_list_size()))) + (demon_dropdown("id_demon", self.demons.iter().filter(|demon| demon.base.position <= config::extended_list_size()), self.initial_demon)) p.error {} } h3 { @@ -46,7 +54,7 @@ impl Render for RecordSubmitter<'_> { (tr("record-submission.holder-info")) } span.form-input.flex.col data-type = "dropdown" { - (player_selection_dropdown("id_player", "/api/v1/players/", "name", "player")) + (player_selection_dropdown("id_player", "/api/v1/players/", "name", "player", self.initial_holder)) p.error {} } h3 { diff --git a/pointercrate-demonlist-pages/src/demon_page.rs b/pointercrate-demonlist-pages/src/demon_page.rs index 78fc970b..7ccab54f 100644 --- a/pointercrate-demonlist-pages/src/demon_page.rs +++ b/pointercrate-demonlist-pages/src/demon_page.rs @@ -10,6 +10,7 @@ use chrono::NaiveDateTime; use maud::{html, Markup, PreEscaped}; use pointercrate_core::{localization::tr, trp}; use pointercrate_core_pages::{head::HeadLike, trp_html, PageFragment}; +use pointercrate_demonlist::player::DatabasePlayer; use pointercrate_demonlist::{ config::{self as list_config, extended_list_size}, demon::{Demon, FullDemon}, @@ -29,6 +30,7 @@ pub struct DemonPage { pub data: FullDemon, pub movements: Vec, pub integration: Option, + pub claimed_player: Option, } impl From for PageFragment { @@ -145,12 +147,18 @@ impl DemonPage { } } + let demon = self + .demonlist + .iter() + .position(|d| d.base.id == self.data.demon.base.id) + .map(|i| i as i16 + 1); + html! { (dropdowns) div.flex.m-center.container { main.left { - (RecordSubmitter::new(false, &self.demonlist)) + (RecordSubmitter::new(false, &self.demonlist, self.claimed_player.as_ref(), demon)) (self.demon_panel()) div.panel.fade.js-scroll-anim.js-collapse data-anim = "fade" { h2.underlined.pad { diff --git a/pointercrate-demonlist-pages/src/overview.rs b/pointercrate-demonlist-pages/src/overview.rs index 7e425995..727dd711 100644 --- a/pointercrate-demonlist-pages/src/overview.rs +++ b/pointercrate-demonlist-pages/src/overview.rs @@ -97,7 +97,7 @@ impl OverviewPage { div.flex.m-center.container { main.left { (self.time_machine) - (RecordSubmitter::new(self.submitter_initially_visible, &self.demonlist)) + (RecordSubmitter::new(self.submitter_initially_visible, &self.demonlist, self.claimed_player.as_ref().map(|p| &p.player.base), None)) @match &self.time_machine { Tardis::Activated { demons, ..} => { diff --git a/pointercrate-demonlist/src/player/get.rs b/pointercrate-demonlist/src/player/get.rs index 14ad3677..02736b6b 100644 --- a/pointercrate-demonlist/src/player/get.rs +++ b/pointercrate-demonlist/src/player/get.rs @@ -114,6 +114,20 @@ impl DatabasePlayer { result => result, } } + + pub async fn by_user(user_id: i32, connection: &mut PgConnection) -> Result> { + sqlx::query_as!( + DatabasePlayer, + "SELECT players.id, players.name, players.banned FROM players + JOIN player_claims ON player_claims.player_id = players.id + JOIN members ON members.member_id = player_claims.member_id + WHERE members.member_id = $1", + user_id + ) + .fetch_optional(connection) + .await + .map_err(DemonlistError::from) + } } #[cfg(test)]