From 80bc0f01c0bcaea846f069b0808468b6082df64b Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Sun, 7 Aug 2022 04:17:53 -0400 Subject: [PATCH 01/34] Moved from Trello to GitHub porjects --- boards/boards.json | 44 ----- package-lock.json | 203 ++++++++++++++++++++++++ package.json | 2 +- public/assets/js/progress-charts.js | 8 +- src/cache.js | 171 ++++++++++++-------- src/middleware/render-data.js | 2 +- src/routes/home.js | 42 +++-- src/routes/progress.js | 11 +- views/home.handlebars | 2 +- views/partials/progress-list.handlebars | 22 ++- views/progress.handlebars | 2 +- 11 files changed, 346 insertions(+), 163 deletions(-) delete mode 100644 boards/boards.json diff --git a/boards/boards.json b/boards/boards.json deleted file mode 100644 index 56fe690..0000000 --- a/boards/boards.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "boards": { - "gb7qURRJ": { - "github_links": [ - { - "display": "Account server repo", - "url": "https://github.com/PretendoNetwork/account" - } - ] - }, - "G54H0wtc": { - "github_links": [ - { - "display": "Juxtaposition repo", - "url": "https://github.com/PretendoNetwork/juxt-web" - } - ] - }, - "lmAbWUyn": { - "github_links": [ - { - "display": "BOSS repo", - "url": "https://github.com/PretendoNetwork/BOSS" - } - ] - }, - "LQZftqhE": { - "github_links": [ - { - "display": "Super Mario Maker (authentication) repo", - "url": "https://github.com/PretendoNetwork/super-mario-maker-authentication" - } - ] - }, - "9frc4r0Q": { - "github_links": [ - { - "display": "Website repo", - "url": "https://github.com/PretendoNetwork/website" - } - ] - } - } -} diff --git a/package-lock.json b/package-lock.json index 828d476..2c751d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "express-locale": "^2.0.0", "fs-extra": "^9.1.0", "got": "^11.8.5", + "graphql-request": "^4.3.0", "gray-matter": "^4.0.3", "kaitai-struct": "^0.9.0", "marked": "^4.0.10", @@ -516,6 +517,11 @@ "node": ">=8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -998,6 +1004,17 @@ "node": ">=0.10.0" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -1126,6 +1143,14 @@ "sha.js": "^2.4.8" } }, + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dependencies": { + "node-fetch": "2.6.7" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1229,6 +1254,14 @@ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/denque": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", @@ -1793,6 +1826,17 @@ "node": ">=0.10.0" } }, + "node_modules/extract-files": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz", + "integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==", + "engines": { + "node": "^10.17.0 || ^12.0.0 || >= 13.7.0" + }, + "funding": { + "url": "https://github.com/sponsors/jaydenseric" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1869,6 +1913,19 @@ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -2037,6 +2094,28 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" }, + "node_modules/graphql": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.5.0.tgz", + "integrity": "sha512-qbHgh8Ix+j/qY+a/ZcJnFQ+j8ezakqPiHwPiZhV/3PgGlgf96QMBB5/f2rkiC9sgLoy/xvT6TSiaf2nTHJh5iA==", + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-request": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-4.3.0.tgz", + "integrity": "sha512-2v6hQViJvSsifK606AliqiNiijb1uwWp6Re7o0RTyH+uRTv/u7Uqm2g4Fjq/LgZIzARB38RZEvVBFOQOVdlBow==", + "dependencies": { + "cross-fetch": "^3.1.5", + "extract-files": "^9.0.0", + "form-data": "^3.0.0" + }, + "peerDependencies": { + "graphql": "14 - 16" + } + }, "node_modules/gray-matter": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", @@ -3089,6 +3168,44 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/nodemailer": { "version": "6.7.5", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.5.tgz", @@ -4895,6 +5012,11 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -5283,6 +5405,14 @@ } } }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -5398,6 +5528,14 @@ "sha.js": "^2.4.8" } }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "requires": { + "node-fetch": "2.6.7" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -5479,6 +5617,11 @@ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "denque": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", @@ -5932,6 +6075,11 @@ "is-extendable": "^0.1.0" } }, + "extract-files": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz", + "integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==" + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5999,6 +6147,16 @@ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -6122,6 +6280,22 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" }, + "graphql": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.5.0.tgz", + "integrity": "sha512-qbHgh8Ix+j/qY+a/ZcJnFQ+j8ezakqPiHwPiZhV/3PgGlgf96QMBB5/f2rkiC9sgLoy/xvT6TSiaf2nTHJh5iA==", + "peer": true + }, + "graphql-request": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-4.3.0.tgz", + "integrity": "sha512-2v6hQViJvSsifK606AliqiNiijb1uwWp6Re7o0RTyH+uRTv/u7Uqm2g4Fjq/LgZIzARB38RZEvVBFOQOVdlBow==", + "requires": { + "cross-fetch": "^3.1.5", + "extract-files": "^9.0.0", + "form-data": "^3.0.0" + } + }, "gray-matter": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", @@ -6894,6 +7068,35 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, "nodemailer": { "version": "6.7.5", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.5.tgz", diff --git a/package.json b/package.json index 74f4a91..e89df07 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "express-locale": "^2.0.0", "fs-extra": "^9.1.0", "got": "^11.8.5", + "graphql-request": "^4.3.0", "gray-matter": "^4.0.3", "kaitai-struct": "^0.9.0", "marked": "^4.0.10", @@ -36,7 +37,6 @@ "morgan": "^1.10.0", "nodemailer": "^6.7.5", "stripe": "^9.9.0", - "trello": "^0.11.0", "uuid": "^8.3.2" }, "devDependencies": { diff --git a/public/assets/js/progress-charts.js b/public/assets/js/progress-charts.js index 478263e..6313d34 100644 --- a/public/assets/js/progress-charts.js +++ b/public/assets/js/progress-charts.js @@ -8,12 +8,16 @@ document.querySelectorAll('.feature-list-wrapper').forEach(progressListElement = const allStartedFeatureNodes = progressListElement.querySelectorAll('.feature .ongoing'); // Use percentage override data attribute if present, else calculate - const progressPercentage = Math.round(percentageOverride) || Math.round(Math.min((allDoneFeatureNodes.length + allStartedFeatureNodes.length * 0.5) / allFeatureNodes.length * 100, 100)); + const progressPercentage = Math.round(percentageOverride) || Math.round(Math.min((allDoneFeatureNodes.length + allStartedFeatureNodes.length * 0.5) / allFeatureNodes.length * 100, 100)) || 0; const remainingPercentage = 100 - progressPercentage; // Set inner paragraph progressListElement.querySelectorAll('.percentage-label').forEach(p => { - p.innerText = progressPercentage.toString().padStart(2, '0') + '%'; + if (progressPercentage === 0) { + p.innerText = progressPercentage.toString() + '%'; + } else { + p.innerText = progressPercentage.toString().padStart(2, '0') + '%'; + } }); // Create chart diff --git a/src/cache.js b/src/cache.js index ca86a87..d22f61b 100644 --- a/src/cache.js +++ b/src/cache.js @@ -1,98 +1,131 @@ -const Trello = require('trello'); +const { GraphQLClient, gql } = require('graphql-request'); const Stripe = require('stripe'); -const got = require('got'); const config = require('../config.json'); -const trello = new Trello(config.trello.api_key, config.trello.api_token); +const graphql = new GraphQLClient('https://api.github.com/graphql', { + headers: { + Authorization: `bearer ${config.github.graphql_token}`, + } +}); const stripe = new Stripe(config.stripe.secret_key); -const VALID_LIST_NAMES = ['Not Started', 'Started', 'Completed']; -let trelloCache; +const getProjectCards = gql` + fragment ItemContent on Node { + __typename + ... on DraftIssue { + title + } + ... on Issue { + title + } + } + + fragment Itemfields on Node { + __typename + ... on ProjectV2ItemFieldSingleSelectValue { + name, + field { + ... on ProjectV2SingleSelectField { + name + } + } + } + } + + query getProjectCards($orgName: String!) { + organization(login: $orgName) { + projectsV2(first: 100) { + nodes { + repositories(first: 1) { + nodes { + url + } + } + title + items(first: 100) { + nodes { + id + content { + ...ItemContent + } + fieldValues(first: 20) { + nodes { + ...Itemfields + } + } + } + } + } + } + } + } +`; + +let githubProjectsCache; let stripeDonationCache; -async function getTrelloCache() { - const available = await trelloAPIAvailable(); - if (!available) { - return trelloCache || { - update_time: 0, - sections: [{ - title: 'Upstream API error', - id: '', - percentage_complete: '', - progress: { - not_started: [ 'Trello API unavailable' ], - started: [], - completed: [] - } - }], - }; +async function getGithubProjectsCache() { + if (!githubProjectsCache) { + githubProjectsCache = await updateGithubProjectsCache(); } - if (!trelloCache) { - trelloCache = await updateTrelloCache(); + if (githubProjectsCache.update_time < Date.now() - (1000 * 60 * 60)) { + githubProjectsCache = await updateGithubProjectsCache(); } - if (trelloCache.update_time < Date.now() - (1000 * 60 * 60)) { - trelloCache = await updateTrelloCache(); - } - - return trelloCache; + return githubProjectsCache; } -async function updateTrelloCache() { - const progressCache = { +function getProgressField(fields) { + const found = fields.nodes.find(v => v.field?.name === 'Status'); + return found?.name ?? undefined; +} + +async function updateGithubProjectsCache() { + const projectsCacheData = { update_time: Date.now(), sections: [] }; - const boards = await trello.getOrgBoards(config.trello.board_name); + const data = await graphql.request(getProjectCards, { + orgName: 'PretendoNetwork', + }); - for (const board of boards) { - const meta = { - title: '', - id: '', - percentage_complete: 0, - progress: { - not_started: [], - started: [], - completed: [] - } - }; + const projects = data.organization.projectsV2.nodes; - meta.title = board.name; - meta.id = board.shortLink; - - const lists = await trello.getListsOnBoard(board.id); - - const listNames = lists.map(list => list.name); - const hasAllValidLists = listNames.every(name => VALID_LIST_NAMES.includes(name)); - - if (!hasAllValidLists) { + for (const project of projects) { + if (!project.repositories.nodes[0]) { continue; } - const cards = await trello.getCardsOnBoard(board.id); - - for (const card of cards) { - const cardList = lists.find(({ id }) => id === card.idList); - const listName = cardList.name.toLowerCase().replace(' ', '_'); - - if (meta.progress[listName]) { - meta.progress[listName].push(card.name); + const extractedData = { + title: project.title, + url: project.repositories.nodes[0]?.url, + cards: { + done: [], + in_progress: [], + todo: [] } + }; + + for (const { content, fieldValues } of project.items.nodes) { + const progress = getProgressField(fieldValues); + + if (!['DraftIssue'].includes(content.__typename)) { + continue; // not a supported card, skip + } + + if (!progress) { + continue; // entry does not have a status, skip + } + + extractedData.cards[progress.toLowerCase().replace(' ', '_')]?.push(content.title); } - if (meta.progress.not_started.length !== 0 || meta.progress.started.length !== 0 || meta.progress.completed.length !== 0) { - progressCache.sections.push(meta); - } + projectsCacheData.sections.push(extractedData); } - return progressCache; -} - -async function trelloAPIAvailable() { - const { status } = await got('https://trello.status.atlassian.com/api/v2/status.json').json(); - return status.indicator !== 'major' && status.indicator !== 'critical'; + return projectsCacheData; } async function getStripeDonationCache() { @@ -147,6 +180,6 @@ async function updateStripeDonationCache() { } module.exports = { - getTrelloCache, + getGithubProjectsCache, getStripeDonationCache }; diff --git a/src/middleware/render-data.js b/src/middleware/render-data.js index ea1fe05..cb78e76 100644 --- a/src/middleware/render-data.js +++ b/src/middleware/render-data.js @@ -40,4 +40,4 @@ async function renderDataMiddleware(request, response, next) { } } -module.exports = renderDataMiddleware; +module.exports = renderDataMiddleware; \ No newline at end of file diff --git a/src/routes/home.js b/src/routes/home.js index 1a74742..18905b1 100644 --- a/src/routes/home.js +++ b/src/routes/home.js @@ -1,16 +1,12 @@ const { Router } = require('express'); -const { boards } = require('../../boards/boards.json'); const router = new Router(); -const { getTrelloCache } = require('../cache'); +const { getGithubProjectsCache } = require('../cache'); router.get('/', async (request, response) => { + const renderData = {}; - const renderData = { - boards - }; - - const cache = await getTrelloCache(); + const githubProjectsCache = await getGithubProjectsCache(); // Builds the arrays of people for the special thanks section @@ -43,37 +39,37 @@ router.get('/', async (request, response) => { percentageSum: 0 }, percent: 0, - progress: { - not_started: [], - started: [], - completed: [] + cards: { + todo: [], + in_progress: [], + done: [] } }; // Calculates individual completion percentages and progress states - cache.sections.forEach(section => { - const { not_started, started, completed } = section.progress; + githubProjectsCache.sections.forEach(section => { + const { todo, in_progress, done } = section.cards; // Calculates the completion percentage of the project, and sums it to the total - const sectionCompletionPercentage = (completed.length + started.length * 0.5) / (completed.length + started.length + not_started.length); + const sectionCompletionPercentage = ((done.length + in_progress.length * 0.5) / (done.length + in_progress.length + todo.length)) || 0; totalProgress._calc.percentageSum += sectionCompletionPercentage; const sectionTitle = `${section.title} [${Math.round(sectionCompletionPercentage * 100)}%]`; - if (completed !== [] && started + not_started === []) { - // if all the section tasks have been completed, push to completed - totalProgress.progress.completed.push(sectionTitle); - } else if (not_started !== [] && started + completed === []) { - // if none the section tasks have been completed or started, push to not started - totalProgress.progress.not_started.push(sectionTitle); + if (done !== [] && in_progress + todo === []) { + // if all the section tasks have been done, push to done + totalProgress.cards.done.push(sectionTitle); + } else if (todo !== [] && in_progress + done === []) { + // if none the section tasks have been done or in_progress, push to todo + totalProgress.cards.todo.push(sectionTitle); } else { - // for all other combos, push to started - totalProgress.progress.started.push(sectionTitle); + // for all other combos, push to in_progress + totalProgress.cards.in_progress.push(sectionTitle); } }); // Calculates global completion percentage - totalProgress.percent = Math.round(totalProgress._calc.percentageSum / cache.sections.length * 100); + totalProgress.percent = Math.round(totalProgress._calc.percentageSum / githubProjectsCache.sections.length * 100); renderData.featuredFeatureList = totalProgress; diff --git a/src/routes/progress.js b/src/routes/progress.js index fb133f4..1de949e 100644 --- a/src/routes/progress.js +++ b/src/routes/progress.js @@ -1,19 +1,14 @@ const { Router } = require('express'); -const { boards } = require('../../boards/boards.json'); const router = new Router(); -const { getTrelloCache, getStripeDonationCache } = require('../cache'); +const { getGithubProjectsCache, getStripeDonationCache } = require('../cache'); router.get('/', async (request, response) => { const renderData = { - boards + progressLists: await getGithubProjectsCache(), + donationCache: await getStripeDonationCache() }; - const trelloCache = await getTrelloCache(); - renderData.progressLists = trelloCache; - const stripeDonationCache = await getStripeDonationCache(); - renderData.donationCache = stripeDonationCache; - response.render('progress', renderData); }); diff --git a/views/home.handlebars b/views/home.handlebars index b0e1446..05aae22 100644 --- a/views/home.handlebars +++ b/views/home.handlebars @@ -85,7 +85,7 @@

