ui changes and better secret use
5
TODO.md
@ -7,6 +7,9 @@
|
|||||||
-[ ] Make js api asynchronous
|
-[ ] Make js api asynchronous
|
||||||
-[ ] Hash passwords on front end so server plaintext password is never sent over the internet
|
-[ ] Hash passwords on front end so server plaintext password is never sent over the internet
|
||||||
-[ ] Use secret to uniquely identify
|
-[ ] Use secret to uniquely identify
|
||||||
-[ ] Scrollbars
|
-[x] Scrollbars
|
||||||
-[ ] Change Color Scheme
|
-[ ] Change Color Scheme
|
||||||
-[ ] Be able to view others accounts
|
-[ ] Be able to view others accounts
|
||||||
|
-[ ] Username gets cut off if too long
|
||||||
|
-[ ] Validate username length
|
||||||
|
-[ ] Test caret doesn't keep up
|
@ -11,7 +11,9 @@
|
|||||||
|
|
||||||
// Imports for json handling and rusqlite
|
// Imports for json handling and rusqlite
|
||||||
use sqlx::sqlite::{ SqlitePool, SqlitePoolOptions };
|
use sqlx::sqlite::{ SqlitePool, SqlitePoolOptions };
|
||||||
use rocket::serde::Serialize;
|
use rocket::serde::{ Serialize, json::Json };
|
||||||
|
|
||||||
|
use crate::typing::test::PostTest;
|
||||||
|
|
||||||
/// Contains the database connection pool
|
/// Contains the database connection pool
|
||||||
pub struct Database(SqlitePool);
|
pub struct Database(SqlitePool);
|
||||||
@ -60,11 +62,23 @@ impl Database {
|
|||||||
|
|
||||||
/// takes necessary data about a test and creates
|
/// takes necessary data about a test and creates
|
||||||
/// a database record with the data
|
/// a database record with the data
|
||||||
pub async fn create_test(&self, test_type: &str, test_length: u32, test_time: u32, test_seed: i64, quote_id: i32, wpm: u8, accuracy: u8, user_id: u32) -> Result<(), sqlx::Error> {
|
pub async fn create_test(&self, test: Json<PostTest<'_>>) -> Result<(), sqlx::Error> {
|
||||||
|
// Test to see whether the secret is correct
|
||||||
|
let user = sqlx::query!("
|
||||||
|
Select secret
|
||||||
|
From Users
|
||||||
|
Where user_id=?",
|
||||||
|
test.user_id
|
||||||
|
).fetch_one(&self.0).await?;
|
||||||
|
|
||||||
|
if user.secret != test.secret {
|
||||||
|
return Err(sqlx::Error::RowNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
sqlx::query!("
|
sqlx::query!("
|
||||||
INSERT INTO Tests (test_type, test_length, test_time, test_seed, quote_id, wpm, accuracy, user_id)
|
INSERT INTO Tests (test_type, test_length, test_time, test_seed, quote_id, wpm, accuracy, user_id)
|
||||||
VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
|
VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
|
||||||
test_type, test_length, test_time, test_seed, quote_id, wpm, accuracy, user_id
|
test.test_type, test.test_length, test.test_time, test.test_seed, test.quote_id, test.wpm, test.accuracy, test.user_id
|
||||||
).execute(&self.0).await?;
|
).execute(&self.0).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -17,23 +17,25 @@ use std::{
|
|||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub struct PostTest<'r> {
|
pub struct PostTest<'r> {
|
||||||
test_type: &'r str,
|
pub test_type: &'r str,
|
||||||
test_length: u32,
|
pub test_length: u32,
|
||||||
test_time: u32,
|
pub test_time: u32,
|
||||||
test_seed: i64,
|
pub test_seed: i64,
|
||||||
quote_id: i32,
|
pub quote_id: i32,
|
||||||
wpm: u8,
|
pub wpm: u8,
|
||||||
accuracy: u8,
|
pub accuracy: u8,
|
||||||
user_id: u32
|
pub user_id: u32,
|
||||||
|
pub secret: &'r str
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Api Route that accepts test data and posts it to the database
|
/// Api Route that accepts test data and posts it to the database
|
||||||
/// Acessible from http://url/api/post_test
|
/// Acessible from http://url/api/post_test
|
||||||
#[post("/post_test", data = "<test>")]
|
#[post("/post_test", data = "<test>")]
|
||||||
pub async fn create_test(test: Json<PostTest<'_>>, database: &State<Database>) {
|
pub async fn create_test(test: Json<PostTest<'_>>, database: &State<Database>) {
|
||||||
match database.create_test(test.test_type, test.test_length, test.test_time, test.test_seed, test.quote_id, test.wpm, test.accuracy, test.user_id).await {
|
let user_id = test.user_id;
|
||||||
|
match database.create_test(test).await {
|
||||||
Err(why) => { println!("A database error occured creating a test, {why}"); }
|
Err(why) => { println!("A database error occured creating a test, {why}"); }
|
||||||
Ok(()) => { println!("Successfully created test for {}", test.user_id); }
|
Ok(()) => { println!("Successfully created test for {user_id}"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
websites/Typing/Admin/Delete User/index.html
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Admin Panel</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../index.css">
|
||||||
|
<script src="index.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav>
|
||||||
|
<p>Arlo Filley</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../">Home</a></li>
|
||||||
|
<li><a href="./">Delete A User</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<h1>Password</h1>
|
||||||
|
<input id="password" type="password">
|
||||||
|
<table>
|
||||||
|
<tbody id="table"></tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
51
websites/Typing/Admin/Delete User/index.js
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
get();
|
||||||
|
|
||||||
|
function get() {
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', `https://arlofilley.com/api/typing/leaderboard`);
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||||
|
xhr.responseType = 'json';
|
||||||
|
xhr.send();
|
||||||
|
xhr.onload = () => {
|
||||||
|
json = xhr.response
|
||||||
|
createTable(json);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTable(pJson) {
|
||||||
|
|
||||||
|
let table = document.getElementById("table");
|
||||||
|
console.log(password)
|
||||||
|
|
||||||
|
pJson.forEach(element => {
|
||||||
|
let tr = document.createElement('tr');
|
||||||
|
let button = document.createElement('button');
|
||||||
|
let username = element.username;
|
||||||
|
button.textContent = "delete"
|
||||||
|
button.addEventListener( "click", () => {
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
let password = document.getElementById("password").value;
|
||||||
|
xhr.open('GET', `https://arlofilley.com/api/typing/delete_user/${password}/${username}/120932187`);
|
||||||
|
xhr.send();
|
||||||
|
})
|
||||||
|
|
||||||
|
create_table_element(tr, "", [element.username, element.wpm], button)
|
||||||
|
table.appendChild(tr)
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function create_table_element(tr, string, elements, button) {
|
||||||
|
if (elements.length > 0) {
|
||||||
|
for (let i = 0; i < elements.length; i++) {
|
||||||
|
let td = document.createElement('td');
|
||||||
|
td.appendChild(document.createTextNode(elements[i]));
|
||||||
|
tr.appendChild(td);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let td = document.createElement('td');
|
||||||
|
td.appendChild(document.createTextNode(string));
|
||||||
|
tr.appendChild(td);
|
||||||
|
}
|
||||||
|
tr.appendChild(button);
|
||||||
|
}
|
125
websites/Typing/Admin/index.css
Executable file
@ -0,0 +1,125 @@
|
|||||||
|
:root {
|
||||||
|
--navbar: #333333;
|
||||||
|
|
||||||
|
/* Navbar Button Properties */
|
||||||
|
--navbar-button: #058ED9;
|
||||||
|
--navbar-button-hover: #283F3B;
|
||||||
|
--navbar-button-border: #058ED9;
|
||||||
|
|
||||||
|
/* */
|
||||||
|
--background: #1f1f1f;
|
||||||
|
|
||||||
|
/* Table Properties*/
|
||||||
|
--table: #FFD07B;
|
||||||
|
--table-top:#FDB833;
|
||||||
|
|
||||||
|
/* Title Properties */
|
||||||
|
--title-background: #333333;
|
||||||
|
--title-text: #ffffff;
|
||||||
|
|
||||||
|
/* General Properties */
|
||||||
|
--text: #000000;
|
||||||
|
--border: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #111;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
background-color: #333;
|
||||||
|
height: 75px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav p {
|
||||||
|
margin-right: 700px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 38px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-family: "Montserrat", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-family: "Montserrat", sans-serif;
|
||||||
|
padding: 10px 20px;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-left: 5%;
|
||||||
|
margin-bottom: 0%;
|
||||||
|
color: #fff;
|
||||||
|
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin-left: 5%;
|
||||||
|
margin-top: 1.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 90%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 0px;
|
||||||
|
margin-left: 5%;
|
||||||
|
margin-top: 30px;
|
||||||
|
padding: 0px;
|
||||||
|
border-spacing: 0px;
|
||||||
|
border: 2px var(--border) solid;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:nth-child(n) {
|
||||||
|
background: var(--table);
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:nth-child(1) {
|
||||||
|
background: var(--table-top);
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
border: 1px black dotted;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: larger;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left: 40%;
|
||||||
|
padding: 8px;
|
||||||
|
padding-left: 12.5%;
|
||||||
|
padding-right: 12.5%;
|
||||||
|
}
|
20
websites/Typing/Admin/index.html
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Admin Panel</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="index.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav>
|
||||||
|
<p>Arlo Filley</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="./">Home</a></li>
|
||||||
|
<li><a href="./Delete User/">Delete A User</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -15,12 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
class API {
|
class API {
|
||||||
|
|
||||||
constructor() {
|
constructor() { this.url = "../api"; }
|
||||||
// this.url = "https://arlofilley.com/api/";
|
|
||||||
this.url = "../api";
|
|
||||||
// this is the url of the server
|
|
||||||
// this may have to change later on
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This takes the validated data and makes a post
|
* This takes the validated data and makes a post
|
||||||
@ -43,7 +38,8 @@ class API {
|
|||||||
'quote_id': pQuoteId,
|
'quote_id': pQuoteId,
|
||||||
'wpm': pWpm,
|
'wpm': pWpm,
|
||||||
'accuracy': pAccuracy,
|
'accuracy': pAccuracy,
|
||||||
'user_id': pUserId
|
'user_id': pUserId,
|
||||||
|
'secret': user.secret
|
||||||
}
|
}
|
||||||
|
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
@ -210,13 +206,8 @@ class API {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
login(pUsername, pPassword, initial = false) {
|
login(pUsername, pPassword, initial = false) {
|
||||||
// Variable Validation
|
|
||||||
if (pUsername == undefined || pPassword == undefined) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If Local Storage has the information we need there is no need to make a request to the server
|
// If Local Storage has the information we need there is no need to make a request to the server
|
||||||
if (localStorage.getItem("username") == pUsername) {
|
if (localStorage.getItem("username") === pUsername || (initial && localStorage.length === 3) ) {
|
||||||
user.userId = localStorage.getItem("userId");
|
user.userId = localStorage.getItem("userId");
|
||||||
user.secret = localStorage.getItem("secret");
|
user.secret = localStorage.getItem("secret");
|
||||||
user.username = localStorage.getItem("username");
|
user.username = localStorage.getItem("username");
|
||||||
@ -224,6 +215,11 @@ class API {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Variable Validation
|
||||||
|
if (pUsername == undefined || pPassword == undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let xhr = new XMLHttpRequest();
|
let xhr = new XMLHttpRequest();
|
||||||
xhr.open('GET', `${this.url}/login/${pUsername}/${pPassword}`);
|
xhr.open('GET', `${this.url}/login/${pUsername}/${pPassword}`);
|
||||||
xhr.send();
|
xhr.send();
|
||||||
@ -231,7 +227,7 @@ class API {
|
|||||||
let response = JSON.parse(xhr.response);
|
let response = JSON.parse(xhr.response);
|
||||||
|
|
||||||
// If there is an error with the login we need
|
// If there is an error with the login we need
|
||||||
if (xhr.response == null) {
|
if (xhr.response === null) {
|
||||||
alert("Error Logging in, maybe check your password");
|
alert("Error Logging in, maybe check your password");
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -249,7 +245,7 @@ class API {
|
|||||||
logout() {
|
logout() {
|
||||||
user = new User();
|
user = new User();
|
||||||
user.username = "no one";
|
user.username = "no one";
|
||||||
user.password = "none";
|
user.password = "";
|
||||||
user.userId = 0;
|
user.userId = 0;
|
||||||
user.tests = [];
|
user.tests = [];
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
|
8
websites/Typing/api/user.js
Normal file → Executable file
@ -17,7 +17,7 @@ class User {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.username = "not logged in";
|
this.username = "not logged in";
|
||||||
this.userId = 0;
|
this.userId = 0;
|
||||||
this.secret = "";
|
this.secret;
|
||||||
|
|
||||||
this.leaderboard;
|
this.leaderboard;
|
||||||
this.time = 15;
|
this.time = 15;
|
||||||
@ -27,8 +27,8 @@ class User {
|
|||||||
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.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.colorScheme = {
|
this.colorScheme = {
|
||||||
background: "#eeeee4",
|
background: "#160C1C",
|
||||||
text: "#000",
|
text: "#FBB",
|
||||||
|
|
||||||
timerBar: "#50C5B7",
|
timerBar: "#50C5B7",
|
||||||
timerText: "#000",
|
timerText: "#000",
|
||||||
@ -40,7 +40,7 @@ class User {
|
|||||||
buttonText: "#fff",
|
buttonText: "#fff",
|
||||||
buttonBorder: "#000",
|
buttonBorder: "#000",
|
||||||
|
|
||||||
buttonHoverBG: "#0f0",
|
buttonHoverBG: "#FBB",
|
||||||
buttonHoverText: "#000",
|
buttonHoverText: "#000",
|
||||||
buttonHoverBorder: "#000"
|
buttonHoverBorder: "#000"
|
||||||
}
|
}
|
||||||
|
0
websites/Typing/assets/favicon/android-chrome-192x192.png
Normal file → Executable file
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
0
websites/Typing/assets/favicon/android-chrome-512x512.png
Normal file → Executable file
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
0
websites/Typing/assets/favicon/apple-touch-icon.png
Normal file → Executable file
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
0
websites/Typing/assets/favicon/favicon-16x16.png
Normal file → Executable file
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 552 B |
0
websites/Typing/assets/favicon/favicon-32x32.png
Normal file → Executable file
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 968 B |
0
websites/Typing/assets/favicon/favicon.ico
Normal file → Executable file
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
0
websites/Typing/assets/favicon/site.webmanifest
Normal file → Executable file
0
websites/Typing/assets/fonts/RobotoMono-Medium.ttf
Normal file → Executable file
0
websites/Typing/index.css
Normal file → Executable file
0
websites/Typing/index.html
Normal file → Executable file
@ -32,7 +32,7 @@ function setup() {
|
|||||||
user = new User();
|
user = new User();
|
||||||
screenManager.setScreen(new StartScreen());
|
screenManager.setScreen(new StartScreen());
|
||||||
|
|
||||||
api.login();
|
api.login(null, null, true);
|
||||||
api.getTest();
|
api.getTest();
|
||||||
textFont(roboto);
|
textFont(roboto);
|
||||||
}
|
}
|
||||||
|
0
websites/Typing/lib/p5.js
Normal file → Executable file
0
websites/Typing/lib/p5.min.js
vendored
Normal file → Executable file
0
websites/Typing/screens/accountScreen.js
Normal file → Executable file
0
websites/Typing/screens/endscreen.js
Normal file → Executable file
51
websites/Typing/screens/leaderboardscreen.js
Normal file → Executable file
@ -17,11 +17,12 @@ class LeaderboardScreen {
|
|||||||
this.menu = new Menu();
|
this.menu = new Menu();
|
||||||
api.getLeaderBoard();
|
api.getLeaderBoard();
|
||||||
this.testButtons;
|
this.testButtons;
|
||||||
this.buttons = [
|
// this.buttons = [
|
||||||
new Button(1150, 270, 240, 120, "up"),
|
// new Button(1150, 270, 240, 120, "up"),
|
||||||
new Button(1150, 390, 240, 120, "down"),
|
// new Button(1150, 390, 240, 120, "down"),
|
||||||
]
|
// ]
|
||||||
this.offset = 0;
|
this.offset = 0;
|
||||||
|
this.scroll_bar_button = new Button(1200, 200, 20, 20, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
@ -39,29 +40,40 @@ class LeaderboardScreen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fill(user.colorScheme.testBad);
|
||||||
|
rect(1200, 270, 20, 420);
|
||||||
|
|
||||||
|
fill(user.colorScheme.testGood);
|
||||||
|
rect(1200, 270, 20, 420 / user.leaderboard.length * (this.offset + 1));
|
||||||
|
this.scroll_bar_button.height = (user.leaderboard.length)
|
||||||
|
|
||||||
|
if (this.scroll_bar_button.isPressed()) {
|
||||||
|
this.scroll_bar_button.y = mouseY - this.scroll_bar_button.height / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the furthest up the scrollbar can go is the top
|
||||||
|
if (this.scroll_bar_button.y < 270) {
|
||||||
|
this.scroll_bar_button.y = 270;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.scroll_bar_button.y > 690 - this.scroll_bar_button.height) {
|
||||||
|
this.scroll_bar_button.y = 690 - this.scroll_bar_button.height;
|
||||||
|
}
|
||||||
|
this.scroll_bar_button.draw();
|
||||||
|
|
||||||
if (this.testButtons !== undefined && this.testButtons.length > 1) {
|
if (this.testButtons !== undefined && this.testButtons.length > 1) {
|
||||||
for (let i = 0; i < this.testButtons.length; i++) {
|
for (let i = 0; i < this.testButtons.length; i++) {
|
||||||
this.testButtons[i][0].draw()
|
this.testButtons[i][0].draw()
|
||||||
this.testButtons[i][1].draw()
|
this.testButtons[i][1].draw()
|
||||||
this.testButtons[i][2].draw()
|
this.testButtons[i][2].draw()
|
||||||
}
|
}
|
||||||
if (this.buttons[0].isPressed()) {
|
|
||||||
if (this.offset > 0) {
|
|
||||||
this.offset--;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.createTestButtons(this.offset);
|
this.offset = Number((
|
||||||
} else if (this.buttons[1].isPressed()) {
|
// number of pixels from top of screen / total range of options, put to a whole integer to produce the correct offset
|
||||||
if (this.offset < user.leaderboard.length - 13) {
|
(this.scroll_bar_button.y - 270) / ((420 - this.scroll_bar_button.height) / (user.leaderboard.length - 13))
|
||||||
this.offset++;
|
).toFixed(0));
|
||||||
}
|
|
||||||
|
|
||||||
this.createTestButtons(this.offset);
|
this.createTestButtons(this.offset)
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < this.buttons.length; i++) {
|
|
||||||
this.buttons[i].draw();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
fill(user.colorScheme.text);
|
fill(user.colorScheme.text);
|
||||||
text("Looks Like There Isn't A Leaderboard", windowWidth / 2, 300);
|
text("Looks Like There Isn't A Leaderboard", windowWidth / 2, 300);
|
||||||
@ -78,7 +90,6 @@ class LeaderboardScreen {
|
|||||||
]];
|
]];
|
||||||
let j = 300;
|
let j = 300;
|
||||||
for (let i = 0 + offset; i < user.leaderboard.length && i <= 12+offset; i++) {
|
for (let i = 0 + offset; i < user.leaderboard.length && i <= 12+offset; i++) {
|
||||||
console.log(i);
|
|
||||||
this.testButtons.push([
|
this.testButtons.push([
|
||||||
new Button(400, j, 100, 30, `${i+1}`, true, true, "#000", "#000", "#fff"), // test # button
|
new Button(400, j, 100, 30, `${i+1}`, true, true, "#000", "#000", "#fff"), // test # button
|
||||||
new Button(500, j, 400, 30, `${user.leaderboard[i].username}`, true, true, "#000", "#000", "#fff"), // accuracy button
|
new Button(500, j, 400, 30, `${user.leaderboard[i].username}`, true, true, "#000", "#000", "#fff"), // accuracy button
|
||||||
|
0
websites/Typing/screens/loginscreen.js
Normal file → Executable file
72
websites/Typing/screens/profilescreen.js
Normal file → Executable file
@ -19,11 +19,12 @@ class ProfileScreen {
|
|||||||
this.menu = new Menu();
|
this.menu = new Menu();
|
||||||
api.getUserTests();
|
api.getUserTests();
|
||||||
this.testButtons;
|
this.testButtons;
|
||||||
this.buttons = [
|
// this.buttons = [
|
||||||
new Button(950, 270, 240, 120, "up"),
|
// new Button(950, 270, 240, 120, "up"),
|
||||||
new Button(950, 390, 240, 120, "down"),
|
// new Button(950, 390, 240, 120, "down"),
|
||||||
]
|
// ]
|
||||||
this.offset = 0;
|
this.offset = 0;
|
||||||
|
this.scroll_bar_button = new Button(1200, 200, 20, 20, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
@ -49,21 +50,6 @@ class ProfileScreen {
|
|||||||
this.testButtons[i][2].draw()
|
this.testButtons[i][2].draw()
|
||||||
this.testButtons[i][3].draw()
|
this.testButtons[i][3].draw()
|
||||||
}
|
}
|
||||||
if (this.buttons[0].isPressed()) {
|
|
||||||
if (this.offset > 0) {
|
|
||||||
this.offset--;
|
|
||||||
}
|
|
||||||
this.createTestButtons(this.offset);
|
|
||||||
} else if (this.buttons[1].isPressed()) {
|
|
||||||
if (user.tests.length - 13 - this.offset > 0) {
|
|
||||||
this.offset++;
|
|
||||||
}
|
|
||||||
this.createTestButtons(this.offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < this.buttons.length; i++) {
|
|
||||||
this.buttons[i].draw();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
fill(user.colorScheme.text);
|
fill(user.colorScheme.text);
|
||||||
text("Looks Like You Don't have any tests :(", windowWidth / 2, 300);
|
text("Looks Like You Don't have any tests :(", windowWidth / 2, 300);
|
||||||
@ -71,22 +57,54 @@ class ProfileScreen {
|
|||||||
|
|
||||||
fill(user.colorScheme.text);
|
fill(user.colorScheme.text);
|
||||||
text(`Logged in as ${user.username}`, windowWidth-150, 15);
|
text(`Logged in as ${user.username}`, windowWidth-150, 15);
|
||||||
|
|
||||||
|
if (user.tests === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fill(user.colorScheme.testBad);
|
||||||
|
rect(1200, 270, 20, 420);
|
||||||
|
|
||||||
|
fill(user.colorScheme.testGood);
|
||||||
|
rect(1200, 270, 20, 420 / user.tests.length * (this.offset + 1));
|
||||||
|
this.scroll_bar_button.height = (user.tests.length)
|
||||||
|
|
||||||
|
if (this.scroll_bar_button.isPressed()) {
|
||||||
|
this.scroll_bar_button.y = mouseY - this.scroll_bar_button.height / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// the furthest up the scrollbar can go is the top
|
||||||
|
if (this.scroll_bar_button.y < 270) {
|
||||||
|
this.scroll_bar_button.y = 270;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.scroll_bar_button.y > 690 - this.scroll_bar_button.height) {
|
||||||
|
this.scroll_bar_button.y = 690 - this.scroll_bar_button.height;
|
||||||
|
}
|
||||||
|
this.scroll_bar_button.draw();
|
||||||
|
|
||||||
|
this.offset = Number((
|
||||||
|
// number of pixels from top of screen / total range of options, put to a whole integer to produce the correct offset
|
||||||
|
(this.scroll_bar_button.y - 270) / ((420 - this.scroll_bar_button.height) / (user.tests.length - 13))
|
||||||
|
).toFixed(0));
|
||||||
|
this.createTestButtons(this.offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
createTestButtons(offset = 0) {
|
createTestButtons(offset = 0) {
|
||||||
this.testButtons = [[
|
this.testButtons = [[
|
||||||
new Button(400, 270, 100, 30, "test #"), // test # button
|
new Button(600, 270, 100, 30, "test #"), // test # button
|
||||||
new Button(500, 270, 100, 30, "wpm"), // wpm button
|
new Button(700, 270, 100, 30, "wpm"), // wpm button
|
||||||
new Button(600, 270, 100, 30, "accuracy"), // accuracy button
|
new Button(800, 270, 100, 30, "accuracy"), // accuracy button
|
||||||
new Button(700, 270, 240, 30, "characters typed")
|
new Button(900, 270, 240, 30, "characters typed")
|
||||||
]];
|
]];
|
||||||
let j = 300;
|
let j = 300;
|
||||||
for (let i = user.tests.length-1-offset; i >= user.tests.length-13-offset && i >= 0; i--) {
|
for (let i = user.tests.length-1-offset; i >= user.tests.length-13-offset && i >= 0; i--) {
|
||||||
this.testButtons.push([
|
this.testButtons.push([
|
||||||
new Button(400, j, 100, 30, `${i+1}`, true, true , "#000", "#000", "#fff"), // test # button
|
new Button(600, j, 100, 30, `${i+1}`, true, true , "#000", "#000", "#fff"), // test # button
|
||||||
new Button(500, j, 100, 30, `${user.tests[i].wpm}`, true, true , "#000", "#000", "#fff"), // accuracy button
|
new Button(700, j, 100, 30, `${user.tests[i].wpm}`, true, true , "#000", "#000", "#fff"), // accuracy button
|
||||||
new Button(600, j, 100, 30, `${user.tests[i].accuracy}`, true, true , "#000", "#000", "#fff"), // wpm button
|
new Button(800, j, 100, 30, `${user.tests[i].accuracy}`, true, true , "#000", "#000", "#fff"), // wpm button
|
||||||
new Button(700, j, 240, 30, `${user.tests[i].test_length}`, true, true , "#000", "#000", "#fff")
|
new Button(900, j, 240, 30, `${user.tests[i].test_length}`, true, true , "#000", "#000", "#fff")
|
||||||
])
|
])
|
||||||
j+=30;
|
j+=30;
|
||||||
}
|
}
|
||||||
|
0
websites/Typing/screens/screenmanager.js
Normal file → Executable file
5
websites/Typing/screens/settingsScreen.js
Normal file → Executable file
@ -17,9 +17,12 @@ class settingsScreen {
|
|||||||
|
|
||||||
textSize(100);
|
textSize(100);
|
||||||
fill(user.colorScheme.text);
|
fill(user.colorScheme.text);
|
||||||
text("Test Settings", 450, 100);
|
text("Test Settings", windowWidth / 2, 100);
|
||||||
|
|
||||||
this.menu.draw();
|
this.menu.draw();
|
||||||
|
|
||||||
|
fill(user.colorScheme.text);
|
||||||
|
text("Test Duration", windowWidth / 2 - 250, 265)
|
||||||
this.timeMenu.draw();
|
this.timeMenu.draw();
|
||||||
fill("#000");
|
fill("#000");
|
||||||
text(`Logged in as ${user.username}`, windowWidth-150, 15);
|
text(`Logged in as ${user.username}`, windowWidth-150, 15);
|
||||||
|
0
websites/Typing/screens/signUpScreen.js
Normal file → Executable file
0
websites/Typing/screens/startscreen.js
Normal file → Executable file
10
websites/Typing/screens/testscreen.js
Normal file → Executable file
@ -14,9 +14,13 @@
|
|||||||
*/
|
*/
|
||||||
class TestScreen {
|
class TestScreen {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.textbox = new Textbox(100,100,windowWidth - 200,windowHeight,0,true,"#000", false, "#000", "#000", true, true);
|
this.textbox = new Textbox(
|
||||||
|
100, windowHeight / 2 - 100,
|
||||||
|
windowWidth - 500,windowHeight,
|
||||||
|
0, true, "#000", false, "#000", "#000", true, true);
|
||||||
this.timer = new Timer(0,0,windowWidth,50,0,true,"#fff", true, "#000", "#666", user.time, true);
|
this.timer = new Timer(0,0,windowWidth,50,0,true,"#fff", true, "#000", "#666", user.time, true);
|
||||||
this.timerStarted = false;
|
this.timerStarted = false;
|
||||||
|
this.stopButton = new Button(0,50,200,50, "Stop Test");
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
@ -25,6 +29,10 @@ class TestScreen {
|
|||||||
if (this.timerStarted) {
|
if (this.timerStarted) {
|
||||||
this.timer.tick();
|
this.timer.tick();
|
||||||
}
|
}
|
||||||
|
this.stopButton.draw();
|
||||||
|
if (this.stopButton.isPressed()) {
|
||||||
|
screenManager.setScreen(new StartScreen())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
letterTyped(key) {
|
letterTyped(key) {
|
||||||
|
1
websites/Typing/ui_elements/button.js
Normal file → Executable file
@ -179,6 +179,7 @@ class Button {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
textSize(20);
|
textSize(20);
|
||||||
|
textAlign(CENTER, CENTER);
|
||||||
|
|
||||||
if (mouseX > this.x && mouseX < this.x + this.width && mouseY > this.y && mouseY < this.y + this.height) {
|
if (mouseX > this.x && mouseX < this.x + this.width && mouseY > this.y && mouseY < this.y + this.height) {
|
||||||
|
|
||||||
|
0
websites/Typing/ui_elements/canvas.js
Normal file → Executable file
10
websites/Typing/ui_elements/menu.js
Normal file → Executable file
@ -13,11 +13,11 @@
|
|||||||
class Menu {
|
class Menu {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.buttons = [
|
this.buttons = [
|
||||||
new Button(0, 0, 100, 30, "Account"),
|
new Button(0, 0, 200, 50, "Account"),
|
||||||
new Button(101, 0, 130, 30, "Test Data"),
|
new Button(201, 0, 200, 50, "Test Data"),
|
||||||
new Button(232, 0, 140, 30, "Start Test"),
|
new Button(402, 0, 200, 50, "Start Test"),
|
||||||
new Button(373, 0, 140, 30, "Leaderboard"),
|
new Button(603, 0, 200, 50, "Leaderboard"),
|
||||||
new Button(514, 0, 180, 30, "Test Settings")
|
new Button(804, 0, 200, 50, "Test Settings")
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
websites/Typing/ui_elements/textbox.js
Normal file → Executable file
@ -27,6 +27,9 @@ class Textbox {
|
|||||||
* @param {bool} pBorder
|
* @param {bool} pBorder
|
||||||
* @param {hexcode} pBorderColor
|
* @param {hexcode} pBorderColor
|
||||||
* @param {hexcode} pBackgroundColor
|
* @param {hexcode} pBackgroundColor
|
||||||
|
* @param {bool} pLine
|
||||||
|
* @param {bool} pIsTest
|
||||||
|
* @param {bool} pIsPassword
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
pX, pY,
|
pX, pY,
|
||||||
@ -73,6 +76,7 @@ class Textbox {
|
|||||||
|
|
||||||
this.goodColor = user.colorScheme.testGood;
|
this.goodColor = user.colorScheme.testGood;
|
||||||
this.badColor = user.colorScheme.testBad;
|
this.badColor = user.colorScheme.testBad;
|
||||||
|
this.textColor = user.colorScheme.text;
|
||||||
}
|
}
|
||||||
|
|
||||||
getX() {
|
getX() {
|
||||||
@ -271,7 +275,9 @@ class Textbox {
|
|||||||
} else {
|
} else {
|
||||||
fill("#00044");
|
fill("#00044");
|
||||||
}
|
}
|
||||||
|
|
||||||
text(this.testContent[this.currentLine-1][x], i, j);
|
text(this.testContent[this.currentLine-1][x], i, j);
|
||||||
|
|
||||||
i += 13;
|
i += 13;
|
||||||
}
|
}
|
||||||
j+= 30;
|
j+= 30;
|
||||||
@ -289,10 +295,18 @@ class Textbox {
|
|||||||
} else {
|
} else {
|
||||||
fill(this.textColor);
|
fill(this.textColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
text(this.testContent[this.currentLine][x], i, j);
|
text(this.testContent[this.currentLine][x], i, j);
|
||||||
|
|
||||||
|
if (this.letters.length > 0 && x == this.letters.length && this.line) {
|
||||||
|
fill(this.textColor)
|
||||||
|
rect(i, j-15, 1, 30)
|
||||||
|
}
|
||||||
i += 13;
|
i += 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
i = this.x;
|
i = this.x;
|
||||||
j += 30;
|
j += 30;
|
||||||
|
|
||||||
@ -343,4 +357,8 @@ class Textbox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
draw_line(line, y) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
24
websites/Typing/ui_elements/timemenu.js
Normal file → Executable file
@ -15,13 +15,14 @@
|
|||||||
class TimeMenu {
|
class TimeMenu {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.buttons = [
|
this.buttons = [
|
||||||
new Button(100, 230, 100, 30, "15s"),
|
new Button(900, 250, 100, 30, "15s"),
|
||||||
new Button(100, 260, 100, 30, "30s"),
|
new Button(900, 280, 100, 30, "30s"),
|
||||||
new Button(100, 290, 100, 30, "45s"),
|
new Button(900, 310, 100, 30, "45s"),
|
||||||
new Button(100, 320, 100, 30, "60s"),
|
new Button(900, 340, 100, 30, "60s"),
|
||||||
];
|
];
|
||||||
|
|
||||||
this.topButton = this.buttons[0];
|
this.topButton = this.buttons[0];
|
||||||
|
this.dropDownButton = new Button(1000, 250, 30, 30, "v")
|
||||||
this.dropdown = false;
|
this.dropdown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,29 +34,28 @@ class TimeMenu {
|
|||||||
|
|
||||||
if (this.buttons[0].isPressed() && user.time != 15) {
|
if (this.buttons[0].isPressed() && user.time != 15) {
|
||||||
user.time = 15;
|
user.time = 15;
|
||||||
this.topButton = new Button(100, 230, 100, 30, "15s");
|
this.topButton = new Button(900, 250, 100, 30, "15s");
|
||||||
this.dropdown = false;
|
this.dropdown = false;
|
||||||
} else if (this.buttons[1].isPressed()) {
|
} else if (this.buttons[1].isPressed()) {
|
||||||
user.time = 30;
|
user.time = 30;
|
||||||
this.topButton = new Button(100, 230, 100, 30, "30s");
|
this.topButton = new Button(900, 250, 100, 30, "30s");
|
||||||
this.dropdown = false;
|
this.dropdown = false;
|
||||||
} else if (this.buttons[2].isPressed()) {
|
} else if (this.buttons[2].isPressed()) {
|
||||||
user.time = 45;
|
user.time = 45;
|
||||||
this.topButton = new Button(100, 230, 100, 30, "45s");
|
this.topButton = new Button(900, 250, 100, 30, "45s");
|
||||||
this.dropdown = false;
|
this.dropdown = false;
|
||||||
} else if (this.buttons[3].isPressed()) {
|
} else if (this.buttons[3].isPressed()) {
|
||||||
user.time = 60;
|
user.time = 60;
|
||||||
this.topButton = new Button(100, 230, 100, 30, "60s");
|
this.topButton = new Button(900, 250, 100, 30, "60s");
|
||||||
this.dropdown = false;
|
this.dropdown = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.topButton.draw();
|
this.topButton.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.topButton.isPressed()) {
|
this.dropDownButton.draw();
|
||||||
|
if (this.dropDownButton.isPressed()) {
|
||||||
this.dropdown = true;
|
this.dropdown = true;
|
||||||
} else if (mouseIsPressed) {
|
} else if (mouseIsPressed) { this.dropdown = false };
|
||||||
this.dropdown = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|