diff --git a/backend/models/user.js b/backend/models/user.js index 7d36ef76b..0363e12ea 100644 --- a/backend/models/user.js +++ b/backend/models/user.js @@ -1,9 +1,50 @@ const mongoose = require("mongoose"); const Schema = mongoose.Schema; +const tagSchema = new Schema({ + name: { type: String, required: true }, + //should be pb storage here i think +}); + +const resultSchema = new Schema({ + wpm: { type: Number, required: true }, + rawWpm: { type: Number, required: true }, + correctChars: { type: Number, required: true }, + incorrectChars: { type: Number, required: true }, + allChars: { type: Number, required: true }, + acc: { type: Number, required: true }, + mode: { type: String, required: true }, //is this always string type? + mode2: { type: Number, required: true }, //is this always number type? + quoteLength: { type: Number, required: true }, + timestamp: { type: Number, required: true }, //can this be removed if timestamps are added to mongoose + language: { type: String, required: true }, + restartCount: { type: Number, required: true }, + incompleteTestSeconds: { type: Number, required: true }, + testDuration: { type: Number, required: true }, + afkDuration: { type: Number, required: true }, + theme: { type: String, required: true }, + tags: [{ type: String }], //the id of each tag + keySpacing: { type: String, required: true }, + keyDuration: { type: String, required: true }, + consistency: { type: Number, required: true }, + keyConsistency: { type: Number, required: true }, + chartData: { + //should chartData have it's own schema? + wpm: [{ type: Number }], + raw: [{ type: Number }], + err: [{ type: Number }], + }, + customText: { type: Schema.Types.Mixed }, + keySpacingStats: String, //not sure that this needs to exist, it's set as null in all of mine + name: { type: String, required: true }, //name of the user who took the test //should probably be typistName/username or something + isPb: { type: Boolean, required: true }, +}); + +const configSchema = new Schema({}); + const userSchema = new Schema( { - results: [{ type: Schema.Types.Mixed, default: {} }], + results: [{ type: resultSchema, default: {} }], personalBests: { custom: { type: Schema.Types.Mixed, default: {} }, time: { type: Schema.Types.Mixed, default: {} }, @@ -12,7 +53,7 @@ const userSchema = new Schema( }, name: { type: String, required: true }, presets: [{ type: Schema.Types.Mixed, default: {} }], - tags: [{ type: Schema.Types.Mixed, default: {} }], + tags: [{ type: tagSchema, default: {} }], favouriteThemes: [], refactored: { type: Boolean, default: true }, banned: { type: Boolean, default: false }, diff --git a/backend/server.js b/backend/server.js index 423e04bce..35cb7829a 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1184,6 +1184,101 @@ app.post("/api/checkLeaderboards", (req, res) => { }); */ +function isTagPresetNameValid(name) { + if (name === null || name === undefined || name === "") return false; + if (name.length > 16) return false; + return /^[0-9a-zA-Z_.-]+$/.test(name); +} + +//could use /api/tags/add instead +app.post("/api/addTag", authenticateToken, (req, res) => { + try { + if (!isTagPresetNameValid(req.body.tagName)) return { resultCode: -1 }; + User.findOne({ name: req.name }, (err, user) => { + if (err) res.status(500).send({ error: err }); + if (user.tags.includes(req.body.tagName)) { + return { resultCode: -999, message: "Duplicate tag" }; + } + const tagObj = { name: req.body.tagName }; + user.tags.push(tagObj); + user.save(); + }) + .then((updatedUser) => { + console.log(`user ${req.name} created a tag: ${req.body.tagName}`); + return { + resultCode: 1, + id: updatedUser.tags[updatedUser.tags.length - 1]._id, + }; + }) + .catch((e) => { + console.error( + `error while creating tag for user ${req.name}: ${e.message}` + ); + return { resultCode: -999, message: e.message }; + }); + } catch (e) { + console.error(`error adding tag for ${req.name} - ${e}`); + return { resultCode: -999, message: e.message }; + } +}); + +app.post("/api/editTag", authenticateToken, (req, res) => { + try { + if (!isTagPresetNameValid(req.body.tagName)) return { resultCode: -1 }; + User.findOne({ name: req.name }, (err, user) => { + if (err) res.status(500).send({ error: err }); + for (var i = 0; i < user.tags.length; i++) { + if (user.tags[i]._id == req.body.tagId) { + user.tags[i].name = req.body.tagName; + } + } + user.save(); + }) + .then((updatedUser) => { + console.log(`user ${req.name} updated a tag: ${req.name}`); + return { + resultCode: 1, + }; + }) + .catch((e) => { + console.error( + `error while updating tag for user ${req.name}: ${e.message}` + ); + return { resultCode: -999, message: e.message }; + }); + } catch (e) { + console.error(`error updating tag for ${req.name} - ${e}`); + return { resultCode: -999, message: e.message }; + } +}); + +app.post("/api/removeTag", authenticateToken, (req, res) => { + try { + User.findOne({ name: req.name }, (err, user) => { + if (err) res.status(500).send({ error: err }); + for (var i = 0; i < user.tags.length; i++) { + if (user.tags[i]._id == req.body.tagId) { + user.tags.splice(i, 1); + } + } + user.save(); + }) + .then((updatedUser) => { + console.log(`user ${req.name} deleted a tag`); + return { + resultCode: 1, + }; + }) + .catch((e) => { + console.error(`error deleting tag for user ${req.name}: ${e.message}`); + return { resultCode: -999 }; + }); + } catch (e) { + console.error(`error deleting tag for ${req.name} - ${e}`); + return { resultCode: -999 }; + } +}); + // ANALYTICS API function newAnalyticsEvent(event, data) { diff --git a/functions/index.js b/functions/index.js index b9fb0e056..1fe5571af 100644 --- a/functions/index.js +++ b/functions/index.js @@ -759,96 +759,6 @@ function updateDiscordRole(discordId, wpm) { }); } -function isTagPresetNameValid(name) { - if (name === null || name === undefined || name === "") return false; - if (name.length > 16) return false; - return /^[0-9a-zA-Z_.-]+$/.test(name); -} - -exports.addTag = functions.https.onCall((request, response) => { - try { - if (!isTagPresetNameValid(request.name)) { - return { resultCode: -1 }; - } else { - return db - .collection(`users/${request.uid}/tags`) - .add({ - name: request.name, - }) - .then((e) => { - console.log(`user ${request.uid} created a tag: ${request.name}`); - return { - resultCode: 1, - id: e.id, - }; - }) - .catch((e) => { - console.error( - `error while creating tag for user ${request.uid}: ${e.message}` - ); - return { resultCode: -999, message: e.message }; - }); - } - } catch (e) { - console.error(`error adding tag for ${request.uid} - ${e}`); - return { resultCode: -999, message: e.message }; - } -}); - -exports.editTag = functions.https.onCall((request, response) => { - try { - if (!isTagPresetNameValid(request.name)) { - return { resultCode: -1 }; - } else { - return db - .collection(`users/${request.uid}/tags`) - .doc(request.tagid) - .update({ - name: request.name, - }) - .then((e) => { - console.log(`user ${request.uid} updated a tag: ${request.name}`); - return { - resultCode: 1, - }; - }) - .catch((e) => { - console.error( - `error while updating tag for user ${request.uid}: ${e.message}` - ); - return { resultCode: -999, message: e.message }; - }); - } - } catch (e) { - console.error(`error updating tag for ${request.uid} - ${e}`); - return { resultCode: -999, message: e.message }; - } -}); - -exports.removeTag = functions.https.onCall((request, response) => { - try { - return db - .collection(`users/${request.uid}/tags`) - .doc(request.tagid) - .delete() - .then((e) => { - console.log(`user ${request.uid} deleted a tag`); - return { - resultCode: 1, - }; - }) - .catch((e) => { - console.error( - `error deleting tag for user ${request.uid}: ${e.message}` - ); - return { resultCode: -999 }; - }); - } catch (e) { - console.error(`error deleting tag for ${request.uid} - ${e}`); - return { resultCode: -999 }; - } -}); - exports.updateResultTags = functions.https.onCall((request, response) => { try { let validTags = true; @@ -1122,21 +1032,6 @@ exports.removePreset = functions.https.onCall((request, response) => { } }); -function generate(n) { - var add = 1, - max = 12 - add; - - if (n > max) { - return generate(max) + generate(n - max); - } - - max = Math.pow(10, n + add); - var min = max / 10; // Math.pow(10, n) basically - var number = Math.floor(Math.random() * (max - min + 1)) + min; - - return ("" + number).substring(add); -} - class Leaderboard { constructor(size, mode, mode2, type, starting) { this.size = size; diff --git a/src/js/account.js b/src/js/account.js index 605fb622a..71519053a 100644 --- a/src/js/account.js +++ b/src/js/account.js @@ -224,7 +224,7 @@ function loadMoreLines() { if (result.tags !== undefined && result.tags.length > 0) { result.tags.forEach((tag) => { DB.getSnapshot().tags.forEach((snaptag) => { - if (tag === snaptag.id) { + if (tag === snaptag._id) { tagNames += snaptag.name + ", "; } }); diff --git a/src/js/account/result-filters.js b/src/js/account/result-filters.js index 485ea2789..82f9ae144 100644 --- a/src/js/account/result-filters.js +++ b/src/js/account/result-filters.js @@ -98,7 +98,7 @@ export function getFilter(group, filter) { export function loadTags(tags) { tags.forEach((tag) => { - defaultResultFilters.tags[tag.id] = true; + defaultResultFilters.tags[tag._id] = true; }); } @@ -301,7 +301,7 @@ export function updateTags() { DB.getSnapshot().tags.forEach((tag) => { $( ".pageAccount .content .filterButtons .buttonsAndTitle.tags .buttons" - ).append(`
`); + ).append(` `); }); } else { $(".pageAccount .content .filterButtons .buttonsAndTitle.tags").addClass( @@ -408,7 +408,7 @@ $(".pageAccount .topFilters .button.currentConfigFilter").click((e) => { DB.getSnapshot().tags.forEach((tag) => { if (tag.active === true) { filters["tags"]["none"] = false; - filters["tags"][tag.id] = true; + filters["tags"][tag._id] = true; } }); diff --git a/src/js/commandline-lists.js b/src/js/commandline-lists.js index 562f4e223..90e80da7a 100644 --- a/src/js/commandline-lists.js +++ b/src/js/commandline-lists.js @@ -213,11 +213,11 @@ export function updateTagCommands() { } commandsTags.list.push({ - id: "toggleTag" + tag.id, + id: "toggleTag" + tag._id, display: dis, sticky: true, exec: () => { - TagController.toggle(tag.id); + TagController.toggle(tag._id); TestUI.updateModesNotice(); let txt = tag.name; @@ -228,11 +228,11 @@ export function updateTagCommands() { } if (Commandline.isSingleListCommandLineActive()) { $( - `#commandLine .suggestions .entry[command='toggleTag${tag.id}']` + `#commandLine .suggestions .entry[command='toggleTag${tag._id}']` ).html("Change tags > " + txt); } else { $( - `#commandLine .suggestions .entry[command='toggleTag${tag.id}']` + `#commandLine .suggestions .entry[command='toggleTag${tag._id}']` ).html(txt); } }, diff --git a/src/js/db.js b/src/js/db.js index d577bb9f5..03ffb815e 100644 --- a/src/js/db.js +++ b/src/js/db.js @@ -480,7 +480,7 @@ export async function saveConfig(config) { // export async functio(tagId, wpm) { // function cont() { // dbSnapshot.tags.forEach((tag) => { -// if (tag.id === tagId) { +// if (tag._id === tagId) { // tag.pb = wpm; // } // }); diff --git a/src/js/popups/edit-tags-popup.js b/src/js/popups/edit-tags-popup.js index 3830619ac..dc9bcfb24 100644 --- a/src/js/popups/edit-tags-popup.js +++ b/src/js/popups/edit-tags-popup.js @@ -5,6 +5,7 @@ import * as DB from "./db"; import * as CloudFunctions from "./cloud-functions"; import * as Notifications from "./notifications"; import * as Settings from "./settings"; +import axiosInstance from "./axios-instance"; export function show(action, id, name) { if (action === "add") { @@ -66,74 +67,77 @@ function apply() { hide(); if (action === "add") { Loader.show(); - CloudFunctions.addTag({ - uid: DB.currentUser().uid, - name: inputVal, - }).then((e) => { - Loader.hide(); - let status = e.data.resultCode; - if (status === 1) { - Notifications.add("Tag added", 1, 2); - DB.getSnapshot().tags.push({ - name: inputVal, - id: e.data.id, - }); - ResultTagsPopup.updateButtons(); - Settings.update(); - ResultFilters.updateTags(); - } else if (status === -1) { - Notifications.add("Invalid tag name", 0); - } else if (status < -1) { - Notifications.add("Unknown error: " + e.data.message, -1); - } - }); + axiosInstance + .post("/api/addTag", { + tagName: inputVal, + }) + .then((e) => { + Loader.hide(); + let status = e.data.resultCode; + if (status === 1) { + Notifications.add("Tag added", 1, 2); + DB.getSnapshot().tags.push({ + name: inputVal, + id: e.data.id, + }); + ResultTagsPopup.updateButtons(); + Settings.update(); + ResultFilters.updateTags(); + } else if (status === -1) { + Notifications.add("Invalid tag name", 0); + } else if (status < -1) { + Notifications.add("Unknown error: " + e.data.message, -1); + } + }); } else if (action === "edit") { Loader.show(); - CloudFunctions.editTag({ - uid: DB.currentUser().uid, - name: inputVal, - tagid: tagid, - }).then((e) => { - Loader.hide(); - let status = e.data.resultCode; - if (status === 1) { - Notifications.add("Tag updated", 1); - DB.getSnapshot().tags.forEach((tag) => { - if (tag.id === tagid) { - tag.name = inputVal; - } - }); - ResultTagsPopup.updateButtons(); - Settings.update(); - ResultFilters.updateTags(); - } else if (status === -1) { - Notifications.add("Invalid tag name", 0); - } else if (status < -1) { - Notifications.add("Unknown error: " + e.data.message, -1); - } - }); + axiosInstance + .post("/api/editTag", { + tagName: inputVal, + tagId: tagid, + }) + .then((e) => { + Loader.hide(); + let status = e.data.resultCode; + if (status === 1) { + Notifications.add("Tag updated", 1); + DB.getSnapshot().tags.forEach((tag) => { + if (tag._id === tagid) { + tag.name = inputVal; + } + }); + ResultTagsPopup.updateButtons(); + Settings.update(); + ResultFilters.updateTags(); + } else if (status === -1) { + Notifications.add("Invalid tag name", 0); + } else if (status < -1) { + Notifications.add("Unknown error: " + e.data.message, -1); + } + }); } else if (action === "remove") { Loader.show(); - CloudFunctions.removeTag({ - uid: DB.currentUser().uid, - tagid: tagid, - }).then((e) => { - Loader.hide(); - let status = e.data.resultCode; - if (status === 1) { - Notifications.add("Tag removed", 1); - DB.getSnapshot().tags.forEach((tag, index) => { - if (tag.id === tagid) { - DB.getSnapshot().tags.splice(index, 1); - } - }); - ResultTagsPopup.updateButtons(); - Settings.update(); - ResultFilters.updateTags(); - } else if (status < -1) { - Notifications.add("Unknown error: " + e.data.message, -1); - } - }); + axiosInstance + .post("/api/removeTag", { + tagId: tagid, + }) + .then((e) => { + Loader.hide(); + let status = e.data.resultCode; + if (status === 1) { + Notifications.add("Tag removed", 1); + DB.getSnapshot().tags.forEach((tag, index) => { + if (tag._id === tagid) { + DB.getSnapshot().tags.splice(index, 1); + } + }); + ResultTagsPopup.updateButtons(); + Settings.update(); + ResultFilters.updateTags(); + } else if (status < -1) { + Notifications.add("Unknown error: " + e.data.message, -1); + } + }); } } diff --git a/src/js/popups/result-tags-popup.js b/src/js/popups/result-tags-popup.js index b81a6bef8..e77bacd35 100644 --- a/src/js/popups/result-tags-popup.js +++ b/src/js/popups/result-tags-popup.js @@ -34,7 +34,7 @@ export function updateButtons() { $("#resultEditTagsPanel .buttons").empty(); DB.getSnapshot().tags.forEach((tag) => { $("#resultEditTagsPanel .buttons").append( - ` ` + ` ` ); }); } @@ -104,7 +104,7 @@ $("#resultEditTagsPanel .confirmButton").click((e) => { if (newtags.length > 0) { newtags.forEach((tag) => { DB.getSnapshot().tags.forEach((snaptag) => { - if (tag === snaptag.id) { + if (tag === snaptag._id) { tagNames += snaptag.name + ", "; } }); diff --git a/src/js/settings.js b/src/js/settings.js index 3c1bf98dc..958819c6a 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -406,11 +406,11 @@ function showActiveTags() { DB.getSnapshot().tags.forEach((tag) => { if (tag.active === true) { $( - `.pageSettings .section.tags .tagsList .tag[id='${tag.id}'] .active` + `.pageSettings .section.tags .tagsList .tag[id='${tag._id}'] .active` ).html(''); } else { $( - `.pageSettings .section.tags .tagsList .tag[id='${tag.id}'] .active` + `.pageSettings .section.tags .tagsList .tag[id='${tag._id}'] .active` ).html(''); } }); @@ -457,7 +457,7 @@ function refreshTagsSettingsSection() { tagPbString = `PB: ${tag.pb}`; } tagsEl.append(` -