numerous bug fixes
This commit is contained in:
parent
e8bec58827
commit
5df9198be8
60
src/main.rs
60
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 = "<test>")]
|
||||
fn post_test(
|
||||
test: Json<PostTest<'_>>
|
||||
@ -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 = "<user>")]
|
||||
fn create_user(
|
||||
user: Json<User<'_>>
|
||||
@ -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/<username>/<password>")]
|
||||
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/<user_id>")]
|
||||
fn get_user_tests(user_id: u32) -> Json<Vec<Test>> {
|
||||
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<Vec<LeaderBoardTest>> {
|
||||
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<Build> {
|
||||
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")))
|
||||
}
|
33
src/sql.rs
33
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<Vec<Test>> {
|
||||
@ -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<Vec<LeaderBoardTest>>{
|
||||
|
@ -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
|
||||
|
@ -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`;
|
||||
}
|
||||
}
|
@ -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))
|
Binary file not shown.
@ -32,10 +32,12 @@
|
||||
<script src="./screens/startscreen.js"></script>
|
||||
<script src="./screens/testscreen.js"></script>
|
||||
<script src="./screens/endscreen.js"></script>
|
||||
<script src="./screens/accountScreen.js"></script>
|
||||
<script src="./screens/signUpScreen.js"></script>
|
||||
<script src="./screens/loginscreen.js"></script>
|
||||
<script src="./screens/profilescreen.js"></script>
|
||||
<script src="./screens/leaderboardscreen.js"></script>
|
||||
<script src="./screens/settingsScreen.js"></script>
|
||||
|
||||
<!-- API Script Files -->
|
||||
<script src="./api/api.js"></script>
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
133
website/screens/accountScreen.js
Normal file
133
website/screens/accountScreen.js
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
26
website/screens/settingsScreen.js
Normal file
26
website/screens/settingsScreen.js
Normal file
@ -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();
|
||||
}
|
||||
}
|
@ -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"
|
||||
),
|
||||
]
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
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);
|
||||
// passing 4 arguments to this function means the text will wrap within this box
|
||||
}
|
||||
noStroke();
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -1,38 +1,58 @@
|
||||
/**
|
||||
* @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.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.buttons[0].isPressed()) {
|
||||
if (this.topButton.isPressed()) {
|
||||
this.dropdown = true;
|
||||
} else if (mouseIsPressed) {
|
||||
this.dropdown = false;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user