diff --git a/functions/index.js b/functions/index.js index b80621a..adbd1d5 100644 --- a/functions/index.js +++ b/functions/index.js @@ -1,6 +1,6 @@ /* eslint-disable indent */ /* eslint-disable no-tabs */ -const functions = require("firebase-functions").region("europe-west2");//.region("europe-west2") +const levenshtein = require('js-levenshtein'); const admin = require("firebase-admin"); admin.initializeApp(); const db = admin.firestore(); @@ -238,6 +238,7 @@ exports.createProgress = functions.https.onCall((data, context) => { } return 0; }), + typo: false, } return { @@ -400,6 +401,7 @@ function cleanseVocabString(item) { * @return {integer} totalQuestions Total number of questions in the set (including duplicates after incorrect answers). * @return {integer} totalCorrect Total number of correct answers so far. * @return {integer} totalIncorrect Total number of incorrect answers so far. + * @return {boolean} typo Whether the inputted answer is likely to include a typo. */ exports.processAnswer = functions.https.onCall((data, context) => { const uid = context.auth.uid; @@ -458,6 +460,24 @@ exports.processAnswer = functions.https.onCall((data, context) => { } }); + if (!isCorrectAnswer && !progressDoc.data().typo) { + let typo = false; + cleansedSplitCorrectAnswers.forEach((answer, index, array) => { + const levDistance = levenshtein(answer, cleansedInputAnswer); + if (levDistance <= 1 || + answer.length > 5 && levDistance <= 3 || + answer.length > 10 && levDistance <= 4) { + docData.typo = true; + transaction.set(progressDocId, docData); + typo = true; + array.length = index + 1; + } + }); + if (typo) return { + typo: true, + }; + } + let prevCorrect = progressDoc.data().current_correct; var returnData = { @@ -471,8 +491,11 @@ exports.processAnswer = functions.https.onCall((data, context) => { totalQuestions: docData.questions.length, totalCorrect: docData.correct.length, totalIncorrect: docData.incorrect.length, + typo: false, } + docData.typo = false; + if (isCorrectAnswer) { if (mode === "lives") { returnData.lives = docData.lives; diff --git a/functions/package-lock.json b/functions/package-lock.json index ce6a512..2f9c9d5 100644 --- a/functions/package-lock.json +++ b/functions/package-lock.json @@ -120,9 +120,9 @@ } }, "@google-cloud/common": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.7.1.tgz", - "integrity": "sha512-BJfcV5BShbunYcn5HniebXLVp2Y6fpuesNegyar5CG8H2AKYHlKxnVID+FSwy92WAW4N2lpGdvxRsmiAn8Fc3w==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.7.2.tgz", + "integrity": "sha512-5Q9f74IbZaY6xAwJSNFy5SrGwbm1j7mpv+6A/r+K2dymjsXBH5UauB0tziaMwWoVVaMq1IQnZF9lgtfqqvxcUg==", "optional": true, "requires": { "@google-cloud/projectify": "^2.0.0", @@ -137,9 +137,9 @@ } }, "@google-cloud/firestore": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.15.0.tgz", - "integrity": "sha512-0hJnzkGeEGcFtyJ6PWYOzBT5xYcm7V1xZCQIeG0dIH989OPnWGJoM2ZUZGFNIxRB24KtbRasoy75+8dyYpKAyw==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.15.1.tgz", + "integrity": "sha512-2PWsCkEF1W02QbghSeRsNdYKN1qavrHBP3m72gPDMHQSYrGULOaTi7fSJquQmAtc4iPVB2/x6h80rdLHTATQtA==", "optional": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -171,9 +171,9 @@ "optional": true }, "@google-cloud/storage": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.14.0.tgz", - "integrity": "sha512-tc8IrD1ZfKOm0WoC2r3+YG8K7NdaxsubedM3KYOf0m2QqqD4j9gYuEqIegs+jGoV2fr1XMibb9g/4DLp5Sv5kg==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.14.1.tgz", + "integrity": "sha512-rDwL5QKFs4p20lXep/ELYscCpCiaiPK9H9QTnPTqRQgswsDO0p+SEupq0Uw00+r4SfbHnjtvYTzBFlTu7Gn34g==", "optional": true, "requires": { "@google-cloud/common": "^3.7.0", @@ -389,9 +389,9 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "@types/node": { - "version": "16.7.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", - "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==" + "version": "16.7.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.13.tgz", + "integrity": "sha512-pLUPDn+YG3FYEt/pHI74HmnJOWzeR+tOIQzUx93pi9M7D8OE7PSLr97HboXwk5F+JS+TLtWuzCOW97AHjmOXXA==" }, "@types/qs": { "version": "6.9.7", @@ -1196,9 +1196,9 @@ } }, "firebase-admin": { - "version": "9.11.1", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-9.11.1.tgz", - "integrity": "sha512-Y9fjelljy6MKqwsSbM/UN1k8gBQh5zfm5fCTe0Z6Gch2T3nDUIPsTcf+jfe4o40/MPYuybili9XJjTMmM2e5MQ==", + "version": "9.11.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-9.11.0.tgz", + "integrity": "sha512-68fXdwcKF99LkWBE33M5hnLwjvGpbCRznIOtZVsiBqZdM9iwxlTfNEpAckh++o3GdJcSLRUWmIN+MKqPUsxoCA==", "requires": { "@firebase/database": "^0.10.0", "@firebase/database-types": "^0.7.2", @@ -1212,9 +1212,9 @@ } }, "firebase-functions": { - "version": "3.15.4", - "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-3.15.4.tgz", - "integrity": "sha512-6Zq+QIqdslZLsSwWg25Hv39cgFBZr0oUAbCjfe/MXqSHMy8ZK/1Vdy/WKx5IRC6hE7+JrmfVylIyEonTyNcheA==", + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-3.15.3.tgz", + "integrity": "sha512-1FupNg/bSrCH5All5BQ3ld7STKHWl3RAqlM2Dwf96Mf2RtOEeGt18Slh23A78igZTj7b++1Lg6fE2aGgzJT8LQ==", "requires": { "@types/cors": "^2.8.5", "@types/express": "4.17.3", @@ -1283,22 +1283,22 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" }, "gaxios": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.0.tgz", - "integrity": "sha512-pHplNbslpwCLMyII/lHPWFQbJWOX0B3R1hwBEOvzYi1GmdKZruuEHK4N9V6f7tf1EaPYyF80mui1+344p6SmLg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.1.tgz", + "integrity": "sha512-9qXV7yrMCGzTrphl9/YGMVH41oSg0rhn1j3wJWed4Oqk45/hXDD2wBT5J1NjQcqTCcv4g3nFnyQ7reSRHNgBgw==", "optional": true, "requires": { "abort-controller": "^3.0.0", "extend": "^3.0.2", "https-proxy-agent": "^5.0.0", "is-stream": "^2.0.0", - "node-fetch": "^2.3.0" + "node-fetch": "^2.6.1" } }, "gcp-metadata": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.0.tgz", - "integrity": "sha512-L9XQUpvKJCM76YRSmcxrR4mFPzPGsgZUH+GgHMxAET8qc6+BhRJq63RLhWakgEO2KKVgeSDVfyiNjkGSADwNTA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz", + "integrity": "sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==", "optional": true, "requires": { "gaxios": "^4.0.0", @@ -1306,9 +1306,9 @@ } }, "gcs-resumable-upload": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.3.0.tgz", - "integrity": "sha512-MQKWi+9hOSTyg5/SI1NBW4gAjL1wlkoevHefvr1PCBBXH4uKYLsug5qRrcotWKolDPLfWS51cWaHRN0CTtQNZw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.3.1.tgz", + "integrity": "sha512-WyC0i4VkslIdrdmeM5PNuGzANALLXTG5RoHb08OE30gYT+FEvCDPiA8KOjV2s1wOu9ngEW4+IuzBjtP/ni7UdQ==", "optional": true, "requires": { "abort-controller": "^3.0.0", @@ -1365,9 +1365,9 @@ } }, "google-auth-library": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.8.0.tgz", - "integrity": "sha512-I2DPwbBbQicoLqPbUOcq7oxtWewbLO6Za2jZ5QM9Lz1hXxPJVXYXBUrb8lP9626SRTQQWIzNSWJuEDtu0pI4ag==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.9.1.tgz", + "integrity": "sha512-cWGykH2WBR+UuYPGRnGVZ6Cjq2ftQiEIFjQWNIRIauZH7hUWoYTr/lkKUqLTYt5dex77nlWWVQ8aPV80mhfp5w==", "optional": true, "requires": { "arrify": "^2.0.0", @@ -1382,9 +1382,9 @@ } }, "google-gax": { - "version": "2.24.3", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.24.3.tgz", - "integrity": "sha512-0Rb2i2GWCEEYi1/EjQt1QsFW2Jwzm1ob21kYc7UpOmkfsmTXuyhzK7Aeyxxpo7QT/Qh+50vqER1b2ruq6WGDXg==", + "version": "2.25.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.25.1.tgz", + "integrity": "sha512-wvirrn34kKe42NRF9uy68ukM/2CWT2ce3abS2SOJaMe7Ecdya1Zcd54WAX9WAWnD0WoAfieeibD4o1Ijue8iJw==", "optional": true, "requires": { "@grpc/grpc-js": "~1.3.0", @@ -1592,6 +1592,11 @@ "@panva/asn1.js": "^1.0.0" } }, + "js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1899,9 +1904,9 @@ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-aLoxToI6RfZ+0NOjmWAgn9+LEd30YCkJKFSyWacNZdEKTit/ZMcKjGkTRo8uWEsnIb/hfKecNPEbln02PdWbcA==", "optional": true }, "node-forge": { diff --git a/functions/package.json b/functions/package.json index 8307846..872fdf5 100644 --- a/functions/package.json +++ b/functions/package.json @@ -14,8 +14,9 @@ }, "main": "index.js", "dependencies": { - "firebase-admin": "^9.11.1", - "firebase-functions": "^3.15.4" + "firebase-admin": "^9.11.0", + "firebase-functions": "^3.15.3", + "js-levenshtein": "^1.1.6" }, "devDependencies": { "eslint": "^7.32.0", diff --git a/package-lock.json b/package-lock.json index 204132c..43178e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1951,9 +1951,9 @@ } }, "@google-cloud/precise-date": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-2.0.3.tgz", - "integrity": "sha512-+SDJ3ZvGkF7hzo6BGa8ZqeK3F6Z4+S+KviC9oOK+XCs3tfMyJCh/4j93XIWINgMMDIh9BgEvlw4306VxlXIlYA==" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-2.0.4.tgz", + "integrity": "sha512-nOB+mZdevI/1Si0QAfxWfzzIqFdc7wrO+DYePFvgbOoMtvX+XfFTINNt7e9Zg66AbDbWCPRnikU+6f5LTm9Wyg==" }, "@google-cloud/projectify": { "version": "2.1.0", @@ -1966,9 +1966,9 @@ "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==" }, "@google-cloud/pubsub": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@google-cloud/pubsub/-/pubsub-2.17.0.tgz", - "integrity": "sha512-9Xya69A5VAYVEGf651jy071RuBIjv+jpyozSc3j8V21LIiKRr9x+KyplHcLTYWdj+uXbP9cry8Ck8JEFc7GiqQ==", + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/@google-cloud/pubsub/-/pubsub-2.18.1.tgz", + "integrity": "sha512-L2ejjRPszBybsRXJMLHN19UimpchNLNrtE7hJtoZxcy4fhITQGIDk1Ba4LceJYgSMJGA/YatZMYNavgyYpxhvA==", "requires": { "@google-cloud/paginator": "^3.0.0", "@google-cloud/precise-date": "^2.0.0", @@ -4159,46 +4159,11 @@ "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" }, "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", "requires": { - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } + "string-width": "^4.1.0" } }, "ansi-colors": { @@ -5188,9 +5153,9 @@ } }, "big-integer": { - "version": "1.6.48", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", - "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" + "version": "1.6.49", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.49.tgz", + "integrity": "sha512-KJ7VhqH+f/BOt9a3yMwJNmcZjG53ijWMTjSAGMveQWyLwqIiwkjNP5PFgDob3Snnx86SjDj6I89fIbv0dkQeNw==" }, "big.js": { "version": "5.2.2", @@ -7038,13 +7003,14 @@ } }, "degenerator": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-2.2.0.tgz", - "integrity": "sha512-aiQcQowF01RxFI4ZLFMpzyotbQonhNpBao6dkI8JPk5a+hmSjR5ErHp2CQySmQe8os3VBqLCIh87nDBgZXvsmg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.1.tgz", + "integrity": "sha512-LFsIFEeLPlKvAKXu7j3ssIG6RT0TbI7/GhsqrI0DnHASEQjXQ0LUSYcjJteGgRGmZbl1TnMSxpNQIAiJ7Du5TQ==", "requires": { "ast-types": "^0.13.2", "escodegen": "^1.8.1", - "esprima": "^4.0.0" + "esprima": "^4.0.0", + "vm2": "^3.9.3" } }, "del": { @@ -8863,11 +8829,6 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, - "fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" - }, "fast-text-encoding": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz", @@ -9169,9 +9130,9 @@ } }, "firebase-tools": { - "version": "9.16.5", - "resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-9.16.5.tgz", - "integrity": "sha512-dp/cvt+39wv5CO+MzX36snmRnvn5j7Nn73QfKiIvHXAT5Ek/fRJn2pWnaxP+bhd19SuEY1Buf8PcdlMl42hzlw==", + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-9.19.0.tgz", + "integrity": "sha512-jo9IhtddYBXEwO5m99oPZ53H4KInfhyYWD5+j6LD1bCsT9M95u+hNx+oXnRBVNTJjbwvlv/stF+zDy1WrpU1FA==", "requires": { "@google-cloud/pubsub": "^2.7.0", "@types/archiver": "^5.1.0", @@ -9214,7 +9175,7 @@ "ora": "^3.4.0", "portfinder": "^1.0.23", "progress": "^2.0.3", - "proxy-agent": "^4.0.0", + "proxy-agent": "^5.0.0", "request": "^2.87.0", "rimraf": "^3.0.0", "semver": "^5.7.1", @@ -13709,14 +13670,14 @@ } }, "logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.0.tgz", + "integrity": "sha512-graeoWUH2knKbGthMtuG1EfaSPMZFZBIrhuJHhkS5ZseFBrc7DupCzihOQAzsK/qIKPQaPJ/lFQFctILUY5ARQ==", "requires": { "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", "fecha": "^4.2.0", "ms": "^2.1.1", + "safe-stable-stringify": "^1.1.0", "triple-beam": "^1.3.0" }, "dependencies": { @@ -15337,9 +15298,9 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "pac-proxy-agent": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-4.1.0.tgz", - "integrity": "sha512-ejNgYm2HTXSIYX9eFlkvqFp8hyJ374uDf0Zq5YUAifiSh1D6fo+iBivQZirGvVv8dCYUsLhmLBRhlAYvBKI5+Q==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz", + "integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==", "requires": { "@tootallnate/once": "1", "agent-base": "6", @@ -15347,17 +15308,17 @@ "get-uri": "3", "http-proxy-agent": "^4.0.1", "https-proxy-agent": "5", - "pac-resolver": "^4.1.0", + "pac-resolver": "^5.0.0", "raw-body": "^2.2.0", "socks-proxy-agent": "5" } }, "pac-resolver": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-4.2.0.tgz", - "integrity": "sha512-rPACZdUyuxT5Io/gFKUeeZFfE5T7ve7cAkE5TUZRRfuKP0u5Hocwe48X7ZEm6mYB+bTB0Qf+xlVlA/RM/i6RCQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.0.tgz", + "integrity": "sha512-H+/A6KitiHNNW+bxBKREk2MCGSxljfqRX76NjummWEYIat7ldVXRU3dhRIE3iXZ0nvGBk6smv3nntxKkzRL8NA==", "requires": { - "degenerator": "^2.2.0", + "degenerator": "^3.0.1", "ip": "^1.1.5", "netmask": "^2.0.1" } @@ -16884,16 +16845,16 @@ } }, "proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-4.0.1.tgz", - "integrity": "sha512-ODnQnW2jc/FUVwHHuaZEfN5otg/fMbvMxz9nMSUQfJ9JU7q2SZvSULSsjLloVgJOiv9yhc8GlNMKc4GkFmcVEA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz", + "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==", "requires": { "agent-base": "^6.0.0", "debug": "4", "http-proxy-agent": "^4.0.0", "https-proxy-agent": "^5.0.0", "lru-cache": "^5.1.1", - "pac-proxy-agent": "^4.1.0", + "pac-proxy-agent": "^5.0.0", "proxy-from-env": "^1.0.0", "socks-proxy-agent": "^5.0.0" }, @@ -18354,6 +18315,11 @@ "ret": "~0.1.10" } }, + "safe-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", + "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -20679,15 +20645,15 @@ } }, "boxen": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz", - "integrity": "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", "requires": { "ansi-align": "^3.0.0", "camelcase": "^6.2.0", "chalk": "^4.1.0", "cli-boxes": "^2.2.1", - "string-width": "^4.2.0", + "string-width": "^4.2.2", "type-fest": "^0.20.2", "widest-line": "^3.1.0", "wrap-ansi": "^7.0.0" @@ -20971,6 +20937,11 @@ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" }, + "vm2": { + "version": "3.9.3", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.3.tgz", + "integrity": "sha512-smLS+18RjXYMl9joyJxMNI9l4w7biW8ilSDaVRvFBDwOH8P0BK1ognFQTpg0wyQ6wIKLTblHJvROW692L/E53Q==" + }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/package.json b/package.json index ded74bf..5385915 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "firebase": "^8.10.0", "firebase-admin": "^9.11.0", "firebase-functions": "^3.14.1", - "firebase-tools": "^9.16.5", + "firebase-tools": "^9.19.0", "firebaseui": "^5.0.0", "normalize-url": "^6.1.0", "rc-slider": "^9.7.2", diff --git a/src/Progress.js b/src/Progress.js index 154072f..bcb50a0 100644 --- a/src/Progress.js +++ b/src/Progress.js @@ -287,6 +287,15 @@ export default withRouter(class Progress extends React.Component { answer: this.state.answerInput, progressId: this.props.match.params.progressId, }).then(async (result) => { + if (result.data.typo) { + this.setState({ + typo: true, + loading: false, + canProceed: true, + }); + return true; + } + const data = result.data; let newState = { currentAnswerStatus: data.correct, @@ -302,6 +311,7 @@ export default withRouter(class Progress extends React.Component { currentVocabId: data.currentVocabId, loading: false, canProceed: true, + typo: false, }; if (data.correct) { @@ -425,6 +435,7 @@ export default withRouter(class Progress extends React.Component { answerInput: "", loading: false, canProceed: true, + typo: false, }); } } @@ -499,6 +510,7 @@ export default withRouter(class Progress extends React.Component { loading={this.state.loading} > +

