From 5df9198be8dfd80dd111faca4d44905727d2d78f Mon Sep 17 00:00:00 2001 From: Arlo Filley Date: Mon, 28 Nov 2022 11:04:49 +0000 Subject: [PATCH] numerous bug fixes --- TODO.md | 2 + src/main.rs | 60 ++++++++-- src/sql.rs | 33 ++++++ website/api/api.js | 25 +++- website/api/user.js | 21 +++- website/assets/favicon/about.txt | 6 - website/assets/favicon/favicon_io (1).zip | Bin 36397 -> 0 bytes website/index.html | 2 + website/index.js | 2 + website/screens/accountScreen.js | 133 ++++++++++++++++++++++ website/screens/endscreen.js | 21 +++- website/screens/leaderboardscreen.js | 14 +++ website/screens/loginscreen.js | 63 ++++------ website/screens/profilescreen.js | 16 +++ website/screens/screenmanager.js | 13 +++ website/screens/settingsScreen.js | 26 +++++ website/screens/signUpScreen.js | 60 ++++------ website/screens/startscreen.js | 9 ++ website/screens/testscreen.js | 22 +++- website/ui_elements/button.js | 69 +++++++++-- website/ui_elements/canvas.js | 10 ++ website/ui_elements/menu.js | 39 ++++--- website/ui_elements/textbox.js | 20 +++- website/ui_elements/timemenu.js | 68 +++++++---- website/ui_elements/timer.js | 18 ++- 25 files changed, 598 insertions(+), 154 deletions(-) create mode 100644 TODO.md delete mode 100644 website/assets/favicon/about.txt delete mode 100644 website/assets/favicon/favicon_io (1).zip create mode 100644 website/screens/accountScreen.js create mode 100644 website/screens/settingsScreen.js diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..7af64fe --- /dev/null +++ b/TODO.md @@ -0,0 +1,2 @@ +### Backend +- [ ] hello there \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 3d0d16b..a7e365e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,12 @@ //! src/main.rs //! This file launches the web server which hosts the fileserver and the api +//! Author: Arlo Filley //! +//! TODO: +//! - move structures into a different file +//! - find a way to make logging in more secure (password hashes?) -pub mod sql; - -// relevant macros and imports for rocket.rs +// Imports for rocket #[macro_use] extern crate rocket; use rocket::{ Rocket, @@ -18,16 +20,31 @@ use rocket::{ json::Json } }; -use sql::LeaderBoardTest; + +// Imports for sql, see sql.rs for more information +pub mod sql; use crate::sql::*; +/// Test api route that returns hello world. +/// Acessible from http://url/test #[get("/")] fn test() -> String { - sql::create_database() - .expect("couldn't create database"); String::from("Hello World!") } +/// Api route that creates a database if one +/// does not already exist. +/// Acessible from http://url/api/create_database +#[get("/create_database")] +fn create_database() -> String { + sql::create_database() + .expect("couldn't create database"); + String::from("Successfully created a database") +} + + +/// the datascructure that the webserver will recieve +/// when a post is made to the http://url/api/post_test route #[derive(Deserialize)] #[serde(crate = "rocket::serde")] struct PostTest<'r> { @@ -41,6 +58,8 @@ struct PostTest<'r> { user_id: u32 } +/// Api Route that accepts test data and posts it to the database +/// Acessible from http://url/api/post_test #[post("/post_test", data = "")] fn post_test( test: Json> @@ -57,6 +76,7 @@ fn post_test( ).expect("error in posting test to tests table"); } +/// Struct representing the user #[derive(Deserialize)] #[serde(crate = "rocket::serde")] struct User<'r> { @@ -64,6 +84,9 @@ struct User<'r> { password: &'r str } +/// Route takes data about the user as a struct +/// and then creates the user in the database +/// Acessible from http://url/api/create_user #[post("/create_user", data = "")] fn create_user( user: Json> @@ -74,28 +97,47 @@ fn create_user( ).expect("Error: Couldn't create new user"); } +/// takes the users login information and returns the users user id +/// which can be used to identify their tests etc. +/// Accessible from http://url/api/login #[get("/login//")] fn login(username: &str, password: &str) -> String { let user_id = sql::find_user(username, password).expect("error finding user_id"); user_id.to_string() } +/// Gets the users tests from the database and returns it as a +/// json array. +/// Accessible from http://url/api/get_user_tests #[get("/get_user_tests/")] fn get_user_tests(user_id: u32) -> Json> { let tests = sql::get_user_tests(user_id).expect("error finding user_id"); Json(tests) } +/// Returns the highest test data from each user as +/// a json array +/// Acessible from http://url/api/leaderboard #[get("/leaderboard")] fn leaderboard() -> Json> { let leaderboard = sql::get_leaderboard(0).expect("error finding user_id"); Json(leaderboard) } +/// The main function which builds and launches the +/// webserver with all appropriate routes and fileservers #[launch] fn rocket() -> Rocket { rocket::build() - .mount("/test", routes![test]) // testing only, should return "Hello world" - .mount("/api", routes![post_test, create_user, login, get_user_tests, leaderboard]) - .mount("/typing", FileServer::from(relative!("website"))) // hosts the fileserver + // testing only, should return "Hello world" + .mount("/test", routes![test]) + // hosts the api routes necessary for the website + // to interact with the database + .mount("/api", routes![ + create_database, create_user, + post_test, login, get_user_tests, + leaderboard + ]) + // hosts the fileserver + .mount("/typing", FileServer::from(relative!("website"))) } \ No newline at end of file diff --git a/src/sql.rs b/src/sql.rs index 6c4ab4a..ab8a2b0 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -1,11 +1,28 @@ +//! src/sql.rs +//! This file contains all the necessary code to +//! interact with the sqlite database using functions +//! it abstracts away the rusqlite necessary to perform +//! these functions +//! Author: Arlo Filley +//! +//! TODO: +//! - put necessary structs into a different file +//! - create structure for the input of post test + +// Imports for json handling and rusqlite use rusqlite::{Connection, Result}; use rocket::serde::Serialize; +/// gets a connection to the database and returns it as +/// a rusqlite::connection fn get_connection() -> rusqlite::Connection { Connection::open("database/database.sqlite") .expect("Error creating database connection") } +/// Creates the necessary tables inside the database with +/// correct normalised links between data for later +/// querying pub fn create_database() -> Result<()> { let connection = get_connection(); @@ -37,6 +54,8 @@ pub fn create_database() -> Result<()> { Ok(()) } +/// takes necessary data about a test and creates +/// a database record with the data pub fn post_test( test_type: &str, test_length: u32, @@ -86,6 +105,8 @@ pub fn post_test( Ok(()) } +/// takes a username and password and creates a database +/// entry for a new user pub fn create_user( username: &str, password: &str @@ -113,11 +134,15 @@ pub fn create_user( Ok(()) } +/// struct which can be deserialised +/// from json to get the user_id #[derive(Debug)] pub struct User { user_id: u32, } +/// takes a username and password as inputs and returns the +/// user_id of the user if one exists pub fn find_user( username: &str, password: &str @@ -146,6 +171,8 @@ pub fn find_user( Ok(user_id) } +/// struct representing data that needs to be sent +/// to the user #[derive(Serialize)] #[serde(crate = "rocket::serde")] pub struct Test { @@ -158,6 +185,8 @@ pub struct Test { accuracy: u8, } +/// returns all the tests that a given user_id has +/// completed from the database pub fn get_user_tests( user_id: u32 ) -> Result> { @@ -189,6 +218,8 @@ pub fn get_user_tests( Ok(tests) } +/// struct that represents all the data that gets sent to the user +/// when they make a leaderboard request #[derive(Serialize)] #[serde(crate = "rocket::serde")] pub struct LeaderBoardTest { @@ -196,6 +227,8 @@ pub struct LeaderBoardTest { wpm: u8, } +/// returns a vector of leaderboard tests, where each one is the fastest words +/// per minute that a given user has achieved pub fn get_leaderboard( _user_id: u32 ) -> Result>{ diff --git a/website/api/api.js b/website/api/api.js index a65a1b5..0db01b9 100644 --- a/website/api/api.js +++ b/website/api/api.js @@ -1,3 +1,18 @@ +/** + * @file This file provides abstracted functions to interact with the api + * @author Arlo Filley + * + * TODO: + * - use localstorage for storing test data + * - allow for test storage without an account + * - validate all inputs that are sent to the server + * - give useful errors to the user if there is an api error + * - split into multiple files to more easily read code + */ + +/** + * This class provides all the useful methods to interact with the api. + */ class API { constructor() { @@ -48,6 +63,8 @@ class API { xhr.send( JSON.stringify(data) ); + + user.lastTest = data; } /** @@ -57,21 +74,25 @@ class API { const test = screenManager.screen.textbox.getLetters(); const testType = "words"; let testLength = test.length; - let testTime = screenManager.timer.getTime(); + let testTime = screenManager.screen.timer.getTime(); const testSeed = 0; const quoteId = 0; let wpm; - const accuracy = 0; const userId = Number(user.userId); let test_content = screenManager.screen.textbox.testContent; let string = ""; + let inaccurateLetters = 0; for (let letter = 0; letter < test.length; letter++) { if (test[letter] === test_content[letter]) { string += test[letter]; + } else { + inaccurateLetters += 1; } } + const accuracy = (test.length - inaccurateLetters) / test.length * 100; + // this is the wpm calculation factoring in the time of test // it assumes that all words are 5 characters long because on average // they are diff --git a/website/api/user.js b/website/api/user.js index f6ee1e0..89030fc 100644 --- a/website/api/user.js +++ b/website/api/user.js @@ -1,11 +1,28 @@ +/** + * @file This file provides an abstraction of all the data about the user + * @author Arlo Filley + * + * TODO: + * - save user preferences about colours + * - make greater useage of localstorage to store tests before signup/login + * and post them to the database if a login is made. + */ + +/** + * this class displays a number of textboxes that allows the user to input a + * username and password. Then find out the user_id of that account through the + * necessary api routes. + */ class User { constructor() { this.username = "not logged in"; this.password = "there"; this.userId = 0; - this.tests; this.leaderboard; - this.nextTest = `satisfy powerful pleasant bells disastrous mean kited is gusted romantic past taste immolate productive leak close show crabby awake handsails finicky betray long-term incompetent wander show manage toys convey hop constitute number send like off ice aboard well-made vast vacuous tramp seed force divergent flower porter fire untidy soggy fetch`; this.time = 15; + + this.tests; + this.lastTest; + this.nextTest = `satisfy powerful pleasant bells disastrous mean kited is gusted romantic past taste immolate productive leak close show crabby awake handsails finicky betray long-term incompetent wander show manage toys convey hop constitute number send like off ice aboard well-made vast vacuous tramp seed force divergent flower porter fire untidy soggy fetch`; } } \ No newline at end of file diff --git a/website/assets/favicon/about.txt b/website/assets/favicon/about.txt deleted file mode 100644 index 9bb2f0c..0000000 --- a/website/assets/favicon/about.txt +++ /dev/null @@ -1,6 +0,0 @@ -This favicon was generated using the following font: - -- Font Title: Roboto -- Font Author: Copyright 2011 Google Inc. All Rights Reserved. -- Font Source: http://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5vAx05IsDqlA.ttf -- Font License: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html)) diff --git a/website/assets/favicon/favicon_io (1).zip b/website/assets/favicon/favicon_io (1).zip deleted file mode 100644 index 7cfaf50e93ee3a39e59704dafdfdd903713184bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36397 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 V`viEivVo*HfbbtMZ%OumcmQqdKl%Uw diff --git a/website/index.html b/website/index.html index fceda9e..f3db97f 100644 --- a/website/index.html +++ b/website/index.html @@ -32,10 +32,12 @@ + + diff --git a/website/index.js b/website/index.js index 91736fe..2cd074c 100644 --- a/website/index.js +++ b/website/index.js @@ -3,6 +3,8 @@ * @author Arlo Filley */ +// these are all of the globally accessible variables that are +// needed for the site to run correctly let canvas, api, screenManager, user; /** diff --git a/website/screens/accountScreen.js b/website/screens/accountScreen.js new file mode 100644 index 0000000..0ed987c --- /dev/null +++ b/website/screens/accountScreen.js @@ -0,0 +1,133 @@ +/** + * @file This file provides a screen for the user to login through + * @author Arlo Filley + * + * TODO: + * - move into an seperated account page with signup and logout + * - make passwords not display plain text + */ + +/** + * this class displays a number of textboxes that allows the user to input a + * username and password. Then find out the user_id of that account through the + * necessary api routes. + */ +class AccountScreen { + constructor() { + this.textboxes = [ + new Textbox( + 120, 350, 500, 100, 0, true, "#000", false, + "#000", "#000", true + ), + + new Textbox( + 120, 500, 500, 100, 0, true, "#000", false, + "000", "#000", false + ) + ] + + this.buttons = [ + new Button( + 100, 300, 500, 100, 0, true, "#000", false, + "#000", "#fff", "", true, "#000", "#000", "#fff" + ), + + new Button( + 100, 450, 500, 100, 0, true, "#000", false, + "#000", "#fff", "", true, "#000", "#000", "#fff" + ), + + new Button( + 900, 300, 100, 50, 0, true, "#000", false, + "#000", "#00ff00", "Login" + ), + + new Button( + 900, 400, 100, 50, 0, true, "#000", false, + "#000", "#00ff00", "Sign up" + ), + + new Button( + 900, 500, 100, 50, 0, true, "#000", false, + "#000", "#00ff00", "Logout" + ), + ] + + this.menu = new Menu(); + + // keeps track of which textbox the user last clicked on + this.activeTextBox = 0 + } + + /** + * Draws the SignUpScreen class with all + * appropriate elements + */ + draw() { + background("#eeeee4"); + textSize(100); + fill("#000"); + text("Account", 300, 100); + for (let i = 0; i < this.buttons.length; i++) { + this.buttons[i].draw(); + } + + for (let i = 0; i < this.textboxes.length; i++) { + this.textboxes[i].draw(); + } + + textSize(30); + text("Username", 110, 275); + text("Password", 110, 425); + + if (this.buttons[0].isPressed()) { + this.textboxes[this.activeTextBox].line = false; + this.activeTextBox=0; + this.textboxes[this.activeTextBox].line = true; + } else if (this.buttons[1].isPressed()) { + this.textboxes[this.activeTextBox].line = false; + this.activeTextBox=1; + this.textboxes[this.activeTextBox].line = true; + } else if (this.buttons[2].isPressed()) { + api.login( + this.textboxes[0].getWords(), + this.textboxes[1].getWords() + ) + screenManager.setScreen(new StartScreen()); + } else if (this.buttons[3].isPressed()) { + api.createUser( + this.textboxes[0].getWords(), + this.textboxes[1].getWords() + ) + screenManager.setScreen(new StartScreen()); + } else if (this.buttons[4].isPressed()) { + api.logout(); + screenManager.setScreen(new StartScreen()); + } + this.menu.draw(); + } + + /** + * + * @param {key} key + */ + letterTyped(key) { + if (key === "Tab" && this.activeTextBox === 0) { + this.textboxes[this.activeTextBox].line = false; + this.activeTextBox=1; + this.textboxes[this.activeTextBox].line = true; + } else if (key === "Tab" && this.activeTextBox === 1) { + this.textboxes[this.activeTextBox].line = false; + this.activeTextBox=0; + this.textboxes[this.activeTextBox].line = true; + } else if (key === "Enter") { + api.login( + this.textboxes[0].getWords(), + this.textboxes[1].getWords() + ) + screenManager.setScreen(new StartScreen()); + } else { + this.textboxes[this.activeTextBox].letterTyped(key); + } + } +} \ No newline at end of file diff --git a/website/screens/endscreen.js b/website/screens/endscreen.js index 2a69753..18526fb 100644 --- a/website/screens/endscreen.js +++ b/website/screens/endscreen.js @@ -1,3 +1,16 @@ +/** + * @file This file provides a screen class that can be displayed at the end of a test + * @author Arlo Filley + * + * TODO: + * - provide the user with the data of the test that they have just + * completed, such as their wpm, accuracy, etc. + */ + +/** + * This class is for a screen that is displayed at the end of a test, + * currently it just tells the user to press start to enter another test + */ class EndScreen { constructor() { this.menu = new Menu(); @@ -8,7 +21,13 @@ class EndScreen { textSize(100); textAlign(CENTER, CENTER); fill(0); - text("Test Complete\nPress enter to start another test", 0, 0, windowWidth - 100, windowHeight - 100); + text("Test Complete", 0, 0, windowWidth - 100, windowHeight / 6); + + textSize(30); + text(`${user.lastTest.wpm} words per minute`, windowWidth / 2, 200); + text(`${user.lastTest.accuracy}% accuracy`, windowWidth / 2, 240); + text(`${user.lastTest.test_length} characters typed`, windowWidth / 2, 280); + text(`${user.lastTest.test_time}s`, windowWidth / 2, 320); this.menu.draw(); } diff --git a/website/screens/leaderboardscreen.js b/website/screens/leaderboardscreen.js index 7f4be05..a3415b6 100644 --- a/website/screens/leaderboardscreen.js +++ b/website/screens/leaderboardscreen.js @@ -1,3 +1,17 @@ +/** + * @file This file provides a leaderboard for the user to compare times. + * @author Arlo Filley + * + * TODO: + * - implement a way for the user to scroll down the tests. + * - display more tests on the screen at once, up to 15 + * - store the leaderboard in localstorage as a cache for the most recent results + */ + +/** + * this class is a screen which shows the current leaderboard from the + * results gotten via the api. + */ class LeaderboardScreen { constructor() { this.menu = new Menu(); diff --git a/website/screens/loginscreen.js b/website/screens/loginscreen.js index 4345d5c..396be9c 100644 --- a/website/screens/loginscreen.js +++ b/website/screens/loginscreen.js @@ -1,65 +1,52 @@ +/** + * @file This file provides a screen for the user to login through + * @author Arlo Filley + * + * TODO: + * - move into an seperated account page with signup and logout + * - make passwords not display plain text + */ + +/** + * this class displays a number of textboxes that allows the user to input a + * username and password. Then find out the user_id of that account through the + * necessary api routes. + */ class LoginScreen { constructor() { this.textboxes = [ new Textbox( - 120,250, - 500,100, - 0, - true, - "#000", - false, "#000", - "#000", - true + 120, 250, 500, 100, 0, true, "#000", false, + "#000", "#000", true ), new Textbox( - 120,400, - 500,100, - 0, - true, - "#000", - false,"000", - "#000", - false + 120, 400, 500, 100, 0, true, "#000", false, + "000", "#000", false ) ] this.buttons = [ new Button( - 100,200, - 500,100, - 0, - true, - "#000", - false,"#000", - "#fff","" + 100, 200, 500, 100, 0, true, "#000", false, + "#000", "#fff", "" ), new Button( - 100,350, - 500,100, - 0, - true, - "#000", - false,"#000", - "#fff","" + 100, 350, 500, 100, 0, true, "#000", false, + "#000", "#fff", "" ), new Button( - 700,300, - 100,50, - 0, - true, - "#000", - false,"#000", - "#00ff00","Login" + 700, 300, 100, 50, 0, true, "#000", false, + "#000", "#00ff00", "Login" ), ] this.menu = new Menu(); - this.activeTextBox = 0 // keeps track of which textbox the user last clicked on + this.activeTextBox = 0 } /** diff --git a/website/screens/profilescreen.js b/website/screens/profilescreen.js index 6c0c7bf..473a7fa 100644 --- a/website/screens/profilescreen.js +++ b/website/screens/profilescreen.js @@ -1,3 +1,19 @@ +/** + * @file This file provides the user with their profilescreen, where they can see their own tests + * @author Arlo Filley + * + * TODO: + * - change button name + * - provide filters for tests + * - implement a way to scroll through tests + * - create a way to have personal bests and track them + * - store tests in localstorage. + * - show user tests even if they are not logged in + */ + +/** + * This class displays all of the test data for a given user + */ class ProfileScreen { constructor() { this.menu = new Menu(); diff --git a/website/screens/screenmanager.js b/website/screens/screenmanager.js index db87124..824998e 100644 --- a/website/screens/screenmanager.js +++ b/website/screens/screenmanager.js @@ -1,3 +1,16 @@ +/** + * @file This file provides the screen manager class, with the necassary code to switch between screen classes + * @author Arlo Filley + * + * TODO: + * - implement transitions between screens in a more fluid way + */ + +/** + * This class provides the ScreenManager class stores the current screen + * and provides the getters and setters necessary to switch between screen classes + * easily + */ class ScreenManager { constructor() { this.textbox; diff --git a/website/screens/settingsScreen.js b/website/screens/settingsScreen.js new file mode 100644 index 0000000..e391f36 --- /dev/null +++ b/website/screens/settingsScreen.js @@ -0,0 +1,26 @@ +/** + * @file This file provides a screen where the user can edit the settings of their tests + * @author Arlo Filley + */ + +/** + * This class provides all of the necessary settings for the user to be able to edit test settings + */ +class settingsScreen { + constructor() { + this.menu = new Menu(); + this.timeMenu = new TimeMenu(); + } + + draw() { + textAlign(CENTER, CENTER); + background("#eeeee4"); + + textSize(100); + fill("#000"); + text("Test Settings", 450, 100); + + this.menu.draw(); + this.timeMenu.draw(); + } +} \ No newline at end of file diff --git a/website/screens/signUpScreen.js b/website/screens/signUpScreen.js index ba1e164..47fded1 100644 --- a/website/screens/signUpScreen.js +++ b/website/screens/signUpScreen.js @@ -1,58 +1,44 @@ +/** + * @file This file provides a way for the user to sign up for an account + * @author Arlo Filley + * + * TODO: + * - move into an seperated account page with signup and logout + * - make passwords not display plain text + */ + +/** + * This class provides the textboxes and methods necessary for a user + * to sign up for a new account, which it should then log them into + */ class SignUpScreen { constructor() { this.textboxes = [ new Textbox( - 120,250, - 500,100, - 0, - true, - "#000", - false, "#000", - "#000", - true + 120, 250, 500, 100,0, true, "#000", false, + "#000", "#000", true ), new Textbox( - 120,400, - 500,100, - 0, - true, - "#000", - false,"000", - "#000", - false + 120, 400, 500, 100, 0, true, "#000", false, + "000", "#000", false ) ] this.buttons = [ new Button( - 100,200, - 500,100, - 0, - true, - "#000", - false,"#000", - "#fff","" + 100, 200, 500, 100, 0, true, "#000", false, + "#000", "#fff", "" ), new Button( - 100,350, - 500,100, - 0, - true, - "#000", - false,"#000", - "#fff","" + 100, 350, 500, 100, 0, true, "#000", false, + "#000", "#fff", "" ), new Button( - 700,300, - 100,50, - 0, - true, - "#000", - false,"#000", - "#00ff00","Sign Up" + 700, 300, 100, 50, 0, true, "#000", false, + "#000", "#00ff00", "Sign Up" ), ] diff --git a/website/screens/startscreen.js b/website/screens/startscreen.js index ef35c69..2bcedf1 100644 --- a/website/screens/startscreen.js +++ b/website/screens/startscreen.js @@ -1,3 +1,12 @@ +/** + * @file This file is the base screen when the user visits the site + * @author Arlo Filley + */ + +/** + * This screen class is the base screen. It provides the user with basic instructions + * and a set of menus to navigate the site + */ class StartScreen { constructor() { this.menu = new Menu(); diff --git a/website/screens/testscreen.js b/website/screens/testscreen.js index f67fb91..54aa073 100644 --- a/website/screens/testscreen.js +++ b/website/screens/testscreen.js @@ -1,15 +1,29 @@ +/** + * @file This file provides the functionality for the test + * @author Arlo Filley + * + * TODO: + * - provide a button that allows the user to exit the test + * - provide a count down to the start of a test + * - implement menus to allow the user to control the parameters of a test + */ + +/** + * this class displays the text of the test to the the screen and then takes input from the user + * displaying red if it is inaccurate, and green if it is + */ class TestScreen { constructor() { this.textbox = new Textbox(100,100,windowWidth - 200,windowHeight,0,true,"#000", false, "#000", "#000", true, true); - screenManager.timer = new Timer(0,0,windowWidth,50,0,true,"#fff", true, "#000", "#666", user.time, true); - screenManager.timer.start(); + this.timer = new Timer(0,0,windowWidth,50,0,true,"#fff", true, "#000", "#666", user.time, true); + this.timer.start(); } draw() { background("#eeeee4"); this.textbox.draw(); - screenManager.timer.draw(); - screenManager.timer.tick(); + this.timer.draw(); + this.timer.tick(); } letterTyped(key) { diff --git a/website/ui_elements/button.js b/website/ui_elements/button.js index f031aec..157f50e 100644 --- a/website/ui_elements/button.js +++ b/website/ui_elements/button.js @@ -1,3 +1,17 @@ +/** + * @file This file provides the button class, which can + * be checked for clicks + * @author Arlo Filley + * + * TODO: + * - implement visual changes (borders, etc) + * - replace with methods with getters and setters + */ + + +/** + * Button class, a rectangle that can be checked for mouse clicks + */ class Button { // this is the doc comment for the Timer class /** @@ -15,7 +29,16 @@ class Button { * @param {bool} pBar * @param {string} Label */ - constructor(pX, pY, pWidth, pHeight, pLayer, pVisible, pTextColor, pBorder, pBorderColor, pBackgroundColor, pLabel) { + constructor(pX = 100, pY = 100, + pWidth = 200, pHeight = 30, + pLayer = 0, pVisible = true, + pTextColor = "#fff", + pBorder = false, pBorderColor = "#000", + pBackgroundColor = "#000", + pLabel = "Default Button", + pHoverBorder = true, pHoverBorderColor = "#000", + pHoverTextColor = "#000", pHoverBackgroundColor = "#00ff00" + ) { this.x = pX; this.y = pY; this.width = pWidth; @@ -27,9 +50,16 @@ class Button { this.borderColor = pBorderColor; this.backgroundColor = pBackgroundColor; this.label = pLabel; + + // Attributes to control the look of the button + // when the user is hovering over it + this.hoverBorder = pHoverBorder; + this.hoverBorderColor = pHoverBorderColor; + this.hoverTextColor = pHoverTextColor; + this.hoverBackgroundColor = pHoverBackgroundColor; } - getX() { + getx() { return this.x; } @@ -119,11 +149,11 @@ class Button { /** * This functions returns more - * @returns */ isPressed() { - if (!mouseIsPressed) { - // a unique p5.js value that checks if the mouse is clicked + if (!this.visible) { + return; + } else if (!mouseIsPressed) { // a unique p5.js value that checks if the mouse is clicked return false; } @@ -138,12 +168,31 @@ class Button { * This function draws the button with the label */ draw() { + if (!this.visible) { + return; + } textSize(20); - fill(this.backgroundColor); - rect(this.x, this.y, this.width, this.height); - fill(this.textColor); - text(this.label, this.x, this.y, this.width, this.height); - // passing 4 arguments to this function means the text will wrap within this box + if (mouseX > this.x && mouseX < this.x + this.width && mouseY > this.y && mouseY < this.y + this.height) { + + if (this.hoverBorder) { + strokeWeight(2); + stroke(this.hoverBorderColor) + } else { + noStroke(); + } + fill(this.hoverBackgroundColor); + rect(this.x, this.y, this.width, this.height); + + noStroke(); + fill(this.hoverTextColor); + text(this.label, this.x, this.y, this.width, this.height); + } else { + fill(this.backgroundColor); + rect(this.x, this.y, this.width, this.height); + fill(this.textColor); + text(this.label, this.x, this.y, this.width, this.height); + } + noStroke(); } } \ No newline at end of file diff --git a/website/ui_elements/canvas.js b/website/ui_elements/canvas.js index 69ff9ee..7aa8eac 100644 --- a/website/ui_elements/canvas.js +++ b/website/ui_elements/canvas.js @@ -1,3 +1,13 @@ +/** + * @file This file provides a canvas class wrapper for the p5.js canvas + * @author Arlo Filley + * + */ + +/** + * this class provides a wrapper around the + * p5.js canvas, with easier methods to work with. + */ class Canvas { constructor() { this.x = 0; diff --git a/website/ui_elements/menu.js b/website/ui_elements/menu.js index b69a103..3fc3776 100644 --- a/website/ui_elements/menu.js +++ b/website/ui_elements/menu.js @@ -1,35 +1,42 @@ +/** + * @file This file provides a menu class to allow the user to easily navigate the site + * @author Arlo Filley + * + * TODO: + * - more sensible button names for easier navigation + */ + +/** + * this class provides a menu with all the relevent buttons the user will need, + * it also handles when the user presses a button, by creating the correct screen + */ class Menu { constructor() { this.buttons = [ - new Button(0,0,100,30,0,true,"#fff",false,"#000","#000","Sign Up"), - new Button(110,0,100,30,0,true,"#fff",false,"#000","#000","Login"), - new Button(220,0,100,30,0,true,"#fff",false,"#000","#000","Logout"), - new Button(330,0,100,30,0,true,"#fff",false,"#000","#000","Profile"), - new Button(440,0,100,30,0,true,"#fff",false,"#000","#000","Test"), - new Button(550,0,140,30,0,true,"#fff",false,"#000","#000","Leaderboard"), + new Button(0, 0, 100, 30, 0, true, "#fff", false, "#000", "#000", "Account"), + new Button(101, 0, 130, 30, 0, true, "#fff", false, "#000", "#000", "Test Data"), + new Button(232, 0, 140, 30, 0, true, "#fff", false, "#000", "#000", "Start Test"), + new Button(373, 0, 140, 30, 0, true, "#fff", false, "#000", "#000", "Leaderboard"), + new Button(514, 0, 180, 30, 0, true, "#fff", false, "#000", "#000", "Test Settings") ] - this.timeMenu = new TimeMenu(); } draw() { + textAlign(CENTER, CENTER); for (let i = 0; i < this.buttons.length; i++) { this.buttons[i].draw() } if (this.buttons[0].isPressed()) { - screenManager.setScreen(new SignUpScreen()); + screenManager.setScreen(new AccountScreen()); } else if (this.buttons[1].isPressed()) { - screenManager.setScreen(new LoginScreen()); - } else if (this.buttons[2].isPressed()) { - api.logout(); - } else if (this.buttons[3].isPressed()) { screenManager.setScreen(new ProfileScreen()); - } else if (this.buttons[4].isPressed()) { + } else if (this.buttons[2].isPressed()) { screenManager.setScreen(new TestScreen()) - } else if (this.buttons[5].isPressed()) { + } else if (this.buttons[3].isPressed()) { screenManager.setScreen(new LeaderboardScreen()) + } else if (this.buttons[4].isPressed()) { + screenManager.setScreen(new settingsScreen()) } - - this.timeMenu.draw(); } } \ No newline at end of file diff --git a/website/ui_elements/textbox.js b/website/ui_elements/textbox.js index 92a87ea..6af37d8 100644 --- a/website/ui_elements/textbox.js +++ b/website/ui_elements/textbox.js @@ -1,3 +1,19 @@ +/** + * @file This file provides the textbox class for taking user input + * @author Arlo Filley + * + * TODO: + * - add all characters a user could press + * - refactor the code displaying the characters. It can become slow after lots of typing + * - password mode, where the charcters are hidden from the user + * - getters and setters + */ + +/** + * This class takes input from the user and displays it some form + * it handles the test input from the user, and the login and sign + * up pages. + */ class Textbox { /** * Creates a new instance of the Textbox class @@ -142,10 +158,6 @@ class Textbox { * @returns */ letterTyped(pKey) { - if (screenManager.screen.constructor.name === "TestScreen" && screenManager.timer.time === 0) { - return; - } - if (pKey === "Backspace" && this.letters.length > 0) { this.letters.pop(); this.words = this.words.substring(0, this.words.length-1) diff --git a/website/ui_elements/timemenu.js b/website/ui_elements/timemenu.js index 418bea4..725f301 100644 --- a/website/ui_elements/timemenu.js +++ b/website/ui_elements/timemenu.js @@ -1,41 +1,61 @@ +/** + * @file This file provides a time menu class for editing the length of a test + * @author Arlo Filley + * + * TODO: + * - implement visual changes (borders, etc) + * - replace with methods with getters and setters + * - highlight which option the user has chosen in some way + */ + +/** + * this class displays a dropdown menu for the user where + * they can edit the duration of a test + */ class TimeMenu { constructor() { this.buttons = [ - new Button(700,0,100,30,0,true,"#fff",false,"#000","#000","15s"), - new Button(700,30,100,30,0,true,"#fff",false,"#000","#000","30s"), - new Button(700,60,100,30,0,true,"#fff",false,"#000","#000","45s"), - new Button(700,90,100,30,0,true,"#fff",false,"#000","#000","60s"), + new Button(100, 230, 100, 30, 0, true, "#fff", false, "#000", "#000", "15s"), + new Button(100, 260, 100, 30, 0, true, "#fff", false, "#000", "#000", "30s"), + new Button(100, 290, 100, 30, 0, true, "#fff", false, "#000", "#000", "45s"), + new Button(100, 320, 100, 30, 0, true, "#fff", false, "#000", "#000", "60s"), ]; + + this.topButton = this.buttons[0]; this.dropdown = false; } draw() { - this.buttons[0].draw(); - if (this.dropdown) { for (let i = 0; i < this.buttons.length; i++) { - this.buttons[i].draw() - if (this.buttons[0].isPressed() && user.time != 15) { - user.time = 15; - this.dropdown = false; - } else if (this.buttons[1].isPressed()) { - user.time = 30; - this.dropdown = false; - } else if (this.buttons[2].isPressed()) { - user.time = 45; - this.dropdown = false; - } else if (this.buttons[3].isPressed()) { - user.time = 60; - this.dropdown = false; - } + this.buttons[i].draw() } - } - - if (this.buttons[0].isPressed()) { + if (this.buttons[0].isPressed() && user.time != 15) { + user.time = 15; + this.topButton = new Button(100, 230, 100, 30, 0, true, "#fff", false, "#000", "#000", "15s"); + this.dropdown = false; + } else if (this.buttons[1].isPressed()) { + user.time = 30; + this.topButton = new Button(100, 230, 100, 30, 0, true, "#fff", false, "#000", "#000", "30s"); + this.dropdown = false; + } else if (this.buttons[2].isPressed()) { + user.time = 45; + this.topButton = new Button(100, 230, 100, 30, 0, true, "#fff", false, "#000", "#000", "45s"); + this.dropdown = false; + } else if (this.buttons[3].isPressed()) { + user.time = 60; + this.topButton = new Button(100, 230, 100, 30, 0, true, "#fff", false, "#000", "#000", "60s"); + this.dropdown = false; + } + } else { + this.topButton.draw(); + } + + if (this.topButton.isPressed()) { this.dropdown = true; } else if (mouseIsPressed) { this.dropdown = false; - } + } } } \ No newline at end of file diff --git a/website/ui_elements/timer.js b/website/ui_elements/timer.js index 422e268..637e868 100644 --- a/website/ui_elements/timer.js +++ b/website/ui_elements/timer.js @@ -1,5 +1,21 @@ +/** + * @file This file provides a time menu class for editing the length of a test + * @author Arlo Filley + * + * TODO: + * - implement visual changes (borders, etc) + * - fix the timer number becoming invisible after a + * it drops below a certain amount of time + * - use getters and setters + * - use the millis() p5.js function for if the framerate becomes + * slowed down by the amount being drawn to the screen + */ + +/** + * This class provides the timer, which handles when a test starts and ends as well + * as providing a visual element for the user to see + */ class Timer { - // this is the doc comment for the Timer class /** * @param {int} pX * @param {int} pY