From 982a7ffd655a746a3edf82cecff82031b674d2e8 Mon Sep 17 00:00:00 2001 From: trotFunky Date: Wed, 24 Jul 2024 15:23:52 +0100 Subject: [PATCH 01/13] vote_chart: sort parsed votes The SQL query retrieving the votes is deterministicly sorted, but goes through a HashMap, for ease of processing and transfer, which does not maintaing order. Sort the resulting object in the Javascript to keep a consistent order in the graph. --- src/vote.rs | 2 ++ static_files/vote_chart.js | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/vote.rs b/src/vote.rs index 09239f6..d4e5d42 100644 --- a/src/vote.rs +++ b/src/vote.rs @@ -115,6 +115,8 @@ pub async fn fetch_vote_data(week: u8, mut db: Connection) -> Opti } + // The hash map makes storing and retrieving the data really easy, *but* + // it does lose the order of the original array (which is sorted via the SQL). let mut vote_data = HashMap::>::new(); let mut next_truth_number = 1; for raw_vote in raw_votes { diff --git a/static_files/vote_chart.js b/static_files/vote_chart.js index 7bdae87..45b3ab2 100644 --- a/static_files/vote_chart.js +++ b/static_files/vote_chart.js @@ -32,11 +32,15 @@ async function main() { return; } + // Sort by label to maintain the same graph order, as it goes through a hash map in the backend. + datasets.sort((a, b) => a.label > b.label) + const chart_canvas = document.getElementById("vote_chart") let chart function create_chart(keys, data) { let main_axis; let aspect_ratio; + // TODO: Move to the event, to check and not re-create for no reason. if (window.innerWidth > window.innerHeight) { main_axis = 'x' aspect_ratio = 2 From 8fd8ce8220657b61cdbec4f080fbec5db2e88c3b Mon Sep 17 00:00:00 2001 From: trotFunky Date: Wed, 24 Jul 2024 16:27:43 +0100 Subject: [PATCH 02/13] vote_chart: Remove test code This was written for testing the chart before plugging in to the database, remove it now that is not useful anymore. --- static_files/vote_chart.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/static_files/vote_chart.js b/static_files/vote_chart.js index 45b3ab2..5bc5e54 100644 --- a/static_files/vote_chart.js +++ b/static_files/vote_chart.js @@ -1,14 +1,4 @@ -// const names = ["Bystus", "Dory", "Fen", "Lucky", "Nico", "Peran", "trot"] -// -// -// let data = []; -// for (let i = 0; i < 7; i++) { -// data.push({ -// parsing: true, -// label: names[i], -// data: Array.from(keys, () => Math.round(Math.random()*1.33))}) -// } async function main() { const vote_response = await fetch(document.URL+"/votes"); if (!vote_response.ok) { From d0843d600e0d62e40887711883f112e86a8aa5a4 Mon Sep 17 00:00:00 2001 From: trotFunky Date: Wed, 24 Jul 2024 16:56:08 +0100 Subject: [PATCH 03/13] vote_chart: Control the graph ratio switch The media query used to switch the ratio of the graph would only fire when switching from landscape to portrait, or the other way around. This makes controlling the point where the graph switches to vertical is only possible at this exact point. Introduce a limit aspect-ratio that controls the change and use it for the MediaQuery as well. Make sure we don't delete/recreate the chart for no reason as well. --- static_files/vote_chart.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/static_files/vote_chart.js b/static_files/vote_chart.js index 5bc5e54..fe93e11 100644 --- a/static_files/vote_chart.js +++ b/static_files/vote_chart.js @@ -1,3 +1,4 @@ +const limit_ratio = 1.3 async function main() { const vote_response = await fetch(document.URL+"/votes"); @@ -27,18 +28,32 @@ async function main() { const chart_canvas = document.getElementById("vote_chart") let chart + let previous_orientation function create_chart(keys, data) { let main_axis; let aspect_ratio; - // TODO: Move to the event, to check and not re-create for no reason. - if (window.innerWidth > window.innerHeight) { + let orientation + + if (window.innerWidth / window.innerHeight > limit_ratio) { + orientation = "landscape" main_axis = 'x' aspect_ratio = 2 } else { + orientation = "portrait" main_axis = 'y' aspect_ratio = 0.5 } + // Don't re-create the chart for no reason. + if (orientation === previous_orientation) { + console.log("bijour") + return; + } else { + console.log("badour") + } + + previous_orientation = orientation + if ( chart ) { chart.destroy() } @@ -69,7 +84,7 @@ async function main() { create_chart(keys, datasets) } - const orientation_query = matchMedia("screen and (orientation:portrait)"); + const orientation_query = matchMedia(`(aspect-ratio < ${limit_ratio})`); orientation_query.onchange = update_chart_ratio create_chart(keys, datasets) From 4c89a0783d2e1fd8af2ede21a2231877f84f2097 Mon Sep 17 00:00:00 2001 From: trotFunky Date: Wed, 24 Jul 2024 18:08:13 +0100 Subject: [PATCH 04/13] vote_chart: Add title to the graph --- static_files/vote_chart.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/static_files/vote_chart.js b/static_files/vote_chart.js index fe93e11..8dd6869 100644 --- a/static_files/vote_chart.js +++ b/static_files/vote_chart.js @@ -75,6 +75,13 @@ async function main() { y: { stacked: true } + }, + plugins: { + title: { + display: true, + position: 'bottom', + text: 'Répartition des suppositions' + } } } }) From f74ed20e80f9f92a687d118e79b50e470cb6ff0f Mon Sep 17 00:00:00 2001 From: trotFunky Date: Wed, 24 Jul 2024 18:11:37 +0100 Subject: [PATCH 05/13] vote_chart: Add custom colors I didn't like the default colors, so introduce a new set of them and set them to the datasets. Do it after the sort, otherwise the colors wouldn't stay consistent. --- static_files/vote_chart.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/static_files/vote_chart.js b/static_files/vote_chart.js index 8dd6869..09850d4 100644 --- a/static_files/vote_chart.js +++ b/static_files/vote_chart.js @@ -1,5 +1,5 @@ const limit_ratio = 1.3 - +const colors = ['#b6b8fc', '#b6f4fc', '#fcb6cc', '#e0fcb6', '#fcdcb6', '#b6fcc8', '#f0b6fc'] async function main() { const vote_response = await fetch(document.URL+"/votes"); if (!vote_response.ok) { @@ -23,8 +23,12 @@ async function main() { return; } + // Sort by label to maintain the same graph order, as it goes through a hash map in the backend. datasets.sort((a, b) => a.label > b.label) + for (let i = 0; i < datasets.length; i++) { + datasets[i].backgroundColor = colors[i % colors.length] + } const chart_canvas = document.getElementById("vote_chart") let chart From f7e1218f210bbb92399861eef2086ef7185278f4 Mon Sep 17 00:00:00 2001 From: trotFunky Date: Thu, 25 Jul 2024 16:54:19 +0100 Subject: [PATCH 06/13] truths: Change truth creation URL As we will be able to create a new week, /new to /new_truth to differentiate it from the week creation URL. --- src/truth.rs | 2 +- templates/index.html.tera | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/truth.rs b/src/truth.rs index dd4c0ea..ae96101 100644 --- a/src/truth.rs +++ b/src/truth.rs @@ -53,7 +53,7 @@ pub async fn edit_truth(week: u8, truth_number: u8, form: Form, Redirect::to(uri!("/")) } -#[post("//new", data="
")] +#[post("//new_truth", data="")] pub async fn create_truth(week: u8, form: Form, mut db: Connection, cookies: &CookieJar<'_>) -> Redirect { let user = auth::get_user(week, &mut db, cookies).await; diff --git a/templates/index.html.tera b/templates/index.html.tera index ae99a96..9c8f302 100644 --- a/templates/index.html.tera +++ b/templates/index.html.tera @@ -91,7 +91,7 @@ {% if user.is_admin == true %}