{{ localeHelper locale "progress" "title" }}

- {{> progress-list data=featuredFeatureList purple=true boards=boards }} + {{> progress-list data=featuredFeatureList purple=true }} diff --git a/views/partials/progress-list.handlebars b/views/partials/progress-list.handlebars index ead3fa1..175f561 100644 --- a/views/partials/progress-list.handlebars +++ b/views/partials/progress-list.handlebars @@ -10,19 +10,15 @@
{{#if data.title}}

{{ data.title }}

- {{#if data.id}}{{#if (lookup boards data.id)}}{{#with (lookup boards data.id) as | board |}} - {{#each board.github_links}} -
- - - {{this.display}} - -
- {{/each}} - {{/with}}{{/if}}{{/if}} +
+ + + GitHub repository + +
{{/if}}
- {{#each data.progress.completed}} + {{#each data.cards.done}}
{{/each}} - {{#each data.progress.started}} + {{#each data.cards.in_progress}}
@@ -43,7 +39,7 @@
{{/each}} - {{#each data.progress.not_started}} + {{#each data.cards.todo}}
{{ this }} diff --git a/views/progress.handlebars b/views/progress.handlebars index 0feb534..3dd004c 100644 --- a/views/progress.handlebars +++ b/views/progress.handlebars @@ -23,7 +23,7 @@ {{#each progressLists.sections}}
- {{> progress-list data=this boards=@root.boards }} + {{> progress-list data=this }}
{{/each}}
From c8b424e18e9a915b007f2fc53ac1cb2e95f4f8ba Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Thu, 11 Aug 2022 20:02:33 -0400 Subject: [PATCH 02/34] Fixed cookies not being set after refresh --- src/middleware/render-data.js | 2 +- src/util.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/middleware/render-data.js b/src/middleware/render-data.js index cb78e76..f534edb 100644 --- a/src/middleware/render-data.js +++ b/src/middleware/render-data.js @@ -33,7 +33,7 @@ async function renderDataMiddleware(request, response, next) { return next(); } catch (error) { response.cookie('error_message', error.message, { domain: '.pretendo.network' }); - return response.redirect('/account/login'); + return response.redirect('/account/logout'); } } else { return next(); diff --git a/src/util.js b/src/util.js index 6e01ea2..f7df0d8 100644 --- a/src/util.js +++ b/src/util.js @@ -99,6 +99,10 @@ async function refreshLogin(request, response) { response.cookie('refresh_token', tokens.refresh_token, { domain: '.pretendo.network' }); response.cookie('access_token', tokens.access_token, { domain: '.pretendo.network' }); response.cookie('token_type', tokens.token_type, { domain: '.pretendo.network' }); + + request.cookies.refresh_token = tokens.refresh_token; + request.cookies.access_token = tokens.access_token; + request.cookies.token_type = tokens.token_type; } async function getUserAccountData(request, response, fromRetry=false) { From 01a34aa2683168bdbb49e0a7fb3150767453e8f9 Mon Sep 17 00:00:00 2001 From: Ash Monty Date: Fri, 12 Aug 2022 21:22:12 +0200 Subject: [PATCH 03/34] locales: convert codes to POSIX-style --- locales/{AR_ar.json => ar_AR.json} | 0 locales/{DE_de.json => de_DE.json} | 0 locales/{US_en.json => en_US.json} | 0 locales/{ES_es.json => es_ES.json} | 0 locales/{FR_fr.json => fr_FR.json} | 0 locales/{IT_it.json => it_IT.json} | 0 locales/{JP_ja.json => ja_JP.json} | 0 locales/{KR_ko.json => ko_KR.json} | 0 locales/{NO_nb.json => nb_NO.json} | 0 locales/{NL_nl.json => nl_NL.json} | 0 locales/{PL_pl.json => pl_PL.json} | 0 locales/{BR_pt.json => pt_BR.json} | 0 locales/{RO_ro.json => ro_RO.json} | 0 locales/{RU_ru.json => ru_RU.json} | 0 locales/{TR_tr.json => tr_TR.json} | 0 locales/{CN_zh.json => zh_CN.json} | 0 src/middleware/render-data.js | 4 ++-- src/server.js | 2 +- src/util.js | 8 ++++---- 19 files changed, 7 insertions(+), 7 deletions(-) rename locales/{AR_ar.json => ar_AR.json} (100%) rename locales/{DE_de.json => de_DE.json} (100%) rename locales/{US_en.json => en_US.json} (100%) rename locales/{ES_es.json => es_ES.json} (100%) rename locales/{FR_fr.json => fr_FR.json} (100%) rename locales/{IT_it.json => it_IT.json} (100%) rename locales/{JP_ja.json => ja_JP.json} (100%) rename locales/{KR_ko.json => ko_KR.json} (100%) rename locales/{NO_nb.json => nb_NO.json} (100%) rename locales/{NL_nl.json => nl_NL.json} (100%) rename locales/{PL_pl.json => pl_PL.json} (100%) rename locales/{BR_pt.json => pt_BR.json} (100%) rename locales/{RO_ro.json => ro_RO.json} (100%) rename locales/{RU_ru.json => ru_RU.json} (100%) rename locales/{TR_tr.json => tr_TR.json} (100%) rename locales/{CN_zh.json => zh_CN.json} (100%) diff --git a/locales/AR_ar.json b/locales/ar_AR.json similarity index 100% rename from locales/AR_ar.json rename to locales/ar_AR.json diff --git a/locales/DE_de.json b/locales/de_DE.json similarity index 100% rename from locales/DE_de.json rename to locales/de_DE.json diff --git a/locales/US_en.json b/locales/en_US.json similarity index 100% rename from locales/US_en.json rename to locales/en_US.json diff --git a/locales/ES_es.json b/locales/es_ES.json similarity index 100% rename from locales/ES_es.json rename to locales/es_ES.json diff --git a/locales/FR_fr.json b/locales/fr_FR.json similarity index 100% rename from locales/FR_fr.json rename to locales/fr_FR.json diff --git a/locales/IT_it.json b/locales/it_IT.json similarity index 100% rename from locales/IT_it.json rename to locales/it_IT.json diff --git a/locales/JP_ja.json b/locales/ja_JP.json similarity index 100% rename from locales/JP_ja.json rename to locales/ja_JP.json diff --git a/locales/KR_ko.json b/locales/ko_KR.json similarity index 100% rename from locales/KR_ko.json rename to locales/ko_KR.json diff --git a/locales/NO_nb.json b/locales/nb_NO.json similarity index 100% rename from locales/NO_nb.json rename to locales/nb_NO.json diff --git a/locales/NL_nl.json b/locales/nl_NL.json similarity index 100% rename from locales/NL_nl.json rename to locales/nl_NL.json diff --git a/locales/PL_pl.json b/locales/pl_PL.json similarity index 100% rename from locales/PL_pl.json rename to locales/pl_PL.json diff --git a/locales/BR_pt.json b/locales/pt_BR.json similarity index 100% rename from locales/BR_pt.json rename to locales/pt_BR.json diff --git a/locales/RO_ro.json b/locales/ro_RO.json similarity index 100% rename from locales/RO_ro.json rename to locales/ro_RO.json diff --git a/locales/RU_ru.json b/locales/ru_RU.json similarity index 100% rename from locales/RU_ru.json rename to locales/ru_RU.json diff --git a/locales/TR_tr.json b/locales/tr_TR.json similarity index 100% rename from locales/TR_tr.json rename to locales/tr_TR.json diff --git a/locales/CN_zh.json b/locales/zh_CN.json similarity index 100% rename from locales/CN_zh.json rename to locales/zh_CN.json diff --git a/src/middleware/render-data.js b/src/middleware/render-data.js index f534edb..50f1f97 100644 --- a/src/middleware/render-data.js +++ b/src/middleware/render-data.js @@ -8,7 +8,7 @@ async function renderDataMiddleware(request, response, next) { // Get user local const reqLocale = request.locale; - const locale = util.getLocale(reqLocale.region, reqLocale.language); + const locale = util.getLocale(reqLocale.language, reqLocale.region); response.locals.locale = locale; response.locals.localeString = reqLocale.toString(); @@ -40,4 +40,4 @@ async function renderDataMiddleware(request, response, next) { } } -module.exports = renderDataMiddleware; \ No newline at end of file +module.exports = renderDataMiddleware; diff --git a/src/server.js b/src/server.js index fd2cd7a..2dce61b 100644 --- a/src/server.js +++ b/src/server.js @@ -12,7 +12,7 @@ const database = require('./database'); const util = require('./util'); const logger = require('./logger'); const config = require('../config.json'); -const defaultLocale = require('../locales/US_en.json'); +const defaultLocale = require('../locales/en_US.json'); const { http: { port } } = config; const app = express(); diff --git a/src/util.js b/src/util.js index f7df0d8..90f4c6a 100644 --- a/src/util.js +++ b/src/util.js @@ -13,16 +13,16 @@ function fullUrl(request) { return `${request.protocol}://${request.hostname}${request.originalUrl}`; } -function getLocale(region, language) { - const path = `${__dirname}/../locales/${region}_${language}.json`; +function getLocale(language, region) { + const path = `${__dirname}/../locales/${language}_${region}.json`; if (fs.pathExistsSync(path)) { return require(path); } - logger.warn(`Could not find locale ${region}_${language}! Loading US_en`); + logger.warn(`Could not find locale ${language}_${region}! Loading en_US`); - return require(`${__dirname}/../locales/US_en.json`); + return require(`${__dirname}/../locales/en_US.json`); } function apiGetRequest(path, headers) { From deb091932afe739a7cb47a3e16806d2e942a55ed Mon Sep 17 00:00:00 2001 From: Ash Monty Date: Sat, 13 Aug 2022 16:52:06 +0200 Subject: [PATCH 04/34] feat(miieditor): switch to mii-js, add missing props --- package-lock.json | 164 +++------------- package.json | 1 + public/assets/css/miieditor.css | 49 ++++- public/assets/images/copy.svg | 1 + public/assets/images/miieditor.svg | 34 +++- public/assets/images/share.svg | 1 + public/assets/images/star.svg | 1 + public/assets/js/MiiClass.js | 214 -------------------- public/assets/js/miieditor.js | 301 +++++++++++++++++++++-------- views/account/miieditor.handlebars | 197 +++++++++++-------- 10 files changed, 441 insertions(+), 522 deletions(-) create mode 100644 public/assets/images/copy.svg create mode 100644 public/assets/images/share.svg create mode 100644 public/assets/images/star.svg delete mode 100644 public/assets/js/MiiClass.js diff --git a/package-lock.json b/package-lock.json index 2c751d3..f3b11fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,11 +25,11 @@ "gray-matter": "^4.0.3", "kaitai-struct": "^0.9.0", "marked": "^4.0.10", + "mii-js": "github:PretendoNetwork/mii-js#v1.0.2", "mongoose": "^6.4.0", "morgan": "^1.10.0", "nodemailer": "^6.7.5", "stripe": "^9.9.0", - "trello": "^0.11.0", "uuid": "^8.3.2" }, "devDependencies": { @@ -576,6 +576,11 @@ "node": ">= 0.8" } }, + "node_modules/bit-buffer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/bit-buffer/-/bit-buffer-0.2.5.tgz", + "integrity": "sha512-x1yGnmXvFg6e3DiyRztElbcn1bsCTFSoM/ncAzY62uE0JdTl5xlKJd0ooqLYoPbhdsnpehSIQrdIvclcZJYwiA==" + }, "node_modules/bn.js": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", @@ -1489,11 +1494,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", - "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=" - }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2915,6 +2915,14 @@ "node": ">= 0.6" } }, + "node_modules/mii-js": { + "version": "1.0.2", + "resolved": "git+ssh://git@github.com/PretendoNetwork/mii-js.git#d3386c348b8428fc8514dbb97a7c24ffefa3017e", + "license": "ISC", + "dependencies": { + "bit-buffer": "^0.2.5" + } + }, "node_modules/miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", @@ -3661,33 +3669,6 @@ "lowercase-keys": "^2.0.0" } }, - "node_modules/restler": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/restler/-/restler-3.3.0.tgz", - "integrity": "sha512-TrnPEY3DJgFJgPd5vJmHpOj0WlAzLcX2eIcAqc/EB/YlgPFz0aMD55GkQN16d3nX0ydlq93lthZWGKyBpVnFEg==", - "dependencies": { - "iconv-lite": "0.2.11", - "qs": "1.2.0", - "xml2js": "0.4.0", - "yaml": "0.2.3" - }, - "engines": { - "node": ">= 0.10.x" - } - }, - "node_modules/restler/node_modules/iconv-lite": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", - "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/restler/node_modules/qs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-1.2.0.tgz", - "integrity": "sha512-XUf0O7rlGjbH+n7uqyT+xn362fmoPe4ehtHL6VK1nbSgQ7CqG0ZZLr1nU2EyXlRq++YphPdQ/5scjIWNMSPnhg==" - }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -3734,11 +3715,6 @@ "node": ">=6" } }, - "node_modules/sax": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", - "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=" - }, "node_modules/section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", @@ -4274,19 +4250,6 @@ "node": ">=12" } }, - "node_modules/trello": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/trello/-/trello-0.11.0.tgz", - "integrity": "sha512-WMjfSTA+ybGnd36ZoMX7Ia7wfpU9KP2lsafv9mjLwFcE1ECp8b9/rX3uW4ivNO7jVLnHRyQ1pHsirbMvK0Ix2Q==", - "dependencies": { - "es6-promise": "~3.0.2", - "object-assign": "~4.1.0", - "restler": "~3.3.0" - }, - "engines": { - "node": ">= 0.10.x" - } - }, "node_modules/tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", @@ -4578,23 +4541,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "node_modules/xml2js": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.0.tgz", - "integrity": "sha1-Ek/EEUtBKcgQgA7LKshs8lRiy5o=", - "dependencies": { - "sax": "0.5.x", - "xmlbuilder": ">=0.4.2" - } - }, - "node_modules/xmlbuilder": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", - "engines": { - "node": ">=8.0" - } - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -4608,14 +4554,6 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true - }, - "node_modules/yaml": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-0.2.3.tgz", - "integrity": "sha1-tUUOkudu82td0k42YAkeuu7z5cc=", - "engines": { - "node": "*" - } } }, "dependencies": { @@ -5045,6 +4983,11 @@ "safe-buffer": "5.1.2" } }, + "bit-buffer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/bit-buffer/-/bit-buffer-0.2.5.tgz", + "integrity": "sha512-x1yGnmXvFg6e3DiyRztElbcn1bsCTFSoM/ncAzY62uE0JdTl5xlKJd0ooqLYoPbhdsnpehSIQrdIvclcZJYwiA==" + }, "bn.js": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", @@ -5812,11 +5755,6 @@ "is-symbol": "^1.0.2" } }, - "es6-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", - "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=" - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -6869,6 +6807,13 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, + "mii-js": { + "version": "git+ssh://git@github.com/PretendoNetwork/mii-js.git#d3386c348b8428fc8514dbb97a7c24ffefa3017e", + "from": "mii-js@github:PretendoNetwork/mii-js#v1.0.2", + "requires": { + "bit-buffer": "^0.2.5" + } + }, "miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", @@ -7450,29 +7395,6 @@ "lowercase-keys": "^2.0.0" } }, - "restler": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/restler/-/restler-3.3.0.tgz", - "integrity": "sha512-TrnPEY3DJgFJgPd5vJmHpOj0WlAzLcX2eIcAqc/EB/YlgPFz0aMD55GkQN16d3nX0ydlq93lthZWGKyBpVnFEg==", - "requires": { - "iconv-lite": "0.2.11", - "qs": "1.2.0", - "xml2js": "0.4.0", - "yaml": "0.2.3" - }, - "dependencies": { - "iconv-lite": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", - "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=" - }, - "qs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-1.2.0.tgz", - "integrity": "sha512-XUf0O7rlGjbH+n7uqyT+xn362fmoPe4ehtHL6VK1nbSgQ7CqG0ZZLr1nU2EyXlRq++YphPdQ/5scjIWNMSPnhg==" - } - } - }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -7510,11 +7432,6 @@ "sparse-bitfield": "^3.0.3" } }, - "sax": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", - "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=" - }, "section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", @@ -7932,16 +7849,6 @@ "punycode": "^2.1.1" } }, - "trello": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/trello/-/trello-0.11.0.tgz", - "integrity": "sha512-WMjfSTA+ybGnd36ZoMX7Ia7wfpU9KP2lsafv9mjLwFcE1ECp8b9/rX3uW4ivNO7jVLnHRyQ1pHsirbMvK0Ix2Q==", - "requires": { - "es6-promise": "~3.0.2", - "object-assign": "~4.1.0", - "restler": "~3.3.0" - } - }, "tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", @@ -8166,20 +8073,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "xml2js": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.0.tgz", - "integrity": "sha1-Ek/EEUtBKcgQgA7LKshs8lRiy5o=", - "requires": { - "sax": "0.5.x", - "xmlbuilder": ">=0.4.2" - } - }, - "xmlbuilder": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==" - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -8190,11 +8083,6 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true - }, - "yaml": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-0.2.3.tgz", - "integrity": "sha1-tUUOkudu82td0k42YAkeuu7z5cc=" } } } diff --git a/package.json b/package.json index e89df07..b6f5838 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "gray-matter": "^4.0.3", "kaitai-struct": "^0.9.0", "marked": "^4.0.10", + "mii-js": "github:PretendoNetwork/mii-js#v1.0.2", "mongoose": "^6.4.0", "morgan": "^1.10.0", "nodemailer": "^6.7.5", diff --git a/public/assets/css/miieditor.css b/public/assets/css/miieditor.css index f7986e8..8371908 100644 --- a/public/assets/css/miieditor.css +++ b/public/assets/css/miieditor.css @@ -190,6 +190,49 @@ div.subtabs .subtabbtn.active:hover::before { transform: scale(3); } +.has-textinput { + grid-template-columns: 1fr 1fr; + grid-template-rows: auto; + grid-auto-rows: auto; + gap: 24px; +} +.has-textinput label { + display: block; + margin-bottom: 6px; + text-transform: uppercase; + font-size: 12px; +} +div.birthday { + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: end; + gap: 12px; +} +div.birthday span { + margin-bottom: 12px; +} +.has-textinput .icons { + display: flex; + flex-flow: row nowrap; + justify-content: space-between; +} +.has-textinput .icons input[type="checkbox"] { + box-sizing: border-box; + margin: 0; + height: 49px; + width: 49px; +} + +input[type="checkbox"]#allowCopying:checked { + background: no-repeat center/80% url(../images/copy.svg), var(--accent-shade-0); +} +input[type="checkbox"]#disableSharing:checked { + background: no-repeat center/80% url(../images/share.svg), var(--accent-shade-0); +} +input[type="checkbox"]#favorite:checked { + background: no-repeat center/80% url(../images/star.svg), var(--accent-shade-0); +} + form.params { grid-template-columns: repeat(2, auto); height: 618px; @@ -593,7 +636,7 @@ button * { grid-row: 4; } - fieldset:not(.has-sliders) { + fieldset:not(.has-sliders, .has-textinput) { position: relative; margin-bottom: -60px; } @@ -648,6 +691,10 @@ button * { width: 36px; height: 36px; } + + .has-textinput { + grid-template-columns: 1fr; + } } @media screen and (max-width: 360px) { diff --git a/public/assets/images/copy.svg b/public/assets/images/copy.svg new file mode 100644 index 0000000..67e47f7 --- /dev/null +++ b/public/assets/images/copy.svg @@ -0,0 +1 @@ + diff --git a/public/assets/images/miieditor.svg b/public/assets/images/miieditor.svg index 27724a6..0ce9d34 100644 --- a/public/assets/images/miieditor.svg +++ b/public/assets/images/miieditor.svg @@ -27,11 +27,11 @@ inkscape:deskcolor="#d1d1d1" showgrid="false" inkscape:lockguides="false" - inkscape:zoom="5.979798" - inkscape:cx="65.888513" - inkscape:cy="316.23142" + inkscape:zoom="5.6568543" + inkscape:cx="104.56341" + inkscape:cy="351.43207" inkscape:window-width="1920" - inkscape:window-height="1023" + inkscape:window-height="1025" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" @@ -75,7 +75,7 @@ d="m 38.391224,328.78155 c -0.402278,0.001 -0.847854,0.0668 -1.343327,0.20523 a 0.43421497,0.4342385 0 0 0 -0.041,0.0153 c -0.576068,0.22485 -0.849154,0.72713 -0.944648,1.11432 -0.095,0.3872 -0.053,0.72084 -0.053,0.72084 a 0.43421497,0.4342385 0 0 0 0.642766,0.32564 c 1.156837,-0.6469 1.853099,-0.71378 2.39647,-0.61567 0.54327,0.0981 0.981247,0.4064 1.575614,0.64112 a 0.43421497,0.4342385 0 0 0 0.537671,-0.61568 c -0.252786,-0.45231 -0.562569,-0.89979 -1.012545,-1.23983 -0.450075,-0.34005 -1.039843,-0.5546 -1.758805,-0.55123 z m 7.218009,0 c -0.718861,-0.004 -1.308729,0.21119 -1.758704,0.55123 -0.450076,0.34005 -0.759759,0.78753 -1.012546,1.23982 a 0.43421497,0.4342385 0 0 0 0.537671,0.61568 c 0.594368,-0.2347 1.032244,-0.54301 1.575615,-0.64111 0.54327,-0.0981 1.239633,-0.0313 2.39647,0.61567 a 0.43421497,0.4342385 0 0 0 0.642665,-0.32565 c 0,0 0.043,-0.33363 -0.053,-0.72083 -0.09499,-0.38719 -0.36858,-0.88946 -0.944649,-1.11432 a 0.43421497,0.4342385 0 0 0 -0.041,-0.0153 c -0.495374,-0.13835 -0.941049,-0.20326 -1.343228,-0.20522 z" /> + + + + + + + diff --git a/public/assets/images/share.svg b/public/assets/images/share.svg new file mode 100644 index 0000000..23fa1fb --- /dev/null +++ b/public/assets/images/share.svg @@ -0,0 +1 @@ + diff --git a/public/assets/images/star.svg b/public/assets/images/star.svg new file mode 100644 index 0000000..d035a75 --- /dev/null +++ b/public/assets/images/star.svg @@ -0,0 +1 @@ + diff --git a/public/assets/js/MiiClass.js b/public/assets/js/MiiClass.js deleted file mode 100644 index 9a097cb..0000000 --- a/public/assets/js/MiiClass.js +++ /dev/null @@ -1,214 +0,0 @@ -// Fetched from https://github.com/PretendoNetwork/account/blob/master/src/mii.js on 2021-12-16 - -const KaitaiStream = require('kaitai-struct/KaitaiStream'); - -class Mii extends KaitaiStream { - constructor(arrayBuffer, byteOffset) { - super(arrayBuffer, byteOffset); - - this.decode(); - } - - decode() { - // Decode raw data - // A lot of this goes unused - this.unknown1 = this.readU1(); - this.characterSet = this.readBitsIntBe(2); - this.regionLock = this.readBitsIntBe(2); - this.profanityFlag = this.readBitsIntBe(1) !== 0; - this.copying = this.readBitsIntBe(1) !== 0; - this.unknown2 = this.readBitsIntBe(2); - this.slotIndex = this.readBitsIntBe(4); - this.pageIndex = this.readBitsIntBe(4); - this.version = this.readBitsIntBe(4); - this.unknown3 = this.readBitsIntBe(4); - this.systemId = Array(8).fill().map(() => this.readU1()); - this.avatarId = Array(4).fill().map(() => this.readU1()); - this.clientId = Array(6).fill().map(() => this.readU1()); - this.padding = this.readU2le(); - this.miiMetaData = this.readU2le(); - this.miiName = Buffer.from(this.readBytes(20)).toString('utf16le'); - this.height = this.readU1(); - this.build = this.readU1(); - this.faceColor = this.readBitsIntBe(3); - this.faceType = this.readBitsIntBe(4); - this.mingle = this.readBitsIntBe(1) !== 0; - this.faceMakeup = this.readBitsIntBe(4); - this.faceWrinkles = this.readBitsIntBe(4); - this.alignToByte(); - this.hairType = this.readU1(); - this.unknown5 = this.readBitsIntBe(4); - this.hairFlip = this.readBitsIntBe(1) !== 0; - this.hairColor = this.readBitsIntBe(3); - this.alignToByte(); - this.eyeData = this.readU4le(); - this.eyebrowData = this.readU4le(); - this.noseData = this.readU2le(); - this.mouthData = this.readU2le(); - this.mouthData2 = this.readU2le(); - this.facialHairData = this.readU2le(); - this.glassesData = this.readU2le(); - this.moleData = this.readU2le(); - this.creatorName = Buffer.from(this.readBytes(20)).toString('utf16le'); - this.padding2 = this.readU2le(); - this.checksum = this.readU2le(); - - // Carve out more specific data from the above values - // TODO: read these bits directly instead of getting them later - - this.gender = (this.miiMetaData & 1); - this.birthMonth = ((this.miiMetaData >> 1) & 15); - this.birthDay = ((this.miiMetaData >> 5) & 31); - this.favoriteColor = ((this.miiMetaData >> 10) & 15); - this.favorite = ((this.miiMetaData >> 14) & 1); - - this.eyeType = (this.eyeData & 63); - this.eyeColor = ((this.eyeData >> 6) & 7); - this.eyeSize = ((this.eyeData >> 9) & 7); - this.eyeStretch = ((this.eyeData >> 13) & 7); - this.eyeRotation = ((this.eyeData >> 16) & 31); - this.eyeHorizontal = ((this.eyeData >> 21) & 15); - this.eyeVertical = ((this.eyeData >> 25) & 31); - - this.eyebrowType = (this.eyebrowData & 31); - this.eyebrowColor = ((this.eyebrowData >> 5) & 7); - this.eyebrowSize = ((this.eyebrowData >> 8) & 15); - this.eyebrowStretch = ((this.eyebrowData >> 12) & 7); - this.eyebrowRotation = ((this.eyebrowData >> 16) & 15); - this.eyebrowHorizontal = ((this.eyebrowData >> 21) & 15); - this.eyebrowVertical = ((this.eyebrowData >> 25) & 31); - - this.noseType = (this.noseData & 31); - this.noseSize = ((this.noseData >> 5) & 15); - this.noseVertical = ((this.noseData >> 9) & 31); - - - this.mouthType = (this.mouthData & 63); - this.mouthColor = ((this.mouthData >> 6) & 7); - this.mouthSize = ((this.mouthData >> 9) & 15); - this.mouthStretch = ((this.mouthData >> 13) & 7); - - this.mouthVertical = (this.mouthData2 & 31); - this.facialHairMustache = ((this.mouthData2 >> 5) & 7); - - this.facialHairType = (this.facialHairData & 7); - this.facialHairColor = ((this.facialHairData >> 3) & 7); - this.facialHairSize = ((this.facialHairData >> 6) & 15); - this.facialHairVertical = ((this.facialHairData >> 10) & 31); - - this.glassesType = (this.glassesData & 15); - this.glassesColor = (this.glassesData >> 4) & 7; - this.glassesSize = (this.glassesData >> 7) & 15; - this.glassesVertical = (this.glassesData >> 11) & 15; - - this.moleEnable = (this.moleData >> 15); - this.moleSize = ((this.moleData >> 1) & 15); - this.moleHorizontal = ((this.moleData >> 5) & 31); - this.moleVertical = ((this.moleData >> 10) & 31); - } - - toStudioMii() { - /* - Can also disable randomization with: - let miiStudioData = Buffer.alloc(0x2F); - let next = 256; - and removing "randomizer" and the "miiStudioData.writeUInt8(randomizer);" call - */ - const miiStudioData = Buffer.alloc(0x2F); - const randomizer = Math.floor(256 * Math.random()); - let next = randomizer; - let pos = 1; - - function encodeMiiPart(partValue) { - const encoded = (7 + (partValue ^ next)) % 256; - next = encoded; - - miiStudioData.writeUInt8(encoded, pos); - pos++; - } - - miiStudioData.writeUInt8(randomizer); - - if (this.facialHairColor === 0) { - encodeMiiPart(8); - } else { - encodeMiiPart(this.facialHairColor); - } - - encodeMiiPart(this.facialHairType); - encodeMiiPart(this.build); - encodeMiiPart(this.eyeStretch); - encodeMiiPart(this.eyeColor + 8); - encodeMiiPart(this.eyeRotation); - encodeMiiPart(this.eyeSize); - encodeMiiPart(this.eyeType); - encodeMiiPart(this.eyeHorizontal); - encodeMiiPart(this.eyeVertical); - encodeMiiPart(this.eyebrowStretch); - - if (this.eyebrowColor === 0) { - encodeMiiPart(8); - } else { - encodeMiiPart(this.eyebrowColor); - } - - encodeMiiPart(this.eyebrowRotation); - encodeMiiPart(this.eyebrowSize); - encodeMiiPart(this.eyebrowType); - encodeMiiPart(this.eyebrowHorizontal); - encodeMiiPart(this.eyebrowVertical); - encodeMiiPart(this.faceColor); - encodeMiiPart(this.faceMakeup); - encodeMiiPart(this.faceType); - encodeMiiPart(this.faceWrinkles); - encodeMiiPart(this.favoriteColor); - encodeMiiPart(this.gender); - - if (this.glassesColor == 0) { - encodeMiiPart(8); - } else if (this.glassesColor < 6) { - encodeMiiPart(this.glassesColor + 13); - } else { - encodeMiiPart(0); - } - - encodeMiiPart(this.glassesSize); - encodeMiiPart(this.glassesType); - encodeMiiPart(this.glassesVertical); - - if (this.hairColor == 0) { - encodeMiiPart(8); - } else { - encodeMiiPart(this.hairColor); - } - - encodeMiiPart(this.hairFlip ? 1 : 0); - encodeMiiPart(this.hairType); - encodeMiiPart(this.height); - encodeMiiPart(this.moleSize); - encodeMiiPart(this.moleEnable); - encodeMiiPart(this.moleHorizontal); - encodeMiiPart(this.moleVertical); - encodeMiiPart(this.mouthStretch); - - if (this.mouthColor < 4) { - encodeMiiPart(this.mouthColor + 19); - } else { - encodeMiiPart(0); - } - - encodeMiiPart(this.mouthSize); - encodeMiiPart(this.mouthType); - encodeMiiPart(this.mouthVertical); - encodeMiiPart(this.facialHairSize); - encodeMiiPart(this.facialHairMustache); - encodeMiiPart(this.facialHairVertical); - encodeMiiPart(this.noseSize); - encodeMiiPart(this.noseType); - encodeMiiPart(this.noseVertical); - - return miiStudioData; - } -} - -module.exports = Mii; diff --git a/public/assets/js/miieditor.js b/public/assets/js/miieditor.js index 928510d..5d91c5a 100644 --- a/public/assets/js/miieditor.js +++ b/public/assets/js/miieditor.js @@ -1,4 +1,4 @@ -// This file gets automatically bundled with browserify when running the start script. This also means that after any update you're gonna need to restart the server. +// This file gets automatically bundled with browserify when running the start script. This also means that after any update you're gonna need to restart the server to see any changes. // Prevent the user from reloading or leaving the page window.addEventListener('beforeunload', function (e) { @@ -6,7 +6,7 @@ window.addEventListener('beforeunload', function (e) { e.returnValue = ''; }); -const Mii = require('./MiiClass.js'); +const Mii = require('mii-js'); // MII RENDERER @@ -15,32 +15,52 @@ const encodedUserMiiData = document.querySelector( 'script#encodedUserMiiData' ).textContent; document.querySelector('script#encodedUserMiiData').remove(); -// We initialize the Mii object with the encoded data and render the Mii + +// We initialize the Mii object const mii = new Mii(Buffer.from(encodedUserMiiData, 'base64')); + +// We set the img sources for the unedited miis in the save animation +const miiStudioNeutralUrl = mii.studioUrl({ + width: 512, + bgColor: '13173300', +}); +const miiStudioSorrowUrl = mii.studioUrl({ + width: 512, + bgColor: '13173300', + expression: 'sorrow', +}); +document.querySelector('.mii-comparison img.old-mii').src = miiStudioNeutralUrl; +document.querySelector('.mii-comparison.confirmed img.old-mii').src = + miiStudioSorrowUrl; + +// Initial mii render renderMii(); -const oldMiiStudioData = mii.toStudioMii().toString('hex'); - -// initial setup for saving animation -document.querySelector('.mii-comparison img.old-mii').src = `https://studio.mii.nintendo.com/miis/image.png?data=${oldMiiStudioData}&type=face&expression=normal&width=512&bgColor=13173300`; -document.querySelector('.mii-comparison.confirmed img.old-mii').src = `https://studio.mii.nintendo.com/miis/image.png?data=${oldMiiStudioData}&type=face&expression=sorrow&width=512&bgColor=13173300`; - // This function renders the Mii on the page function renderMii(type) { - type = type || 'all_body'; // Can be 'all_body' or 'face' + const miiStudioUrl = mii.studioUrl({ + width: 512, + bgColor: '13173300', + type: type || 'all_body', + }); - const miiStudioData = mii.toStudioMii().toString('hex'); - document.querySelector( - 'img#mii-img' - ).src = `https://studio.mii.nintendo.com/miis/image.png?data=${miiStudioData}&type=${type}&expression=normal&width=512&bgColor=13173300`; + const faceMiiStudioUrl = mii.studioUrl({ + width: 512, + bgColor: '13173300', + }); + + const faceMiiStudioSmileUrl = mii.studioUrl({ + width: 512, + bgColor: '13173300', + expression: 'smile' + }); + + // sets the mii + document.querySelector('img#mii-img').src = miiStudioUrl; // sets the new mii in the save tab to the new mii - document.querySelector( - '.mii-comparison img.new-mii' - ).src = `https://studio.mii.nintendo.com/miis/image.png?data=${miiStudioData}&type=face&expression=normal&width=512&bgColor=13173300`; - document.querySelector( - '.mii-comparison.confirmed img.new-mii' - ).src = `https://studio.mii.nintendo.com/miis/image.png?data=${miiStudioData}&type=face&expression=smile&width=512&bgColor=13173300`; + document.querySelector('.mii-comparison img.new-mii').src = faceMiiStudioUrl; + document.querySelector('.mii-comparison.confirmed img.new-mii').src = faceMiiStudioSmileUrl; // this sets the mii height so that the face width stays the same document.querySelector('img#mii-img').style.height = `${ @@ -58,27 +78,91 @@ function renderMii(type) { // This function updates a prop of the Mii and rerenders it function updateMii(e, type) { const prop = e.target.name; - const value = e.target.value; - mii[prop] = parseInt(value); + let value = e.target.value || e.target.defaultValue; + + // if the value comes from a checkbox, we use the checked property + if (value === 'on' || value === 'off') { + value = e.target.checked; + } + + // if the prop is disableSharing, we set the value to the opposite of the current value + if (prop === 'disableSharing') { + value = !value; + } + + // Handle booleans, on/offs and strings + if (value === 'true' || value === 'false') { + mii[prop] = value === 'true'; + } else if (value === 'on' || value === 'off') { + mii[prop] = value === 'on'; + } else if (isNaN(parseInt(value))) { + mii[prop] = value; + } else { + mii[prop] = parseInt(value); + } + renderMii(type); } +// prevent the user from inputting values out of the range +function handleRange(e) { + if (e.target.value === '') return; + + const value = parseInt(e.target.value); + + // if the value is out of range, set to the nearest in-range value + if (value > e.target.max) { + e.target.value = e.target.max; + } else if (value < e.target.min) { + e.target.value = e.target.min; + } +} + +function handleCalendar(e) { + const month = parseInt(e.target.value); + + // we get the days in the month (hardcoded to 2024 'cause it's a leap year) + const daysInMonth = new Date(2024, month, 0).getDate(); + + const birthDayElement = document.querySelector('input[type=\'number\']#birthDay'); + + // we set the max value for the day selector to the days in the month + birthDayElement.max = daysInMonth; + + // if the day is greater than the days in the month, set it to the max + if (parseInt(birthDayElement.value) > daysInMonth) { + birthDayElement.value = daysInMonth; + } +} + +function preventEmpty(e) { + if (e.target.value !== '') return; + + e.target.value = e.target.defaultValue; +} + document.querySelectorAll('fieldset').forEach((fieldset) => { fieldset.addEventListener('change', (e) => updateMii(e)); }); -document.querySelectorAll('input[type=\'range\']').forEach((fieldset) => { - fieldset.addEventListener('input', (e) => updateMii(e)); +document.querySelectorAll('input[type=\'range\']').forEach((input) => { + input.addEventListener('input', (e) => updateMii(e)); }); +document.querySelectorAll('input[type=\'text\'], input[type=\'number\']').forEach((input) => { + input.addEventListener('blur', (e) => preventEmpty(e)); +}); + +document.querySelector('input[type=\'number\']#birthMonth').addEventListener('blur', (e) => handleCalendar(e)); + // FORM // Here we preselect the options corresponding to the Mii's current values [ 'faceType', - 'faceColor', - 'faceMakeup', - 'faceWrinkles', + 'skinColor', + 'makeupType', + 'wrinklesType', 'hairType', 'hairColor', 'eyebrowType', @@ -90,45 +174,78 @@ document.querySelectorAll('input[type=\'range\']').forEach((fieldset) => { 'mouthColor', 'glassesType', 'glassesColor', - 'facialHairType', + 'beardType', 'facialHairColor', - 'facialHairMustache', - 'moleEnable', + 'mustacheType', + 'moleEnabled', 'gender', - 'favoriteColor' + 'favoriteColor', ].forEach((prop) => { - document.querySelector(`#${prop}${mii[prop]}`).checked = true; + const el = document.querySelector(`#${prop}${mii[prop]}`); + if (el) { + el.checked = true; + } + console.log(`[info] preselected value for ${prop}`); }); [ - 'eyebrowVertical', - 'eyebrowHorizontal', - 'eyebrowRotation', - 'eyebrowSize', - 'eyebrowStretch', - 'eyeVertical', - 'eyeHorizontal', - 'eyeRotation', - 'eyeSize', - 'eyeStretch', - 'noseVertical', - 'noseSize', - 'mouthVertical', - 'mouthSize', - 'mouthStretch', - 'glassesVertical', - 'glassesSize', - 'facialHairVertical', - 'facialHairSize', - 'moleVertical', - 'moleHorizontal', - 'moleSize', - 'height', - 'build' + 'favorite', + 'allowCopying' ].forEach((prop) => { - document.querySelector(`#${prop}`).value = mii[prop]; + const el = document.querySelector(`#${prop}`); + if (el) { + el.checked = mii[prop]; + } + console.log(`[info] preselected value for ${prop}`); }); +document.querySelector('#disableSharing').checked = !mii.disableSharing; +console.log('[info] preselected value for disableSharing'); + +[ + 'eyebrowYPosition', + 'eyebrowSpacing', + 'eyebrowRotation', + 'eyebrowScale', + 'eyebrowVerticalStretch', + 'eyeYPosition', + 'eyeSpacing', + 'eyeRotation', + 'eyeScale', + 'eyeVerticalStretch', + 'noseYPosition', + 'noseScale', + 'mouthYPosition', + 'mouthScale', + 'mouthHorizontalStretch', + 'glassesYPosition', + 'glassesScale', + 'mustacheYPosition', + 'mustacheScale', + 'moleYPosition', + 'moleXPosition', + 'moleScale', + 'height', + 'build', + 'miiName', + 'creatorName', + 'birthDay', + 'birthMonth', +].forEach((prop) => { + document.querySelector(`#${prop}`).value = mii[prop]; + document.querySelector(`#${prop}`).defaultValue = mii[prop]; + console.log(`[info] preselected value for ${prop}`); +}); + + +[ + 'birthDay', + 'birthMonth' +].forEach((prop) => { + document.querySelector(`#${prop}`).addEventListener('input', (e) => { + handleRange(e); + }); +}); // TABS, SUBTABS, AND ALL THE INHERENT JANK @@ -136,19 +253,22 @@ function openTab(e, tabType) { e.preventDefault(); // Deselect all subpages - document.querySelectorAll('.subtab.has-subpages .subpage.active').forEach((el) => { - el.classList?.remove('active'); - }); + document + .querySelectorAll('.subtab.has-subpages .subpage.active') + .forEach((el) => { + el.classList?.remove('active'); + }); document.querySelectorAll('.subtab.active').forEach((el) => { el.classList?.remove('active'); }); - const buttonReplacement = tabType.charAt(0).toUpperCase() + tabType.slice(1); + const buttonReplacement = + tabType.charAt(0).toUpperCase() + tabType.slice(1); - document.querySelectorAll(`.${tabType}.active`).forEach(el => { + document.querySelectorAll(`.${tabType}.active`).forEach((el) => { el?.classList?.remove('active'); }); - document.querySelectorAll(`.${tabType}btn.active`).forEach(el => { + document.querySelectorAll(`.${tabType}btn.active`).forEach((el) => { el?.classList?.remove('active'); }); @@ -170,9 +290,7 @@ function openTab(e, tabType) { }); // Selects the first subpage if there is one - document - .querySelector(`#${selectedID} .subpage`) - ?.classList?.add('active'); + document.querySelector(`#${selectedID} .subpage`)?.classList?.add('active'); } // Here we bind all of the functions to the corresponding buttons @@ -223,26 +341,41 @@ document.querySelectorAll('button.page-btn').forEach((el) => { }); // mii saving business (animation jank & actual saving) -document.querySelector('#saveTab #saveButton').addEventListener('click', (e) => { - e.preventDefault(); +document + .querySelector('#saveTab #saveButton') + .addEventListener('click', (e) => { + e.preventDefault(); - document.querySelector('#saveTab #saveButton').classList.add('inactive', 'fade-out'); - document.querySelector('.tabs').style.pointerEvents = 'none'; - document.querySelector('.mii-comparison.confirmed').style.opacity = 1; - document.querySelector('#saveTab p.save-prompt').classList.add('fade-out'); + document + .querySelector('#saveTab #saveButton') + .classList.add('inactive', 'fade-out'); + document.querySelector('.tabs').style.pointerEvents = 'none'; + document.querySelector('.mii-comparison.confirmed').style.opacity = 1; + document + .querySelector('#saveTab p.save-prompt') + .classList.add('fade-out'); - setTimeout(() => { - document.querySelector('.mii-comparison.unconfirmed').style.opacity = 0; - }, 500); + setTimeout(() => { + document.querySelector( + '.mii-comparison.unconfirmed' + ).style.opacity = 0; + }, 500); - setTimeout(() => { - document.querySelector('.mii-comparison.confirmed .old-mii').classList.add('fade-out'); - document.querySelector('.mii-comparison.confirmed svg').classList.add('fade-out'); - }, 1500); + setTimeout(() => { + document + .querySelector('.mii-comparison.confirmed .old-mii') + .classList.add('fade-out'); + document + .querySelector('.mii-comparison.confirmed svg') + .classList.add('fade-out'); + }, 1500); - setTimeout(() => { - document.querySelector('.mii-comparison.confirmed .new-mii-wrapper').classList.add('centered-mii-img'); - }, 2000); + setTimeout(() => { + document + .querySelector('.mii-comparison.confirmed .new-mii-wrapper') + .classList.add('centered-mii-img'); + }, 2000); - -}); + alert(mii.encode().toString('base64')); + // CHECK IF MII IS VALID SERVERSIDE + }); diff --git a/views/account/miieditor.handlebars b/views/account/miieditor.handlebars index 421d0f2..8116077 100644 --- a/views/account/miieditor.handlebars +++ b/views/account/miieditor.handlebars @@ -37,8 +37,8 @@ - - + +
@@ -46,9 +46,9 @@
- - - + + +
@@ -58,32 +58,32 @@ {{/each}}
-
- - - - - - - - - - - - +
+ + + + + + + + + + + +
-
+
{{#each editorJSON.arrayOf12}} - - + + {{/each}}
-
+
{{#each editorJSON.arrayOf12}} - - + + {{/each}}
@@ -173,16 +173,16 @@
- - - - + + + + - - - - + + + +
@@ -251,16 +251,16 @@
- - - - + + + + - - - - + + + +
@@ -294,10 +294,10 @@
- - - - + + + +
@@ -346,12 +346,12 @@
- - - - - - + + + + + +
@@ -384,25 +384,25 @@
- - - - + + + +
-
+
- - + +
-
+
{{#each editorJSON.arrayOf6}} - - + + {{/each}}
@@ -425,40 +425,40 @@
-
+
{{#each editorJSON.arrayOf6}} - - + + {{/each}}
- - - - + + + +
-
+
- +
-
- - - - +
+ + + +
- - - - - - + + + + + +
@@ -467,6 +467,7 @@ +
@@ -483,6 +484,42 @@
+
+
+ + +
+
+ + +
+
+
+ + +
+ / +
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
From f586fe9313c2466c29cd51a0f59d80df8a48968e Mon Sep 17 00:00:00 2001 From: Ash Monty Date: Sat, 13 Aug 2022 17:42:30 +0200 Subject: [PATCH 05/34] style: change birthday label wording --- public/assets/css/miieditor.css | 5 +---- views/account/miieditor.handlebars | 11 ++++------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/public/assets/css/miieditor.css b/public/assets/css/miieditor.css index 8371908..a2af548 100644 --- a/public/assets/css/miieditor.css +++ b/public/assets/css/miieditor.css @@ -205,12 +205,9 @@ div.subtabs .subtabbtn.active:hover::before { div.birthday { display: grid; grid-template-columns: 1fr auto 1fr; - align-items: end; + align-items: center; gap: 12px; } -div.birthday span { - margin-bottom: 12px; -} .has-textinput .icons { display: flex; flex-flow: row nowrap; diff --git a/views/account/miieditor.handlebars b/views/account/miieditor.handlebars index 8116077..3ca56c4 100644 --- a/views/account/miieditor.handlebars +++ b/views/account/miieditor.handlebars @@ -493,14 +493,11 @@
-
-
- +
+ +
-
- / -
- + /
From 54e4ad3237b530a98440c1b01264214893bc695c Mon Sep 17 00:00:00 2001 From: Ash Monty Date: Sat, 13 Aug 2022 20:27:00 +0200 Subject: [PATCH 06/34] feat: use native date input element --- public/assets/css/miieditor.css | 7 +---- public/assets/js/miieditor.js | 50 +++++++----------------------- views/account/miieditor.handlebars | 8 ++--- 3 files changed, 14 insertions(+), 51 deletions(-) diff --git a/public/assets/css/miieditor.css b/public/assets/css/miieditor.css index a2af548..90bdedd 100644 --- a/public/assets/css/miieditor.css +++ b/public/assets/css/miieditor.css @@ -202,12 +202,7 @@ div.subtabs .subtabbtn.active:hover::before { text-transform: uppercase; font-size: 12px; } -div.birthday { - display: grid; - grid-template-columns: 1fr auto 1fr; - align-items: center; - gap: 12px; -} + .has-textinput .icons { display: flex; flex-flow: row nowrap; diff --git a/public/assets/js/miieditor.js b/public/assets/js/miieditor.js index 5d91c5a..d04f671 100644 --- a/public/assets/js/miieditor.js +++ b/public/assets/js/miieditor.js @@ -104,35 +104,13 @@ function updateMii(e, type) { renderMii(type); } -// prevent the user from inputting values out of the range -function handleRange(e) { - if (e.target.value === '') return; - - const value = parseInt(e.target.value); - - // if the value is out of range, set to the nearest in-range value - if (value > e.target.max) { - e.target.value = e.target.max; - } else if (value < e.target.min) { - e.target.value = e.target.min; - } -} - function handleCalendar(e) { - const month = parseInt(e.target.value); + const valueArray = e.target.value.split('-'); + const day = valueArray[2]; + const month = valueArray[1]; - // we get the days in the month (hardcoded to 2024 'cause it's a leap year) - const daysInMonth = new Date(2024, month, 0).getDate(); - - const birthDayElement = document.querySelector('input[type=\'number\']#birthDay'); - - // we set the max value for the day selector to the days in the month - birthDayElement.max = daysInMonth; - - // if the day is greater than the days in the month, set it to the max - if (parseInt(birthDayElement.value) > daysInMonth) { - birthDayElement.value = daysInMonth; - } + mii.birthDay = parseInt(day); + mii.birthMonth = parseInt(month); } function preventEmpty(e) { @@ -153,7 +131,7 @@ document.querySelectorAll('input[type=\'text\'], input[type=\'number\']').forEac input.addEventListener('blur', (e) => preventEmpty(e)); }); -document.querySelector('input[type=\'number\']#birthMonth').addEventListener('blur', (e) => handleCalendar(e)); +document.querySelector('input[type=\'date\']#birthDate').addEventListener('change', (e) => handleCalendar(e)); // FORM @@ -229,23 +207,17 @@ console.log('[info] preselected value for disableSharing'); 'build', 'miiName', 'creatorName', - 'birthDay', - 'birthMonth', ].forEach((prop) => { document.querySelector(`#${prop}`).value = mii[prop]; document.querySelector(`#${prop}`).defaultValue = mii[prop]; console.log(`[info] preselected value for ${prop}`); }); - -[ - 'birthDay', - 'birthMonth' -].forEach((prop) => { - document.querySelector(`#${prop}`).addEventListener('input', (e) => { - handleRange(e); - }); -}); +const paddedBirthDay = mii.birthDay.toString().padStart(2, '0'); +const paddedBirthMonth = mii.birthMonth.toString().padStart(2, '0'); +document.querySelector('input[type=\'date\']#birthDate') + .value = `2024-${paddedBirthMonth}-${paddedBirthDay}`; +console.log('[info] preselected value for birthMonth && birthDay'); // TABS, SUBTABS, AND ALL THE INHERENT JANK diff --git a/views/account/miieditor.handlebars b/views/account/miieditor.handlebars index 3ca56c4..b519eee 100644 --- a/views/account/miieditor.handlebars +++ b/views/account/miieditor.handlebars @@ -494,12 +494,8 @@
- -
- - / - -
+ +
From f4b2618b3a28a926bb781524a7ccaa15b29c6555 Mon Sep 17 00:00:00 2001 From: Ash Monty Date: Sat, 13 Aug 2022 22:19:48 +0200 Subject: [PATCH 07/34] locales: add all strings to locale, rewrite locale helper --- locales/en_US.json | 82 +++++++++++++++++++++++-- src/server.js | 51 +++++++++------ views/account/account.handlebars | 68 ++++++++++---------- views/account/login.handlebars | 14 ++--- views/account/register.handlebars | 18 +++--- views/account/upgrade.handlebars | 46 ++++++++------ views/blog/blog.handlebars | 4 +- views/blog/blogpost.handlebars | 4 +- views/docs/docs.handlebars | 10 +-- views/home.handlebars | 48 +++++++-------- views/localization.handlebars | 12 ++-- views/partials/footer.handlebars | 20 +++--- views/partials/header.handlebars | 16 ++--- views/partials/progress-list.handlebars | 2 +- views/progress.handlebars | 18 ++++-- 15 files changed, 256 insertions(+), 157 deletions(-) diff --git a/locales/en_US.json b/locales/en_US.json index 019c350..2f2e595 100644 --- a/locales/en_US.json +++ b/locales/en_US.json @@ -6,7 +6,11 @@ "credits": "Credits", "progress": "Progress", "blog": "Blog", - "account": "Account" + "account": "Account", + "accountWidget": { + "settings": "Settings", + "logout": "Logout" + } }, "hero": { "subtitle": "Game servers", @@ -25,10 +29,7 @@ }, "progress": { "title": "Progress", - "paragraphs": [ - "Currently, we are working on Miiverse, along with our account servers and its integration with the services.", - "For 3DS, we are also working on Mario Kart 7, with a desire to continue working on other games when possible." - ] + "githubRepo": "Github repository" }, "faq": { "title": "Frequently Asked Questions", @@ -221,9 +222,57 @@ }, "blogPage": { "title": "Blog", - "description": "The latest updates in condensed chunks. If you want to see more frequent updates, consider supporting us on Patreon." + "description": "The latest updates in condensed chunks. If you want to see more frequent updates, consider supporting us on Patreon.", + "published": "Published by", + "publishedOn": "on" }, "account": { + "loginForm": { + "login": "Login", + "register": "Register", + "detailsPrompt": "Enter your account details below", + "username": "Username", + "password": "Password", + "confirmPassword": "Confirm password", + "email": "Email", + "miiName": "Mii name", + "forgotPassword": "Forgot your password?", + "registerPrompt": "Don't have an account?", + "loginPrompt": "Already have an account?" + }, + "settings": { + "downloadFiles": "Download account files", + "downloadFilesDescription": "(will not work on Nintendo Network)", + "upgrade": "Upgrade account", + "settingCards": { + "profile": "Profile", + "nickname": "Nickname", + "birthDate": "Birth date", + "gender": "Gender", + "country": "Country/region", + "timezone": "Timezone", + "serverEnv": "Server environment", + "production": "Production", + "beta": "Beta", + "upgradePrompt": "Beta servers are exclusive to beta testers.
To become a beta tester, upgrade to a higher account tier.", + "hasAccessPrompt": "Your current tier gives you beta server access. Cool!", + "signInSecurity": "Sign in and security", + "email": "Email", + "password": "Password", + "passwordResetNotice": "After changing your password, you will be signed out from all devices.", + "signInHistory": "Sign in history", + "fullSignInHistory": "View full sign in history", + "otherSettings": "Other settings", + "discord": "Discord", + "connectedToDiscord": "Connected to Discord as", + "removeDiscord": "Remove Discord account", + "noDiscordLinked": "No Discord account linked.", + "linkDiscord": "Link Discord account", + "newsletter": "Newsletter", + "newsletterPrompt": "Receive project updates via email (you can opt-out at any time)", + "passwordPrompt": "Enter your PNID password to download Cemu files" + } + }, "accountLevel": [ "Standard", "Tester", @@ -232,6 +281,23 @@ ], "banned": "Banned" }, + "upgrade": { + "title": "Upgrade", + "description": "Reaching the monthly goal will make Pretendo a full time job, providing better quality updates at a faster rate.", + "month": "month", + "tierSelectPrompt": "Select a tier", + "unsub": "Unsubscribe", + "unsubPrompt": "Are you sure you want to unsubscribe from tiername? You will lose access to the perks associated with that tier.", + "unsubConfirm": "Unsubscribe", + "changeTier": "Change tier", + "changeTierPrompt": "Are you sure you want to unsubscribe from oldtiername and subscribe to newtiername?", + "changeTierConfirm": "Change tier", + "back": "Back" + }, + "donation": { + "progress": "$${totd} of $${goald}/month, ${perc}% of the monthly goal.", + "upgradePush": "To become a subscriber and gain access to cool perks, visit the upgrade page." + }, "localizationPage": { "title": "Let's localize", "description": "Paste a link to a publicly accessible JSON locale to test it on the website", @@ -255,5 +321,9 @@ } ] } + }, + "modals": { + "cancel": "Cancel", + "confirm": "Confirm" } } diff --git a/src/server.js b/src/server.js index 2dce61b..8c4bf7d 100644 --- a/src/server.js +++ b/src/server.js @@ -136,28 +136,43 @@ app.engine('handlebars', handlebars({ neq(value1, value2) { return value1 !== value2; }, - localeHelper(...args) { - let userLocaleString = args[0]; - + localeHelper(locale, propString, propVar) { /* - * Removes the first and the last argument, and then loops through the rest to - * get the string in the user's locale. If not available, it will return it in - * the default locale. - */ + locale: the json locale object + propString: the "path" to the property to get (e.g. "faq.title") + propVar: the dynamic property name to get after the "path" (e.g. localeHelper locale "account.level" would result in ) + e.g. localeHelper locale "account.test" "account.level" would result in locale.account.test[account.level] + */ - args.slice(1, -1).forEach(arg => { - userLocaleString = userLocaleString?.[arg]; - }); + let value = locale; + const props = propString.split('.'); - if (!userLocaleString) { - let defaultLocaleString = defaultLocale; - args.slice(1, -1).forEach(arg => { - defaultLocaleString = defaultLocaleString?.[arg]; - }); - return defaultLocaleString; - } else { - return userLocaleString; + if ( typeof propVar === 'string' || propVar instanceof String || + typeof propVar === 'number' || propVar instanceof Number ) { + props.push(propVar); } + + function getPropFromLocale(value, props) { + for (const p of props) { + value = value[p]; + } + + return value; + } + + // fall back to english if the prop doesn't exist in the current locale + try { + value = getPropFromLocale(value, props); + } catch (e) { + value = defaultLocale; + try { + value = getPropFromLocale(value, props); + } catch (e) { + logger.error(`Could not find locale property ${props.join('.')}`); + } + } + + return value; } } })); diff --git a/views/account/account.handlebars b/views/account/account.handlebars index a4b5b01..c94cbff 100644 --- a/views/account/account.handlebars +++ b/views/account/account.handlebars @@ -14,21 +14,21 @@

{{tierName}}

{{else}} {{#if (neq account.access_level -1)}} -

{{ localeHelper locale "account" "accountLevel" account.access_level }}

+

{{ localeHelper locale "account.accountLevel" account.access_level }}

{{else}} -

{{ localeHelper locale "account" "banned" }}

+

{{ localeHelper locale "account.banned" }}

{{/if}} {{/if}}
@@ -36,7 +36,7 @@

User settings

-

Profile

+

{{ localeHelper locale "account.settings.settingCards.profile" }}

@@ -44,30 +44,30 @@
  • -

    Nickname

    +

    {{ localeHelper locale "account.settings.settingCards.nickname" }}

    {{account.mii.name}}

  • -

    Birth date

    +

    {{ localeHelper locale "account.settings.settingCards.birthDate" }}

    {{account.birthdate}}

  • -

    Gender

    +

    {{ localeHelper locale "account.settings.settingCards.gender" }}

    {{account.gender}}

  • -

    Country/region

    +

    {{ localeHelper locale "account.settings.settingCards.country" }}

    {{account.country}}

  • -

    Timezone

    +

    {{ localeHelper locale "account.settings.settingCards.timezone" }}

    {{account.timezone.name}}

-

Server environment

+

{{ localeHelper locale "account.settings.settingCards.serverEnv" }}

@@ -80,7 +80,7 @@ -

Production

+

{{ localeHelper locale "account.settings.settingCards.production" }}

{{#if isTester}} @@ -88,19 +88,19 @@ -

Beta

+

{{ localeHelper locale "account.settings.settingCards.beta" }}

{{/if}}
{{#unless isTester}} -

Beta servers are exclusive to beta testers.
To become a beta tester, upgrade to a higher account tier.

+

{{{ localeHelper locale "account.settings.settingCards.upgradePrompt" }}}

{{else}} -

Your current tier gives you beta server access. Cool!

+

{{ localeHelper locale "account.settings.settingCards.hasAccessPrompt" }}

{{/unless}}
-

Sign in and security

+

{{ localeHelper locale "account.settings.settingCards.signInSecurity" }}

Account

@@ -110,19 +110,19 @@
  • -

    Email

    +

    {{ localeHelper locale "account.settings.settingCards.email" }}

    {{account.email.address}}

  • -

    Password

    -

    ●●●●●●●●

    +

    {{ localeHelper locale "account.settings.settingCards.password" }}

    +

    ●●●●●●●●

-

After changing your password, you will be signed out from all devices.

+

{{ localeHelper locale "account.settings.settingCards.passwordResetNotice" }}

-

Other settings

+

{{ localeHelper locale "account.settings.settingCards.otherSettings" }}

-

Discord

+

{{ localeHelper locale "account.settings.settingCards.discord" }}

{{#if discordUser}} -

Connected to Discord as {{ discordUser.username }}#{{ discordUser.discriminator }}

- +

{{ localeHelper locale "account.settings.settingCards.connectedToDiscord" }} {{ discordUser.username }}#{{ discordUser.discriminator }}

+ {{else}} -

No Discord account linked. Link Discord account here

+

{{ localeHelper locale "account.settings.settingCards.noDiscordLinked" }} {{ localeHelper locale "account.settings.settingCards.linkDiscord" }}

{{/if}}
-

Newsletter

+

{{ localeHelper locale "account.settings.settingCards.newsletter" }}

- +
@@ -178,12 +178,12 @@ diff --git a/views/account/login.handlebars b/views/account/login.handlebars index 03e004c..7664ed6 100644 --- a/views/account/login.handlebars +++ b/views/account/login.handlebars @@ -6,22 +6,22 @@ diff --git a/views/account/register.handlebars b/views/account/register.handlebars index a9b3b61..8419bad 100644 --- a/views/account/register.handlebars +++ b/views/account/register.handlebars @@ -7,33 +7,33 @@ diff --git a/views/account/upgrade.handlebars b/views/account/upgrade.handlebars index 32a8bb9..6e8b32a 100644 --- a/views/account/upgrade.handlebars +++ b/views/account/upgrade.handlebars @@ -2,7 +2,7 @@
- Back + {{ localeHelper locale "upgrade.back" }}

- {{this.price}} / month + {{this.price}} / {{ localeHelper locale "upgrade.month" }}

{{/each}}
- - + +
+ + From 2b2ec1435836e50ce9ecaaa5061d53a3b19a835f Mon Sep 17 00:00:00 2001 From: Ash Monty Date: Sat, 13 Aug 2022 22:26:17 +0200 Subject: [PATCH 08/34] locales: remove untranslated strings from non-en locales --- locales/ar_AR.json | 67 +-------------------------------------- locales/de_DE.json | 37 +--------------------- locales/es_ES.json | 9 ------ locales/fr_FR.json | 9 ------ locales/ja_JP.json | 9 ------ locales/ko_KR.json | 9 ------ locales/nb_NO.json | 9 ------ locales/nl_NL.json | 67 +-------------------------------------- locales/pl_PL.json | 9 ------ locales/pt_BR.json | 25 --------------- locales/ro_RO.json | 29 +---------------- locales/ru_RU.json | 23 +------------- locales/tr_TR.json | 79 +--------------------------------------------- locales/zh_CN.json | 9 ------ 14 files changed, 6 insertions(+), 384 deletions(-) diff --git a/locales/ar_AR.json b/locales/ar_AR.json index df0188c..2043310 100644 --- a/locales/ar_AR.json +++ b/locales/ar_AR.json @@ -68,25 +68,6 @@ } ] }, - "showcase": { - "title": "What we make", - "text": "Our project has many components. Here are some of them.", - "cards": [ - { - "title": "Game servers", - "caption": "Bringing back your favorite games and content using custom servers." - }, - { - "title": "Juxtaposition", - "caption": "A re-imagining of Miiverse, as if it were made in the modern era." - }, - { - "title": "Cemu support", - "caption": "Play your favorite Wii U titles even without a console!" - } - - ] - }, "credits": { "title": "الفريق", "text": "قابل الفريق وراء المشروع", @@ -195,25 +176,6 @@ } ] }, - "discordJoin": { - "title": "Stay up to date", - "text": "Join our Discord server to get the latest updates on the project.", - "widget": { - "text": "Get realtime updates to our progress", - "button": "Join the server" - } - }, - "footer": { - "socials": "Socials", - "usefulLinks": "Useful links", - "widget": { - "captions": [ - "Want to keep updated?", - "Join our Discord server!" - ], - "button": "Join now!" - } - }, "progressPage": { "title": "تقدمنا", "description": "تحقق من تقدم المشروع والأهداف! (يتم تحديثه كل ساعة أو نحو ذلك ، ولا يعكس جميع أهداف المشروع أو تقدمه)" @@ -222,37 +184,10 @@ "title": "البلوج", "description": "آخر التحديثات في أجزاء مكثفة. إذا كنت ترغب في رؤية المزيد من التحديثات المتكررة ، ففكر في دعمنا على Patreon . " }, - "account": { - "accountLevel": [ - "Standard", - "Tester", - "Moderator", - "Developer" - ], - "banned": "Banned" - }, "localizationPage": { "title": "!لنترجم", "description": "الصق رابطا إلي لغة الزيسون يمكن الوصول إليها بشكل عام لاختبارها على موقع الويب", "instructions": "اظهر خطوات الترجمة", - "fileInput": "ملف للتجربة", - "filePlaceholder": "https://a.link.to/the_file.json", - "button": "Test file" - }, - "docs": { - "missingInLocale": "This page is unavailable in your locale. Please check the English version below.", - "quickLinks": { - "header": "Quick links", - "links": [ - { - "header": "Install Pretendo", - "caption": "View the setup instructions" - }, - { - "header": "Got an error?", - "caption": "Search for it here" - } - ] - } + "fileInput": "ملف للتجربة" } } diff --git a/locales/de_DE.json b/locales/de_DE.json index d9db7b3..899e3c6 100644 --- a/locales/de_DE.json +++ b/locales/de_DE.json @@ -4,9 +4,7 @@ "faq": "FAQ", "docs": "Docs", "credits": "Mitwirkende", - "progress": "Fortschritt", - "blog": "Blog", - "account": "Account" + "progress": "Fortschritt" }, "hero": { "subtitle": "Spiel-Server", @@ -222,38 +220,5 @@ "blogPage": { "title": "Blog", "description": "Die letzten Updates in Kurzform. Wenn du Updates häufiger einsehen möchtest, unterstütze uns doch bei Patreon." - }, - "account": { - "accountLevel": [ - "Standard", - "Tester", - "Moderator", - "Developer" - ], - "banned": "Banned" - }, - "localizationPage": { - "title": "Let's localize", - "description": "Paste a link to a publicly accessible JSON locale to test it on the website", - "instructions": "View localization instructions", - "fileInput": "File to test", - "filePlaceholder": "https://a.link.to/the_file.json", - "button": "Test file" - }, - "docs": { - "missingInLocale": "This page is unavailable in your locale. Please check the English version below.", - "quickLinks": { - "header": "Quick links", - "links": [ - { - "header": "Install Pretendo", - "caption": "View the setup instructions" - }, - { - "header": "Got an error?", - "caption": "Search for it here" - } - ] - } } } diff --git a/locales/es_ES.json b/locales/es_ES.json index d6b4e24..42ef30b 100644 --- a/locales/es_ES.json +++ b/locales/es_ES.json @@ -223,15 +223,6 @@ "title": "Blog", "description": "Las últimas actualizaciones en pequeñas cantidades. Si quieres ver actualizaciones más frecuentes, considera apoyarnos en Patreon." }, - "account": { - "accountLevel": [ - "Standard", - "Tester", - "Moderator", - "Developer" - ], - "banned": "Banned" - }, "localizationPage": { "title": "Vamos a localizar", "description": "Pega un enlace a una localización JSON accesible pulicamente para probarla en el sitio", diff --git a/locales/fr_FR.json b/locales/fr_FR.json index 8dfc2fb..709c0de 100644 --- a/locales/fr_FR.json +++ b/locales/fr_FR.json @@ -225,15 +225,6 @@ "title": "Blog", "description": "Les dernières mises à jour en blocs condensés. Si vous souhaitez voir des mises à jour plus fréquentes, envisagez de nous soutenir sur Patreon." }, - "account": { - "accountLevel": [ - "Standard", - "Tester", - "Moderator", - "Developer" - ], - "banned": "Banned" - }, "localizationPage": { "title": "Localisons", "description": "Collez un lien vers un paramètre régional JSON accessible au public pour le tester sur le site Web", diff --git a/locales/ja_JP.json b/locales/ja_JP.json index b5a5492..042cc3b 100644 --- a/locales/ja_JP.json +++ b/locales/ja_JP.json @@ -223,15 +223,6 @@ "title": "ブログ(英語)", "description": "最新のアップデートをまとめてお届けします。もっと多くのアップデートを貰いたい方は、Patreonでの寄付をご検討ください。" }, - "account": { - "accountLevel": [ - "Standard", - "Tester", - "Moderator", - "Developer" - ], - "banned": "Banned" - }, "localizationPage": { "title": "レッツローカライズ", "description": "公開されているJSONへのリンクを貼り付けてこのサイトに試してみて下さい!", diff --git a/locales/ko_KR.json b/locales/ko_KR.json index bd0949d..409ad8c 100644 --- a/locales/ko_KR.json +++ b/locales/ko_KR.json @@ -223,15 +223,6 @@ "title": "블로그", "description": "내용이 압축된 최신 업데이트입니다. 만약 더 많은 업데이트를 원하신다면, Patreon에 기부하는 것을 검토해 주세요." }, - "account": { - "accountLevel": [ - "Standard", - "Tester", - "Moderator", - "Developer" - ], - "banned": "Banned" - }, "localizationPage": { "title": "같이 번역해 주세요!", "description": "액세스 가능한 JSON의 링크를 붙여넣어서, 웹사이트에서 테스트해 보세요!", diff --git a/locales/nb_NO.json b/locales/nb_NO.json index 0e431a8..3003a95 100644 --- a/locales/nb_NO.json +++ b/locales/nb_NO.json @@ -223,15 +223,6 @@ "title": "Blogg", "description": "De nyeste oppdateringen er i kondensert biter. Hvis du har lyst til å se mer hyppig oppdateringen, tenk om å støtte oss på Patreon." }, - "account": { - "accountLevel": [ - "Standard", - "Tester", - "Moderator", - "Developer" - ], - "banned": "Banned" - }, "localizationPage": { "title": "La oss lokalisere", "description": "Lim inn en lenke til en offentlig JSON lokal til å teste det på nettsiden", diff --git a/locales/nl_NL.json b/locales/nl_NL.json index da83bf8..6396fe9 100644 --- a/locales/nl_NL.json +++ b/locales/nl_NL.json @@ -4,9 +4,7 @@ "faq": "FAQ", "docs": "Docs", "credits": "Credits", - "progress": "Vooruitgang", - "blog": "Blog", - "account": "Account" + "progress": "Vooruitgang" }, "hero": { "subtitle": "Game servers", @@ -68,25 +66,6 @@ } ] }, - "showcase": { - "title": "What we make", - "text": "Our project has many components. Here are some of them.", - "cards": [ - { - "title": "Game servers", - "caption": "Bringing back your favorite games and content using custom servers." - }, - { - "title": "Juxtaposition", - "caption": "A re-imagining of Miiverse, as if it were made in the modern era." - }, - { - "title": "Cemu support", - "caption": "Play your favorite Wii U titles even without a console!" - } - - ] - }, "credits": { "title": "Het team", "text": "Ontmoet het team achter het project", @@ -196,25 +175,6 @@ } ] }, - "discordJoin": { - "title": "Stay up to date", - "text": "Join our Discord server to get the latest updates on the project.", - "widget": { - "text": "Get realtime updates to our progress", - "button": "Join the server" - } - }, - "footer": { - "socials": "Socials", - "usefulLinks": "Useful links", - "widget": { - "captions": [ - "Want to keep updated?", - "Join our Discord server!" - ], - "button": "Join now!" - } - }, "progressPage": { "title": "Onze vooruitgang", "description": "Zie hier onze vooruitgang! (Wordt elk uur ververst, geeft niet ALLE vooruitgang en doelen weer)" @@ -223,15 +183,6 @@ "title": "Blog", "description": "De meest recente samenvattingen. Als je vaker nieuws wil, overweeg ons op Patreon te ondersteunen." }, - "account": { - "accountLevel": [ - "Standard", - "Tester", - "Moderator", - "Developer" - ], - "banned": "Banned" - }, "localizationPage": { "title": "Laten we meer talen toe voegen!", "description": "Voeg hier een link in voor een openbaar JSON bestand om het op de site te testen.", @@ -239,21 +190,5 @@ "fileInput": "Bestand om te testen", "filePlaceholder": "https://een.link.naar/het_bestand.json", "button": "Test bestand" - }, - "docs": { - "missingInLocale": "This page is unavailable in your locale. Please check the English version below.", - "quickLinks": { - "header": "Quick links", - "links": [ - { - "header": "Install Pretendo", - "caption": "View the setup instructions" - }, - { - "header": "Got an error?", - "caption": "Search for it here" - } - ] - } } } diff --git a/locales/pl_PL.json b/locales/pl_PL.json index c1bdb73..3e8a99f 100644 --- a/locales/pl_PL.json +++ b/locales/pl_PL.json @@ -222,15 +222,6 @@ "title": "Blog", "description": "Najnowsze informacje w krótkich tekstach. Jeśli chcesz otrzymywać częstsze aktualizacje, pomyśl nad wsparciem nas na Patreon." }, - "account": { - "accountLevel": [ - "Standard", - "Tester", - "Moderator", - "Developer" - ], - "banned": "Banned" - }, "localizationPage": { "title": "Tłumaczmy", "description": "Wklej link do publicznego pliku JSON, aby przetestować go na stronie", diff --git a/locales/pt_BR.json b/locales/pt_BR.json index f9ca0cb..5855a50 100644 --- a/locales/pt_BR.json +++ b/locales/pt_BR.json @@ -223,15 +223,6 @@ "title": "Blog", "description": "As atualizações mais recentes em postagens condensadas. Se você quiser ver atualizações com mais frequência, considere nos ajudar no Patreon." }, - "account": { - "accountLevel": [ - "Standard", - "Tester", - "Moderator", - "Developer" - ], - "banned": "Banned" - }, "localizationPage": { "title": "Vamos traduzir", "description": "Cole um link para um arquivo JSON acessível publicamente para testá-lo no site", @@ -239,21 +230,5 @@ "fileInput": "Arquivo para testar", "filePlaceholder": "https://a.link.to/the_file.json", "button": "Testar aqui" - }, - "docs": { - "missingInLocale": "This page is unavailable in your locale. Please check the English version below.", - "quickLinks": { - "header": "Quick links", - "links": [ - { - "header": "Install Pretendo", - "caption": "View the setup instructions" - }, - { - "header": "Got an error?", - "caption": "Search for it here" - } - ] - } } } diff --git a/locales/ro_RO.json b/locales/ro_RO.json index 384c2e1..06d37ec 100644 --- a/locales/ro_RO.json +++ b/locales/ro_RO.json @@ -4,9 +4,7 @@ "faq": "FAQ", "docs": "Docs", "credits": "Mulțumiri", - "progress": "Progres", - "blog": "Blog", - "account": "Account" + "progress": "Progres" }, "hero": { "subtitle": "Servere de Jocuri", @@ -223,15 +221,6 @@ "title": "Blog", "description": "Ultimele actualizări în bucăți condensate. Dacă vrei să vezi mai multe actualizări, consideră să ne susții pe Patreon." }, - "account": { - "accountLevel": [ - "Standard", - "Tester", - "Moderator", - "Developer" - ], - "banned": "Banned" - }, "localizationPage": { "title": "Hai să localizăm", "description": "Inserați un link către un JSON public pentru a testa traducerea pe site", @@ -239,21 +228,5 @@ "fileInput": "Fișier de testare", "filePlaceholder": "https://a.link.to/the_file.json", "button": "Testează fișier" - }, - "docs": { - "missingInLocale": "This page is unavailable in your locale. Please check the English version below.", - "quickLinks": { - "header": "Quick links", - "links": [ - { - "header": "Install Pretendo", - "caption": "View the setup instructions" - }, - { - "header": "Got an error?", - "caption": "Search for it here" - } - ] - } } } diff --git a/locales/ru_RU.json b/locales/ru_RU.json index bb4e695..ee14c33 100644 --- a/locales/ru_RU.json +++ b/locales/ru_RU.json @@ -4,9 +4,7 @@ "faq": "ЧаВо", "docs": "Docs", "credits": "Команда", - "progress": "Прогресс", - "blog": "Blog", - "account": "Account" + "progress": "Прогресс" }, "hero": { "subtitle": "Игровые сервера", @@ -68,25 +66,6 @@ } ] }, - "showcase": { - "title": "What we make", - "text": "Our project has many components. Here are some of them.", - "cards": [ - { - "title": "Game servers", - "caption": "Bringing back your favorite games and content using custom servers." - }, - { - "title": "Juxtaposition", - "caption": "A re-imagining of Miiverse, as if it were made in the modern era." - }, - { - "title": "Cemu support", - "caption": "Play your favorite Wii U titles even without a console!" - } - - ] - }, "credits": { "title": "Наша команда", "text": "Ознакомтесь с командой, которая работает над разработкой Pretendo.", diff --git a/locales/tr_TR.json b/locales/tr_TR.json index 8b2ab66..d26f668 100644 --- a/locales/tr_TR.json +++ b/locales/tr_TR.json @@ -4,9 +4,7 @@ "faq": "SSS", "docs": "Docs", "credits": "Katkıda Bulunanlar", - "progress": "İlerleme", - "blog": "Blog", - "account": "Account" + "progress": "İlerleme" }, "hero": { "subtitle": "Oyun sunucuları", @@ -68,25 +66,6 @@ } ] }, - "showcase": { - "title": "What we make", - "text": "Our project has many components. Here are some of them.", - "cards": [ - { - "title": "Game servers", - "caption": "Bringing back your favorite games and content using custom servers." - }, - { - "title": "Juxtaposition", - "caption": "A re-imagining of Miiverse, as if it were made in the modern era." - }, - { - "title": "Cemu support", - "caption": "Play your favorite Wii U titles even without a console!" - } - - ] - }, "credits": { "title": "Ekip", "text": "Projenin arkasındaki ekiple tanışın", @@ -196,64 +175,8 @@ } ] }, - "discordJoin": { - "title": "Stay up to date", - "text": "Join our Discord server to get the latest updates on the project.", - "widget": { - "text": "Get realtime updates to our progress", - "button": "Join the server" - } - }, - "footer": { - "socials": "Socials", - "usefulLinks": "Useful links", - "widget": { - "captions": [ - "Want to keep updated?", - "Join our Discord server!" - ], - "button": "Join now!" - } - }, "progressPage": { "title": "İlerlememiz", "description": "Proje ilerlemesini ve hedeflerini kontrol edin! (Her saat başı güncellenir. TÜM proje hedeflerini veya ilerlemesini yansıtmaz)" - }, - "blogPage": { - "title": "Blog", - "description": "The latest updates in condensed chunks. If you want to see more frequent updates, consider supporting us on Patreon." - }, - "account": { - "accountLevel": [ - "Standard", - "Tester", - "Moderator", - "Developer" - ], - "banned": "Banned" - }, - "localizationPage": { - "title": "Let's localize", - "description": "Paste a link to a publicly accessible JSON locale to test it on the website", - "instructions": "View localization instructions", - "fileInput": "File to test", - "filePlaceholder": "https://a.link.to/the_file.json", - "button": "Test file" - }, - "docs": { - "missingInLocale": "This page is unavailable in your locale. Please check the English version below.", - "quickLinks": { - "header": "Quick links", - "links": [ - { - "header": "Install Pretendo", - "caption": "View the setup instructions" - }, - { - "header": "Got an error?", - "caption": "Search for it here" - } - ] - } } } diff --git a/locales/zh_CN.json b/locales/zh_CN.json index bbb5d4a..99d5fcb 100644 --- a/locales/zh_CN.json +++ b/locales/zh_CN.json @@ -223,15 +223,6 @@ "title": "博客", "description": "压缩块的最新更新。如果您想看到更频繁的更新,请考虑在 Patreon 上支持我们。" }, - "account": { - "accountLevel": [ - "Standard", - "Tester", - "Moderator", - "Developer" - ], - "banned": "Banned" - }, "localizationPage": { "title": "让我们本地化", "description": "粘贴指向可公开访问的 JSON 语言环境的链接以在网站上对其进行测试", From 82d9688b414b621369fe440ed33c279a7e5fa4ec Mon Sep 17 00:00:00 2001 From: Ash Monty Date: Sun, 14 Aug 2022 10:43:35 +0200 Subject: [PATCH 09/34] style: add miieditor link to account page --- public/assets/css/account.css | 24 +++++++++++++++++++++++- public/assets/images/edit.svg | 1 + views/account/account.handlebars | 4 +++- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 public/assets/images/edit.svg diff --git a/public/assets/css/account.css b/public/assets/css/account.css index dbfbf68..6852c5c 100644 --- a/public/assets/css/account.css +++ b/public/assets/css/account.css @@ -76,12 +76,34 @@ border-color: #5AFF15; } -.account-sidebar .user .mii { +.account-sidebar .user a.mii { + position: relative; + display: block; width: 128px; height: 128px; + overflow: hidden; border-radius: 100%; background: var(--bg-shade-3); } +.account-sidebar .user a.mii::after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: no-repeat center/40% url("/assets/images/edit.svg"), rgba(55, 60, 101, 0.7); + opacity: 0; + transition: opacity 150ms; +} +.account-sidebar .user a.mii:hover::after { + opacity: 1; +} + +.account-sidebar .user .mii { + width: 100%; + height: 100%; +} .account-sidebar .buttons a { display: flex; flex-flow: column; diff --git a/public/assets/images/edit.svg b/public/assets/images/edit.svg new file mode 100644 index 0000000..d19401d --- /dev/null +++ b/public/assets/images/edit.svg @@ -0,0 +1 @@ + diff --git a/views/account/account.handlebars b/views/account/account.handlebars index a4b5b01..546b6a5 100644 --- a/views/account/account.handlebars +++ b/views/account/account.handlebars @@ -7,7 +7,9 @@