From 3bd02d3292619145f03aa9ed048862955fa7a335 Mon Sep 17 00:00:00 2001 From: Miodec Date: Thu, 8 Jul 2021 16:03:52 +0100 Subject: [PATCH] converted tags to the new system --- backend/api/controllers/user.js | 93 +++++++++++++-- backend/api/routes/user.js | 10 ++ backend/dao/user.js | 67 +++++++---- src/js/account.js | 2 +- src/js/account/result-filters.js | 6 +- src/js/commandline-lists.js | 4 +- src/js/popups/edit-tags-popup.js | 176 +++++++++++++++-------------- src/js/popups/result-tags-popup.js | 4 +- src/js/tag-controller.js | 4 +- src/js/test/test-logic.js | 17 +-- 10 files changed, 248 insertions(+), 135 deletions(-) diff --git a/backend/api/controllers/user.js b/backend/api/controllers/user.js index c26c592b4..3b792cd54 100644 --- a/backend/api/controllers/user.js +++ b/backend/api/controllers/user.js @@ -1,7 +1,9 @@ const UsersDAO = require("../../dao/user"); const BotDAO = require("../../dao/bot"); -const { isUsernameValid } = require("../../handlers/validation"); - +const { + isUsernameValid, + isTagPresetNameValid, +} = require("../../handlers/validation"); // import UsersDAO from "../../dao/user"; // import BotDAO from "../../dao/bot"; @@ -31,7 +33,11 @@ class UserController { static async updateName(req, res, next) { try { const { name } = req.body; - if (!isUsernameValid(name)) return res.status(400).json({message:"Username invalid. Name cannot contain special characters or contain more than 14 characters. Can include _ . and -"}); + if (!isUsernameValid(name)) + return res.status(400).json({ + message: + "Username invalid. Name cannot contain special characters or contain more than 14 characters. Can include _ . and -", + }); await UsersDAO.updateName(); return res.sendStatus(200); } catch (e) { @@ -42,9 +48,15 @@ class UserController { static async checkName(req, res, next) { try { const { name } = req.body; - if (!isUsernameValid(name)) return next({status: 400, message: "Username invalid. Name cannot contain special characters or contain more than 14 characters. Can include _ . and -"}); + if (!isUsernameValid(name)) + return next({ + status: 400, + message: + "Username invalid. Name cannot contain special characters or contain more than 14 characters. Can include _ . and -", + }); const available = await UsersDAO.isNameAvailable(name); - if(!available) return res.status(400).json({message:"Username unavailable"}); + if (!available) + return res.status(400).json({ message: "Username unavailable" }); return res.sendStatus(200); } catch (e) { return next(e); @@ -61,7 +73,7 @@ class UserController { } } - static async linkDiscord(req, res ,next){ + static async linkDiscord(req, res, next) { try { const { uid } = req.decodedToken; let discordFetch = await fetch("https://discord.com/api/users/@me", { @@ -72,13 +84,15 @@ class UserController { discordFetch = await discordFetch.json(); const did = discordFetch.id; let user; - try{ + try { user = await UsersDAO.getUserByDiscordId(did); - } catch(e) { + } catch (e) { user = null; } - if (user !== null){ - return next("This Discord account is already linked to a different account"); + if (user !== null) { + return next( + "This Discord account is already linked to a different account" + ); } await UsersDAO.linkDiscord(uid, did); await BotDAO.linkDiscord(uid, did); @@ -88,6 +102,65 @@ class UserController { } } + static async addTag(req, res, next) { + try { + const { uid } = req.decodedToken; + const { tagName } = req.body; + if (!isTagPresetNameValid(tagName)) + return res.status(400).json({ + message: + "Tag name invalid. Name cannot contain special characters or more than 16 characters. Can include _ . and -", + }); + let tag = await UsersDAO.addTag(uid, tagName); + return res.status(200).json(tag); + } catch (e) { + return next(e); + } + } + + static async clearTagPb(req, res, next) { + try { + const { uid } = req.decodedToken; + const { tagid } = req.body; + await UsersDAO.removeTagPb(uid, tagid); + return res.sendStatus(200); + } catch (e) { + return next(e); + } + } + + static async editTag(req, res, next) { + try { + const { uid } = req.decodedToken; + const { tagid, newname } = req.body; + await UsersDAO.editTag(uid, tagid, newname); + return res.sendStatus(200); + } catch (e) { + return next(e); + } + } + + static async removeTag(req, res, next) { + try { + const { uid } = req.decodedToken; + const { tagid } = req.body; + await UsersDAO.removeTag(uid, tagid); + return res.sendStatus(200); + } catch (e) { + return next(e); + } + } + + static async getTags(req, res, next) { + try { + const { uid } = req.decodedToken; + let tags = await UsersDAO.getTags(uid); + if (tags == undefined) tags = []; + return res.status(200).json(tags); + } catch (e) { + return next(e); + } + } } module.exports = UserController; diff --git a/backend/api/routes/user.js b/backend/api/routes/user.js index 6118d9869..b57941765 100644 --- a/backend/api/routes/user.js +++ b/backend/api/routes/user.js @@ -14,4 +14,14 @@ router.post("/updateName", authenticateRequest, UserController.updateName); router.get("/", authenticateRequest, UserController.getUser); +router.post("/tags/add", authenticateRequest, UserController.addTag); + +router.get("/tags", authenticateRequest, UserController.getTags); + +router.post("/tags/clearPb", authenticateRequest, UserController.clearTagPb); + +router.post("/tags/remove", authenticateRequest, UserController.removeTag); + +router.post("/tags/edit", authenticateRequest, UserController.editTag); + module.exports = router; diff --git a/backend/dao/user.js b/backend/dao/user.js index 3a9aae2fe..2aaef624c 100644 --- a/backend/dao/user.js +++ b/backend/dao/user.js @@ -2,6 +2,7 @@ const MonkeyError = require("../handlers/error"); const { mongoDB } = require("../init/mongodb"); const { checkAndUpdatePb } = require("../handlers/pb"); const { updateAuthEmail } = require("../handlers/auth"); +const uuid = require("uuid"); class UsersDAO { static async addUser(name, email, uid) { @@ -54,7 +55,7 @@ class UsersDAO { static async addTag(uid, name) { return await mongoDB() .collection("users") - .updateOne({ uid }, { $push: { tags: { name } } }); + .updateOne({ uid }, { $push: { tags: { id: uuid.v4(), name } } }); } static async getTags(uid) { @@ -68,7 +69,7 @@ class UsersDAO { if (!user) throw new MonkeyError(404, "User not found"); if ( user.tags === undefined || - user.tags.filter((t) => t._id === id).length === 0 + user.tags.filter((t) => t.id === id).length === 0 ) throw new MonkeyError(404, "Tag not found"); return await mongoDB() @@ -76,9 +77,9 @@ class UsersDAO { .updateOne( { uid: uid, - "tags._id": id, + "tags.id": id, }, - { $set: { tags: { name } } } + { $set: { "tags.$.name": name } } ); } @@ -87,20 +88,7 @@ class UsersDAO { if (!user) throw new MonkeyError(404, "User not found"); if ( user.tags === undefined || - user.tags.filter((t) => t._id === id).length === 0 - ) - throw new MonkeyError(404, "Tag not found"); - return await mongoDB() - .collection("users") - .updateOne({ uid }, { $pull: { id } }); - } - - static async removeTagPb(uid, id) { - const user = await mongoDB().collection("users").findOne({ uid }); - if (!user) throw new MonkeyError(404, "User not found"); - if ( - user.tags === undefined || - user.tags.filter((t) => t._id === id).length === 0 + user.tags.filter((t) => t.id === id).length === 0 ) throw new MonkeyError(404, "Tag not found"); return await mongoDB() @@ -108,9 +96,28 @@ class UsersDAO { .updateOne( { uid: uid, - "tags._id": id, + "tags.id": id, }, - { $pull: { tags: { personalBests: "" } } } + { $pull: { tags: { id } } } + ); + } + + static async removeTagPb(uid, id) { + const user = await mongoDB().collection("users").findOne({ uid }); + if (!user) throw new MonkeyError(404, "User not found"); + if ( + user.tags === undefined || + user.tags.filter((t) => t.id === id).length === 0 + ) + throw new MonkeyError(404, "Tag not found"); + return await mongoDB() + .collection("users") + .updateOne( + { + uid: uid, + "tags.id": id, + }, + { $set: { "tags.$.personalBests": {} } } ); } @@ -174,9 +181,20 @@ class UsersDAO { tags, } = result; + if (mode === "quote") { + return []; + } + + let tagsToCheck = []; + user.tags.forEach((tag) => { + if (tags.includes(tag.id)) { + tagsToCheck.push(tag); + } + }); + let ret = []; - tags.forEach(async (tag) => { + tagsToCheck.forEach(async (tag) => { let tagpb = checkAndUpdatePb( tag.personalBests, mode, @@ -190,10 +208,13 @@ class UsersDAO { wpm ); if (tagpb.isPb) { - ret.push(tag._id); + ret.push(tag.id); await mongoDB() .collection("users") - .updateOne({ uid }, { $set: { tags: { personalBests: tagpb.obj } } }); + .updateOne( + { uid, "tags.id": tag.id }, + { $set: { "tags.$.personalBests": tagpb.obj } } + ); } }); diff --git a/src/js/account.js b/src/js/account.js index ddea0d7c4..b0fa12a36 100644 --- a/src/js/account.js +++ b/src/js/account.js @@ -242,7 +242,7 @@ function loadMoreLines(lineIndex) { 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 03c87ab3f..b85a32bd5 100644 --- a/src/js/account/result-filters.js +++ b/src/js/account/result-filters.js @@ -99,7 +99,7 @@ export function getFilter(group, filter) { export function loadTags(tags) { tags.forEach((tag) => { - defaultResultFilters.tags[tag._id] = true; + defaultResultFilters.tags[tag.id] = true; }); } @@ -305,7 +305,7 @@ export function updateTags() { DB.getSnapshot().tags.forEach((tag) => { $( ".pageAccount .content .filterButtons .buttonsAndTitle.tags .buttons" - ).append(`
${tag.name}
`); + ).append(`
${tag.name}
`); }); } else { $(".pageAccount .content .filterButtons .buttonsAndTitle.tags").addClass( @@ -412,7 +412,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 9b9f917e8..2b35db653 100644 --- a/src/js/commandline-lists.js +++ b/src/js/commandline-lists.js @@ -232,7 +232,7 @@ export function updateTagCommands() { display: dis, sticky: true, exec: () => { - TagController.toggle(tag._id); + TagController.toggle(tag.id); TestUI.updateModesNotice(); let txt = tag.name; @@ -250,7 +250,7 @@ export function updateTagCommands() { ); } else { $( - `#commandLine .suggestions .entry[command='toggleTag${tag._id}']` + `#commandLine .suggestions .entry[command='toggleTag${tag.id}']` ).html(txt); } }, diff --git a/src/js/popups/edit-tags-popup.js b/src/js/popups/edit-tags-popup.js index 8d70d9f12..6938e3f0e 100644 --- a/src/js/popups/edit-tags-popup.js +++ b/src/js/popups/edit-tags-popup.js @@ -64,7 +64,7 @@ function hide() { } } -function apply() { +async function apply() { // console.log(DB.getSnapshot()); let action = $("#tagsWrapper #tagsEdit").attr("action"); let inputVal = $("#tagsWrapper #tagsEdit input").val(); @@ -72,100 +72,108 @@ function apply() { hide(); if (action === "add") { Loader.show(); - axiosInstance - .post("/addTag", { + let response; + try { + response = await axiosInstance.post("/user/tags/add", { 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); - } }); + } catch (e) { + Loader.hide(); + let msg = e?.response?.data?.message ?? e.message; + Notifications.add("Failed to add tag: " + msg, -1); + return; + } + Loader.hide(); + if (response.status !== 200) { + Notifications.add(response.data.message); + } else { + Notifications.add("Tag added", 1); + DB.getSnapshot().tags.push({ + name: inputVal, + _id: response.data._id, + }); + ResultTagsPopup.updateButtons(); + Settings.update(); + ResultFilters.updateTags(); + } } else if (action === "edit") { Loader.show(); - axiosInstance - .post("/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); + let response; + try { + response = await axiosInstance.post("/user/tags/edit", { + tagid, + newname: inputVal, + }); + } catch (e) { + Loader.hide(); + let msg = e?.response?.data?.message ?? e.message; + Notifications.add("Failed to edit tag: " + msg, -1); + return; + } + Loader.hide(); + if (response.status !== 200) { + Notifications.add(response.data.message); + } else { + 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 (action === "remove") { Loader.show(); - axiosInstance - .post("/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); + let response; + try { + response = await axiosInstance.post("/user/tags/remove", { tagid }); + } catch (e) { + Loader.hide(); + let msg = e?.response?.data?.message ?? e.message; + Notifications.add("Failed to remove tag: " + msg, -1); + return; + } + Loader.hide(); + if (response.status !== 200) { + Notifications.add(response.data.message); + } else { + 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 (action === "clearPb") { - //TODO: rewrite to axios - // Loader.show(); - // CloudFunctions.clearTagPb({ - // uid: firebase.auth().currentUser.uid, - // tagid: tagid, - // }).then((e) => { - // Loader.hide(); - // let status = e.data.resultCode; - // if (status === 1) { - // Notifications.add("PB cleared", 1); - // DB.getSnapshot().tags.forEach((tag, index) => { - // if (tag.id === tagid) { - // tag.personalBests = {}; - // } - // }); - // ResultTagsPopup.updateButtons(); - // Settings.update(); - // ResultFilters.updateTags(); - // } else if (status < -1) { - // Notifications.add("Unknown error: " + e.data.message, -1); - // } - // }); + Loader.show(); + let response; + try { + response = await axiosInstance.post("/user/tags/clearPb", { tagid }); + } catch (e) { + Loader.hide(); + let msg = e?.response?.data?.message ?? e.message; + Notifications.add("Failed to clear tag pb: " + msg, -1); + return; + } + Loader.hide(); + if (response.status !== 200) { + Notifications.add(response.data.message); + } else { + Notifications.add("Tag PB cleared", 1); + DB.getSnapshot().tags.forEach((tag, index) => { + if (tag.id === tagid) { + tag.personalBests = {}; + } + }); + ResultTagsPopup.updateButtons(); + Settings.update(); + ResultFilters.updateTags(); + } } } diff --git a/src/js/popups/result-tags-popup.js b/src/js/popups/result-tags-popup.js index 43daa948a..93826a793 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( - `
${tag.name}
` + `
${tag.name}
` ); }); } @@ -105,7 +105,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/tag-controller.js b/src/js/tag-controller.js index 1b252baa9..8327338d8 100644 --- a/src/js/tag-controller.js +++ b/src/js/tag-controller.js @@ -7,7 +7,7 @@ export function saveActiveToLocalStorage() { try { DB.getSnapshot().tags.forEach((tag) => { if (tag.active === true) { - tags.push(tag._id); + tags.push(tag.id); } }); // let d = new Date(); @@ -41,7 +41,7 @@ export function set(tagid, state, nosave = false) { export function toggle(tagid, nosave = false) { DB.getSnapshot().tags.forEach((tag) => { - if (tag._id === tagid) { + if (tag.id === tagid) { if (tag.active === undefined) { tag.active = true; } else { diff --git a/src/js/test/test-logic.js b/src/js/test/test-logic.js index c416cfd0c..5dc97f441 100644 --- a/src/js/test/test-logic.js +++ b/src/js/test/test-logic.js @@ -1437,7 +1437,7 @@ export function finish(difficultyFailed = false) { DB.getSnapshot().tags.forEach((tag) => { if (tag.active === true) { activeTags.push(tag); - activeTagsIds.push(tag._id); + activeTagsIds.push(tag.id); } }); } catch (e) {} @@ -1605,7 +1605,7 @@ export function finish(difficultyFailed = false) { let annotationSide = "left"; activeTags.forEach(async (tag) => { let tpb = await DB.getLocalTagPB( - tag._id, + tag.id, Config.mode, mode2, Config.punctuation, @@ -1613,13 +1613,13 @@ export function finish(difficultyFailed = false) { Config.difficulty ); $("#result .stats .tags .bottom").append(` -
${tag.name}
+
${tag.name}
`); if (Config.mode != "quote") { if (tpb < stats.wpm) { //new pb for that tag DB.saveLocalTagPB( - tag._id, + tag.id, Config.mode, mode2, Config.punctuation, @@ -1631,11 +1631,12 @@ export function finish(difficultyFailed = false) { consistency ); $( - `#result .stats .tags .bottom div[tagid="${tag._id}"] .fas` + `#result .stats .tags .bottom div[tagid="${tag.id}"] .fas` ).removeClass("hidden"); - $( - `#result .stats .tags .bottom div[tagid="${tag._id}"]` - ).attr("aria-label", "+" + Misc.roundTo2(stats.wpm - tpb)); + $(`#result .stats .tags .bottom div[tagid="${tag.id}"]`).attr( + "aria-label", + "+" + Misc.roundTo2(stats.wpm - tpb) + ); // console.log("new pb for tag " + tag.name); } else { ChartController.result.options.annotation.annotations.push({