Nouvelle vérité

- + {% include "truth_editor" %}
From 2b3dd28fed2bcec21815009ecd0628c4019fa8ec Mon Sep 17 00:00:00 2001 From: trotFunky Date: Thu, 25 Jul 2024 18:32:53 +0100 Subject: [PATCH 07/13] style: Fix edit box clipping the graph It looks like the width of the element is calculated *inside* the padding, so the outer width is really width+2*padding. This leads to the edit box going outside of the truth column and clipping the graph. Compute the width to take the padding into account. --- static_files/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static_files/style.css b/static_files/style.css index 3de7065..2978081 100644 --- a/static_files/style.css +++ b/static_files/style.css @@ -24,7 +24,7 @@ } .editor { - width: 100%; + width: calc(100% - 1.25em); /* The width is calculated *inside* the padding, so adjust for it. */ height: 6eM; font-size: large; padding: 0.5em; From 207ce6c1d220682415b5ff1e8d83799932f2962f Mon Sep 17 00:00:00 2001 From: trotFunky Date: Thu, 25 Jul 2024 23:29:36 +0100 Subject: [PATCH 08/13] templates: Check vote against truth id, not number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I made a choice to only log confirmed votes : there is no blank vote in the database. This means that when fetching a user's vote, if they have not voted for everyone there will be votes missing. As this is sent to the templating engine via a Vector, the ordering of the votes will be incorrect : all existing votes will follow each other, and there will be missing votes at the end. Update the select logic in the truth template to account for that by checking the truth_id directly, rather than via the index of the array. (O(N²)...) Remove 'has_vote' as this is not useful anymore. --- templates/index.html.tera | 9 +-------- templates/truth.html.tera | 12 +++++++----- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/templates/index.html.tera b/templates/index.html.tera index 9c8f302..f3c3c42 100644 --- a/templates/index.html.tera +++ b/templates/index.html.tera @@ -12,13 +12,6 @@ -{# Check if the user has a vote in advance, for readability #} -{% if user.logged_in == true and user.has_week_vote == true%} - {% set has_vote = true %} -{% else %} - {% set has_vote = false %} -{% endif -%} -

{{ title }}

@@ -78,7 +71,7 @@ {% if user.logged_in == true and user.is_admin == false %}
+ + {% endif %}
{{ week_data.rendered_text | safe }} {%- if user.is_admin == true -%} From a0b79a17ea0a583c6773db43b9dbc1b29b334aa9 Mon Sep 17 00:00:00 2001 From: trotFunky Date: Fri, 26 Jul 2024 01:08:30 +0100 Subject: [PATCH 13/13] Redirects: properly redirect to the current week Previously, most redirects targeted the root of the application. This was okay for the first part of development where only one week would be active, but would be annoying when using multiple weeks. Change those redirects to call week::week. Change the login path to be dependant on the current week as well, so it can be correctly redirected. --- README.md | 2 +- src/auth.rs | 16 ++++++++-------- src/truth.rs | 12 ++++++------ src/vote.rs | 6 +++--- src/week.rs | 6 +++--- templates/index.html.tera | 2 +- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ac48cb6..2b6d940 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ A list of things that could be implemented/added to the application, some of the - [x] Being able to change from one week to the next - [x] Create new weeks for the admin - - [ ] Proper week redirection + - [x] Proper week redirection - [ ] Correctly handle non-existing week number - [x] Add introduction to the weekly truths - [ ] Bundle static assets in the binary diff --git a/src/auth.rs b/src/auth.rs index 1fd62fa..7f6b251 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -9,7 +9,7 @@ use argon2::{Argon2, PasswordHash, PasswordVerifier}; use blake2::{Blake2b512, Digest}; use blake2::digest::FixedOutput; use crate::database_records::{AuthTokens, PlayerLoginInfo, Vote}; -use crate::database; +use crate::{database, week}; use database::Db; // TODO: Make FromRequest guard https://api.rocket.rs/v0.5/rocket/request/trait.FromRequest and split admin @@ -126,8 +126,8 @@ pub struct AuthForm { password: String } -#[post("/login", data="
")] -pub async fn login(form: Form, mut db: Connection, cookies: &CookieJar<'_>) -> Redirect { +#[post("//login", data="")] +pub async fn login(week: u8, form: Form, mut db: Connection, cookies: &CookieJar<'_>) -> Redirect { let user_search: Result = sqlx::query_as("SELECT id, is_admin, name, pwd_hash FROM Players WHERE name == $1") .bind(&form.name) .fetch_one(&mut **db) @@ -136,7 +136,7 @@ pub async fn login(form: Form, mut db: Connection, cookies: &Cooki if user_search.is_err() { error!("Login failed : invalid user {:?}, err: {:?}", form.name, user_search.err()); cookies.add(("toast_error", "Impossible de se connecter !")); - return Redirect::to(uri!("/")); + return Redirect::to(uri!(week::week(week))); } let new_user = user_search.unwrap(); @@ -144,7 +144,7 @@ pub async fn login(form: Form, mut db: Connection, cookies: &Cooki if password_hash_parse.is_err() { error!("Login failed : could not parse password hash {:?}", password_hash_parse.err()); cookies.add(("toast_error", "Impossible de se connecter !")); - return Redirect::to(uri!("/")); + return Redirect::to(uri!(week::week(week))); } let password_hash = password_hash_parse.unwrap(); @@ -168,7 +168,7 @@ pub async fn login(form: Form, mut db: Connection, cookies: &Cooki Err(error) => { error!("Login failed : coult not store auth token in database : {error}"); cookies.add(("toast_error", "Impossible de se connecter !")); - return Redirect::to(uri!("/")); + return Redirect::to(uri!(week::week(week))); } } @@ -178,11 +178,11 @@ pub async fn login(form: Form, mut db: Connection, cookies: &Cooki Err(err) => { error!("Login failed : invalid password for {:?}\nError : {err}", new_user.name); cookies.add(("toast_error", "Impossible de se connecter !")); - return Redirect::to(uri!("/")); + return Redirect::to(uri!(week::week(week))); } } - Redirect::to(uri!("/")) + Redirect::to(uri!(week::week(week))) } pub fn bypass_auth_debug(cookies: &CookieJar<'_>) { diff --git a/src/truth.rs b/src/truth.rs index ae96101..861449c 100644 --- a/src/truth.rs +++ b/src/truth.rs @@ -6,7 +6,7 @@ use rocket_db_pools::{sqlx, Connection}; use pulldown_cmark::{Parser, Options}; use sqlx::Row; -use crate::{auth, database}; +use crate::{auth, database, week}; #[derive(FromForm)] pub struct TruthUpdateForm { @@ -20,7 +20,7 @@ pub async fn edit_truth(week: u8, truth_number: u8, form: Form, let user = auth::get_user(week, &mut db, cookies).await; if !user.is_admin { cookies.add(("toast_error", "Vous n'avez pas la permission de changer la vérité.")); - return Redirect::to(uri!("/")); + return Redirect::to(uri!(week::week(week))); } let mut options = Options::empty(); @@ -50,7 +50,7 @@ pub async fn edit_truth(week: u8, truth_number: u8, form: Form, } }; - Redirect::to(uri!("/")) + Redirect::to(uri!(week::week(week))) } #[post("//new_truth", data="")] @@ -59,7 +59,7 @@ pub async fn create_truth(week: u8, form: Form, let user = auth::get_user(week, &mut db, cookies).await; if !user.is_admin { cookies.add(("toast_error", "Vous n'avez pas la permission d'ajouter de vérité.")); - return Redirect::to(uri!("/")); + return Redirect::to(uri!(week::week(week))); } let truth_number: u8 = match sqlx::query("SELECT max(number) from Truths WHERE week == $1;") @@ -76,7 +76,7 @@ pub async fn create_truth(week: u8, form: Form, if truth_number == 0 { error!("Error while getting max truth number."); cookies.add(("toast_error", "Erreur lors de l'ajout de la vérité...")); - return Redirect::to(uri!("/")); + return Redirect::to(uri!(week::week(week))); } let mut options = Options::empty(); @@ -108,5 +108,5 @@ pub async fn create_truth(week: u8, form: Form, debug!("Truth was successfully added"); - Redirect::to(uri!("/")) + Redirect::to(uri!(week::week(week))) } diff --git a/src/vote.rs b/src/vote.rs index 831068c..f3becde 100644 --- a/src/vote.rs +++ b/src/vote.rs @@ -10,7 +10,7 @@ use rocket::serde::json::Json; use rocket_db_pools::{sqlx, Connection}; -use crate::{auth, database}; +use crate::{auth, database, week}; use crate::database_records::{Vote, VotingData}; #[derive(FromForm)] @@ -25,7 +25,7 @@ pub async fn vote(week: u8, form: Form, if !user.logged_in { cookies.add(("toast_error", "Vous n'avez pas la permission de changer de vote.")); - return Redirect::to(uri!("/")); + return Redirect::to(uri!(week::week(week))); } let filtered_votes = form.truth_votes.iter().filter_map( @@ -85,7 +85,7 @@ pub async fn vote(week: u8, form: Form, debug!("Vote successful") } - Redirect::to(uri!("/")) + Redirect::to(uri!(week::week(week))) } #[derive(Serialize, Deserialize)] diff --git a/src/week.rs b/src/week.rs index 6e6de1a..1eb52db 100644 --- a/src/week.rs +++ b/src/week.rs @@ -22,7 +22,7 @@ pub async fn week(week_number: u8, mut db: Connection, cookies: &CookieJar<' .fetch_all(&mut **db).await { Ok(v) => v, Err(error) => { - println!("Some error while getting players : {error}"); + error!("Some error while getting players : {error}"); Vec::::new() } } @@ -85,7 +85,7 @@ pub async fn update_week(week: u8, raw_intro: Form, let user = auth::get_user(week, &mut db, cookies).await; if !user.is_admin { cookies.add(("toast_error", "Vous n'avez pas la permission de changer la semaine.")); - return Redirect::to(uri!("/")); + return Redirect::to(uri!(week(week))); } let mut options = Options::empty(); @@ -113,7 +113,7 @@ pub async fn update_week(week: u8, raw_intro: Form, } }; - Redirect::to(uri!("/")) + Redirect::to(uri!(week(week))) } #[post("//set_last")] diff --git a/templates/index.html.tera b/templates/index.html.tera index cd11ae4..c2f3afa 100644 --- a/templates/index.html.tera +++ b/templates/index.html.tera @@ -42,7 +42,7 @@ {% if user.logged_in == true %}

Connecté en tant que {{ user.name }}

{% else %} - +