/** * @file This file provides abstracted functions to interact with the api * @author Arlo Filley * */ /** * This class provides all the useful methods to interact with the api. */ class API { constructor() { this.url = "/api"; } async createTest() { const test = screenManager.screen.textbox.getLetters(); const testType = "words"; const testLength = test.length; const testTime = screenManager.screen.timer.getTime(); const testSeed = 0; const quoteId = 0; const userId = Number(user.userId); const testContent = screenManager.screen.textbox.getTestContent(); let correctLetters = 0; test.forEach((letter, index) => { if (letter === testContent[index]) correctLetters++; }); const accuracy = Math.round((correctLetters / test.length) * 100); const wpm = Math.round((correctLetters / 5) * (60 / testTime)); const validations = [ {value: testType, type: "string"}, {value: testLength, type: "number", min: 0}, {value: testTime, type: "number", min: 1}, {value: testSeed, type: "number", min: 0}, {value: quoteId, type: "number", min: 0}, {value: wpm, type: "number", min: 0}, {value: accuracy, type: "number", min: 0, max: 100}, {value: userId, type: "number", min: 0} ]; for (let {value, type, min, max} of validations) { if (typeof value !== type || value < min || (max !== undefined && value > max)) { console.error(`Validation failed for value: ${value}, Type: ${type}, Min: ${min}, Max: ${max}`); return; } } const data = { test_type: testType, test_length: testLength, test_time: testTime, test_seed: testSeed, quote_id: quoteId, wpm: wpm, accuracy: accuracy, user_id: userId, secret: user.secret }; try { const response = await fetch(`${this.url}/user/test`, { method: "POST", headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error('Network response was not ok'); } // const responseData = await response.json(); // console.log(responseData); user.lastTest = data; // Consider updating user.lastTest only on successful response window.location.href = "/typing/pages/end/index.html" } catch (error) { console.error('There has been a problem with your fetch operation:', error); } } /** * Fetches the latest test for a given user. * @param {number} userId The user's ID. * @param {string} secret The secret key for authentication. * @returns {Promise} The latest test object or an error message. */ async getLatestUserTest() { let userId = Number(user.userId); let secret = user.secret; const url = `${this.url}/user/test/${userId}/${secret}`; try { const response = await fetch(url); if (!response.ok) { // Handle HTTP errors throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); console.log("Successfully fetched latest test:", data); return data; } catch (error) { console.error("Error fetching the latest user test:", error); return null; // or handle as needed } } /** * takes a validated name and password and sends * a post request to make a user with the given * username and password * @param {String} username * @param {String} password * @returns */ createUser( username, password ) { console.log( username, password ); const user = { username: username, password: password }; const xhr = new XMLHttpRequest(); xhr.open( "POST", `${this.url}/user/create/` ); xhr.send( JSON.stringify(user) ); xhr.onload = () => { if (xhr.status === 500) { alert("Sorry, looks like your username isn't unique"); console.error("Sorry, looks like your username isn't unique") } else { this.login(username, password); } }; } /** * takes a validated name and password and sends * a post request to make a user with the given * username and password * @param {String} username * @param {String} password * @param {boolean} initial * @returns */ login(pUsername, pPassword, initial = false) { // If Local Storage has the information we need there is no need to make a request to the server if (localStorage.getItem("username") === pUsername || (initial && localStorage.length === 3) ) { user.userId = localStorage.getItem("userId"); user.secret = localStorage.getItem("secret"); user.username = localStorage.getItem("username"); return } // Variable Validation if (pUsername == undefined || pPassword == undefined) { return } let xhr = new XMLHttpRequest(); xhr.open('GET', `${this.url}/user/login/${pUsername}/${pPassword}`); xhr.send(); xhr.onload = () => { let response = JSON.parse(xhr.response); // If there is an error with the login we need if (xhr.response === null) { alert("Error Logging in, maybe check your password"); return } user.userId = response.user_id; user.username = pUsername user.secret = response.secret; localStorage.setItem("userId", user.userId); localStorage.setItem("username", pUsername); localStorage.setItem("secret", user.secret); }; } logout() { user = new User(); user.username = "no one"; user.password = ""; user.userId = 0; user.tests = []; localStorage.clear(); this.getTest(); } getUserTests() { if (user.userId === 0) { user.tests = undefined; return; } let xhr = new XMLHttpRequest(); xhr.open('GET', `${this.url}/user/tests/${user.userId}/${user.secret}`); xhr.send(); xhr.onload = () => { user.tests = JSON.parse(xhr.response); }; } async getLeaderBoard() { try { const response = await fetch(`${this.url}/leaderboard/`); if (!response.ok) { throw new Error('Network response was not ok.'); } user.leaderboard = await response.json(); } catch (error) { console.error('Failed to fetch leaderboard:', error); } } async getTest() { try { const response = await fetch(`${this.url}/test/`); if (!response.ok) { throw new Error('Network response was not ok.'); } const effectiveWidth = (windowWidth - 200) / 13; let textArr = await response.json(); let finalText = []; let text = ""; for (let i = 0; i < textArr.length; i++) { if (text.length + textArr[i].length < effectiveWidth) { text += `${textArr[i]} ` } else { finalText.push(text.substring(0,text.length-1)); text = `${textArr[i]} `; } } user.nextTest = finalText; } catch (error) { console.error('Failed to fetch a test:', error); } } }