Are you sure?

{ this.state.currentCorrect && this.state.currentCorrect.length > 0 @@ -619,7 +631,7 @@ export default withRouter(class Progress extends React.Component { (this.answerInput = inputEl)} readOnly @@ -632,7 +644,12 @@ export default withRouter(class Progress extends React.Component { loading={this.state.loading} > -
+

+ { + this.state.currentAnswerStatus ? "Correct!" : "Incorrect" + } +

+
{ this.state.currentCorrect ? diff --git a/src/css/App.css b/src/css/App.css index d9cb474..27a7470 100644 --- a/src/css/App.css +++ b/src/css/App.css @@ -169,7 +169,7 @@ button:focus-visible, a:focus-visible { } input { - border-top: none; + border-top: none; border-right: none; border-left: none; border-bottom: 2px solid var(--overlay-color); @@ -177,6 +177,7 @@ input { color: var(--text-color); font: inherit; min-width: 100px; + width: 0; } input:focus { @@ -390,6 +391,10 @@ label .MuiIconButton-label > input { margin-left: 12px; } +.hide { + visibility: hidden; +} + @media screen and (max-width: 420px) { .progress-history-container > div > *:nth-child(2), .progress-history-container--complete > div > *:nth-last-child(3), .progress-history-container--incomplete > div > *:nth-last-child(4) { display: none; diff --git a/src/css/Form.css b/src/css/Form.css index 44c79cd..2260b95 100644 --- a/src/css/Form.css +++ b/src/css/Form.css @@ -58,5 +58,4 @@ input[type=text].set-title-input { flex: 1; - width: 0; } diff --git a/src/css/Progress.css b/src/css/Progress.css index 4eb228d..041ad49 100644 --- a/src/css/Progress.css +++ b/src/css/Progress.css @@ -16,11 +16,11 @@ caret-color: transparent; } -input.answer-input--correct { +.answer--correct { color: #3ac535; } -input.answer-input--incorrect { +.answer--incorrect { color: #ff5252; } diff --git a/test/functions.test.js b/test/functions.test.js index 9048575..b6f17fa 100644 --- a/test/functions.test.js +++ b/test/functions.test.js @@ -104,6 +104,7 @@ describe("Parandum Cloud Functions", () => { assert.strictEqual(snapAfter.switch_language, false); assert.strictEqual(snapAfter.uid, userOne); assert.strictEqual(snapAfter.mode, "questions"); + assert.strictEqual(snapAfter.typo, false); assert.deepStrictEqual(termOneSnapAfter, { item: termOne, @@ -188,6 +189,7 @@ describe("Parandum Cloud Functions", () => { assert.strictEqual(snapAfter.switch_language, false); assert.strictEqual(snapAfter.uid, userOne); assert.strictEqual(snapAfter.mode, "questions"); + assert.strictEqual(snapAfter.typo, false); }); it("createProgress can create new lives mode progress file from existing set", async () => { @@ -243,6 +245,7 @@ describe("Parandum Cloud Functions", () => { assert.strictEqual(snapAfter.uid, userOne); assert.strictEqual(snapAfter.mode, "lives"); assert.strictEqual(snapAfter.lives, 2); + assert.strictEqual(snapAfter.typo, false); }); it("createProgress can create new progress file from public set they aren't the owner of", async () => { @@ -333,6 +336,7 @@ describe("Parandum Cloud Functions", () => { switch_language: false, uid: userOne, mode: "questions", + typo: false, }; const termDataOne = { "item": termOne, @@ -391,6 +395,7 @@ describe("Parandum Cloud Functions", () => { totalQuestions: 3, totalCorrect: 0, totalIncorrect: 1, + typo: false, }), hamjest.is({ mode: "questions", @@ -407,6 +412,7 @@ describe("Parandum Cloud Functions", () => { totalQuestions: 3, totalCorrect: 0, totalIncorrect: 1, + typo: false, }) )); @@ -429,6 +435,7 @@ describe("Parandum Cloud Functions", () => { switch_language: false, uid: userOne, mode: "questions", + typo: false, }), hamjest.is({ correct: [], @@ -446,6 +453,7 @@ describe("Parandum Cloud Functions", () => { switch_language: false, uid: userOne, mode: "questions", + typo: false, }) )); @@ -476,6 +484,7 @@ describe("Parandum Cloud Functions", () => { assert.strictEqual(snapAfterCorrectData.switch_language, false); assert.strictEqual(snapAfterCorrectData.uid, userOne); assert.strictEqual(snapAfterCorrectData.mode, "questions"); + assert.strictEqual(snapAfterCorrectData.typo, false); }); it("processAnswer returns correct data", async () => { @@ -495,6 +504,7 @@ describe("Parandum Cloud Functions", () => { switch_language: false, uid: userOne, mode: "questions", + typo: false, }; const termDataOne = { "item": termOne, @@ -541,6 +551,7 @@ describe("Parandum Cloud Functions", () => { totalQuestions: 2, totalCorrect: 0, totalIncorrect: 1, + typo: false, }); assert.strictEqual(returnAfterCorrect.mode, "questions"); @@ -576,6 +587,7 @@ describe("Parandum Cloud Functions", () => { switch_language: false, uid: userOne, mode: "questions", + typo: false, }; const termDataOne = { "item": termOne, @@ -637,6 +649,7 @@ describe("Parandum Cloud Functions", () => { totalQuestions: 2, totalCorrect: 0, totalIncorrect: 0, + typo: false, }); const snapAfterTermOneAnswerOneData = await progressDocId.get().then((doc) => doc.data()); @@ -656,6 +669,7 @@ describe("Parandum Cloud Functions", () => { switch_language: false, uid: userOne, mode: "questions", + typo: false, }); const returnAfterIncorrect = await processAnswer(secondTermAnswerTwoRequestData); @@ -679,6 +693,7 @@ describe("Parandum Cloud Functions", () => { switch_language: false, uid: userOne, mode: "questions", + typo: false, }), hamjest.is({ correct: [], @@ -696,6 +711,7 @@ describe("Parandum Cloud Functions", () => { switch_language: false, uid: userOne, mode: "questions", + typo: false, }) )); @@ -730,6 +746,7 @@ describe("Parandum Cloud Functions", () => { assert.strictEqual(snapAfterCorrectData.switch_language, false); assert.strictEqual(snapAfterCorrectData.uid, userOne); assert.strictEqual(snapAfterCorrectData.mode, "questions"); + assert.strictEqual(snapAfterCorrectData.typo, false); }).timeout(5000); it("processAnswer ignores punctuation", async () => { @@ -750,6 +767,7 @@ describe("Parandum Cloud Functions", () => { switch_language: false, uid: userOne, mode: "questions", + typo: false, }; const termDataOne = { "item": termOne,