diff --git a/config.json.example b/config.json.example index 8a8a092ef0..d20fd328f1 100644 --- a/config.json.example +++ b/config.json.example @@ -75,7 +75,6 @@ "S3_ACCESS_KEY_ID": "accessKeyId", "S3_BUCKET": "bucket", "S3_SECRET_ACCESS_KEY": "secretAccessKey", - "SESSION_SECRET_IV": "12345678912345678912345678912345", "SESSION_SECRET_KEY": "1234567891234567891234567891234567891234567891234567891234567891", "SESSION_SECRET": "YOUR SECRET HERE", "SITE_HTTP_AUTH_ENABLED": "false", diff --git a/habitica-images b/habitica-images index 359153997e..32a4678c6b 160000 --- a/habitica-images +++ b/habitica-images @@ -1 +1 @@ -Subproject commit 359153997e4169898692eb4182450ee488599e89 +Subproject commit 32a4678c6b6fe12437c763ab7ed00d8d8ccea6c9 diff --git a/package-lock.json b/package-lock.json index 55926bd146..cfd857b4cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "habitica", - "version": "5.44.3", + "version": "5.47.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "habitica", - "version": "5.44.3", + "version": "5.47.0", "hasInstallScript": true, "dependencies": { "@babel/core": "^7.22.10", @@ -44,7 +44,7 @@ "gulp-filter": "^7.0.0", "gulp-imagemin": "^7.1.0", "gulp.spritesmith": "^6.13.0", - "habitica-markdown": "^3.0.0", + "habitica-markdown": "^4.1.0", "heapdump": "^0.3.15", "helmet": "^4.6.0", "in-app-purchase": "^1.11.3", @@ -105,6 +105,9 @@ "npm": "^10" } }, + "../habitica-markdown/habitica-markdown": { + "extraneous": true + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -4167,6 +4170,12 @@ "node": ">=14.0.0" } }, + "node_modules/apidoc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, "node_modules/apidoc/node_modules/bootstrap": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz", @@ -4202,6 +4211,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/apidoc/node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, "node_modules/apidoc/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4213,6 +4231,22 @@ "node": ">=10" } }, + "node_modules/apidoc/node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, "node_modules/apidoc/node_modules/nodemon": { "version": "2.0.22", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", @@ -12497,50 +12531,15 @@ } }, "node_modules/habitica-markdown": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-3.0.0.tgz", - "integrity": "sha512-rw1LJ5Vsjx8sfjNa4e2wFuZf5eqqyb5/kfZXPxqfMMgJCCgIhWStDqY3nIclnpGWpemlKd+qbdh2rLiLgm9kng==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-4.1.0.tgz", + "integrity": "sha512-iqiFT8BbuWIrrs6v9eIXSG7UfWOBdJVGDi9FjGiFFOe8+dOPi62yyXhm81vrx79/5Y2s+jG+7LItyaemD310uQ==", + "license": "GPL-3.0", "dependencies": { - "habitica-markdown-emoji": "1.2.4", - "markdown-it": "10.0.0", - "markdown-it-link-attributes": "3.0.0", - "markdown-it-linkify-images": "^1.1.1" - } - }, - "node_modules/habitica-markdown-emoji": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/habitica-markdown-emoji/-/habitica-markdown-emoji-1.2.4.tgz", - "integrity": "sha512-UV0AxpDToldFQULuhTxC1y4sdNTApaIOh7ZuV/92HCPmCGkv3DAlHtYE67OmCqLVfs26HWAGVJaU3+OEnW3gjg==", - "dependencies": { - "markdown-it-emoji": "^1.1.1" - } - }, - "node_modules/habitica-markdown/node_modules/entities": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", - "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==" - }, - "node_modules/habitica-markdown/node_modules/linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", - "dependencies": { - "uc.micro": "^1.0.1" - } - }, - "node_modules/habitica-markdown/node_modules/markdown-it": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", - "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", - "dependencies": { - "argparse": "^1.0.7", - "entities": "~2.0.0", - "linkify-it": "^2.0.0", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" + "markdown-it": "^14.0.0", + "markdown-it-emoji": "^2.0.2", + "markdown-it-link-attributes": "^4.0.1", + "markdown-it-linkify-images": "^3.0.0" } }, "node_modules/handlebars": { @@ -14552,13 +14551,20 @@ "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, + "node_modules/linkify-it/node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, "node_modules/load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -14906,59 +14912,79 @@ } }, "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "markdown-it": "bin/markdown-it.js" + "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/markdown-it-emoji": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz", - "integrity": "sha512-QCz3Hkd+r5gDYtS2xsFXmBYrgw6KuWcJZLCEkdfAuwzZbShCmCfta+hwAMq4NX/4xPzkSHduMKgMkkPUJxSXNg==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-2.0.2.tgz", + "integrity": "sha512-zLftSaNrKuYl0kR5zm4gxXjHaOI3FAOEaloKmRA5hijmJZvSjmxcokOLlzycb/HXlUFWzXqpIEoyEMCE4i9MvQ==", + "license": "MIT" }, "node_modules/markdown-it-link-attributes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/markdown-it-link-attributes/-/markdown-it-link-attributes-3.0.0.tgz", - "integrity": "sha512-B34ySxVeo6MuEGSPCWyIYryuXINOvngNZL87Mp7YYfKIf6DcD837+lXA8mo6EBbauKsnGz22ZH0zsbOiQRWTNg==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/markdown-it-link-attributes/-/markdown-it-link-attributes-4.0.1.tgz", + "integrity": "sha512-pg5OK0jPLg62H4k7M9mRJLT61gUp9nvG0XveKYHMOOluASo9OEF13WlXrpAp2aj35LbedAy3QOCgQCw0tkLKAQ==", + "license": "MIT" }, "node_modules/markdown-it-linkify-images": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/markdown-it-linkify-images/-/markdown-it-linkify-images-1.1.1.tgz", - "integrity": "sha512-1IEmAaAjIgAwY+tZI0sxDXdy9QKHutj5cN0lH2JBiSZt+2NYKrWRJj0cloQW3OFIfP2MLFA1E+6OLJhXPiLgNw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-linkify-images/-/markdown-it-linkify-images-3.0.0.tgz", + "integrity": "sha512-Vs5yGJa5MWjFgytzgtn8c1U6RcStj3FZKhhx459U8dYbEE5FTWZ6mMRkYMiDlkFO0j4VCsQT1LT557bY0ETgtg==", + "license": "MIT", "dependencies": { - "markdown-it": "^8.4.2" + "markdown-it": "^13.0.1" } }, + "node_modules/markdown-it-linkify-images/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, "node_modules/markdown-it-linkify-images/node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, "node_modules/markdown-it-linkify-images/node_modules/linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", + "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", + "license": "MIT", "dependencies": { "uc.micro": "^1.0.1" } }, "node_modules/markdown-it-linkify-images/node_modules/markdown-it": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", - "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.2.tgz", + "integrity": "sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==", + "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "entities": "~1.1.1", - "linkify-it": "^2.0.0", + "argparse": "^2.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" }, @@ -14969,7 +14995,32 @@ "node_modules/markdown-it/node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/markdown-it/node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, + "node_modules/markdown-it/node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" }, "node_modules/matchdep": { "version": "2.0.0", @@ -18049,6 +18100,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", diff --git a/package.json b/package.json index de92a06e03..f39b93dbb2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "habitica", "description": "A habit tracker app which treats your goals like a Role Playing Game.", - "version": "5.44.3", + "version": "5.47.0", "main": "./website/server/index.js", "dependencies": { "@babel/core": "^7.22.10", @@ -39,7 +39,7 @@ "gulp-filter": "^7.0.0", "gulp-imagemin": "^7.1.0", "gulp.spritesmith": "^6.13.0", - "habitica-markdown": "^3.0.0", + "habitica-markdown": "^4.1.0", "heapdump": "^0.3.15", "helmet": "^4.6.0", "in-app-purchase": "^1.11.3", diff --git a/test/api/unit/libs/payments/amazon/cancel.test.js b/test/api/unit/libs/payments/amazon/cancel.test.js index e24d3dbf52..24acf9795e 100644 --- a/test/api/unit/libs/payments/amazon/cancel.test.js +++ b/test/api/unit/libs/payments/amazon/cancel.test.js @@ -66,13 +66,15 @@ describe('Amazon Payments - Cancel Subscription', () => { group = generateGroup({ name: 'test group', type: 'guild', - privacy: 'public', + privacy: 'private', leader: user._id, }); group.purchased.plan.customerId = 'customer-id'; group.purchased.plan.planId = subKey; group.purchased.plan.lastBillingDate = new Date(); await group.save(); + user.guilds.push(group._id); + await user.save(); subscriptionBlock = common.content.subscriptionBlocks[subKey]; subscriptionLength = subscriptionBlock.months * 30; diff --git a/test/api/unit/libs/payments/amazon/subscribe.test.js b/test/api/unit/libs/payments/amazon/subscribe.test.js index 03cef6b0bc..5dd4835bb4 100644 --- a/test/api/unit/libs/payments/amazon/subscribe.test.js +++ b/test/api/unit/libs/payments/amazon/subscribe.test.js @@ -30,12 +30,14 @@ describe('Amazon Payments - Subscribe', () => { group = generateGroup({ name: 'test group', type: 'guild', - privacy: 'public', + privacy: 'private', leader: user._id, }); group.purchased.plan.customerId = 'customer-id'; group.purchased.plan.planId = subKey; await group.save(); + user.guilds.push(group._id); + await user.save(); amount = common.content.subscriptionBlocks[subKey].price; billingAgreementId = 'billingAgreementId'; @@ -246,11 +248,6 @@ describe('Amazon Payments - Subscribe', () => { user.guilds.push(groupId); await user.save(); - // Add existing users - user = new User(); - user.guilds.push(groupId); - await user.save(); - // Set expected amount sub.key = 'group_monthly'; sub.price = 9; diff --git a/test/api/unit/libs/payments/group-plans/group-payments-create.test.js b/test/api/unit/libs/payments/group-plans/group-payments-create.test.js index 17ff154a0a..0cd1f0dbed 100644 --- a/test/api/unit/libs/payments/group-plans/group-payments-create.test.js +++ b/test/api/unit/libs/payments/group-plans/group-payments-create.test.js @@ -128,11 +128,12 @@ describe('Purchasing a group plan for group', () => { expect(publicGroup.purchased.plan.planId).to.not.exist; data.groupId = publicGroup._id; + // Public Guilds are no longer even findable await expect(api.createSubscription(data)) .to.eventually.be.rejected.and.to.eql({ - httpCode: 401, - name: 'NotAuthorized', - message: i18n.t('onlyPrivateGuildsCanUpgrade'), + httpCode: 404, + name: 'NotFound', + message: i18n.t('groupNotFound'), }); const updatedGroup = await Group.findById(publicGroup._id).exec(); diff --git a/test/api/unit/libs/payments/paypal/subscribe-cancel.test.js b/test/api/unit/libs/payments/paypal/subscribe-cancel.test.js index 30618252d1..b925763e2f 100644 --- a/test/api/unit/libs/payments/paypal/subscribe-cancel.test.js +++ b/test/api/unit/libs/payments/paypal/subscribe-cancel.test.js @@ -30,13 +30,15 @@ describe('paypal - subscribeCancel', () => { group = generateGroup({ name: 'test group', type: 'guild', - privacy: 'public', + privacy: 'private', leader: user._id, }); group.purchased.plan.customerId = groupCustomerId; group.purchased.plan.planId = subKey; group.purchased.plan.lastBillingDate = new Date(); await group.save(); + user.guilds.push(group._id); + await user.save(); nextBillingDate = new Date(); diff --git a/test/api/unit/libs/payments/stripe/checkout.test.js b/test/api/unit/libs/payments/stripe/checkout.test.js index 42b05f6efb..b7a132f497 100644 --- a/test/api/unit/libs/payments/stripe/checkout.test.js +++ b/test/api/unit/libs/payments/stripe/checkout.test.js @@ -236,7 +236,7 @@ describe('Stripe - Checkout', () => { const group = generateGroup({ name: 'test group', type: 'guild', - privacy: 'public', + privacy: 'private', leader: user._id, }); const groupId = group._id; @@ -376,11 +376,13 @@ describe('Stripe - Checkout', () => { group = generateGroup({ name: 'test group', type: 'guild', - privacy: 'public', + privacy: 'private', leader: user._id, }); groupId = group._id; await group.save(); + user.guilds.push(group._id); + await user.save(); }); it('throws if user is not allowed to change group plan', async () => { diff --git a/test/api/unit/libs/payments/stripe/subscriptions.test.js b/test/api/unit/libs/payments/stripe/subscriptions.test.js index 3955037c02..ebde76fb3f 100644 --- a/test/api/unit/libs/payments/stripe/subscriptions.test.js +++ b/test/api/unit/libs/payments/stripe/subscriptions.test.js @@ -136,7 +136,7 @@ describe('Stripe - Subscriptions', () => { group = generateGroup({ name: 'test group', type: 'guild', - privacy: 'public', + privacy: 'private', leader: user._id, }); groupId = group._id; @@ -315,12 +315,14 @@ describe('Stripe - Subscriptions', () => { group = generateGroup({ name: 'test group', type: 'guild', - privacy: 'public', + privacy: 'private', leader: user._id, }); group.purchased.plan.customerId = 'customer-id'; group.purchased.plan.planId = subKey; await group.save(); + user.guilds.push(group._id); + await user.save(); groupId = group._id; }); diff --git a/test/api/unit/models/userNotification.test.js b/test/api/unit/models/userNotification.test.js index 772e8dba8f..8e9405ee0a 100644 --- a/test/api/unit/models/userNotification.test.js +++ b/test/api/unit/models/userNotification.test.js @@ -50,5 +50,59 @@ describe('UserNotification Model', () => { expect(safeNotifications[0].type).to.equal('NEW_CHAT_MESSAGE'); expect(safeNotifications[0].id).to.equal('123'); }); + + it('removes duplicate STREAK_ACHIEVEMENT notifications', () => { + // Fixes issue #13325 - Users receiving duplicate streak achievement notifications + const notifications = [ + new UserNotification({ + type: 'STREAK_ACHIEVEMENT', + id: 123, + data: {}, + }), + new UserNotification({ + type: 'STREAK_ACHIEVEMENT', + id: 456, + data: {}, + }), + new UserNotification({ + type: 'CRON', + id: 789, + data: {}, + }), // different type, should be kept + ]; + + const safeNotifications = UserNotification.cleanupCorruptData(notifications); + expect(safeNotifications.length).to.equal(2); + expect(safeNotifications[0].type).to.equal('STREAK_ACHIEVEMENT'); + expect(safeNotifications[0].id).to.equal('123'); + expect(safeNotifications[1].type).to.equal('CRON'); + expect(safeNotifications[1].id).to.equal('789'); + }); + + it('handles multiple STREAK_ACHIEVEMENT duplicates correctly', () => { + // Test case: 3 duplicate STREAK_ACHIEVEMENT notifications + const notifications = [ + new UserNotification({ + type: 'STREAK_ACHIEVEMENT', + id: 111, + data: {}, + }), + new UserNotification({ + type: 'STREAK_ACHIEVEMENT', + id: 222, + data: {}, + }), + new UserNotification({ + type: 'STREAK_ACHIEVEMENT', + id: 333, + data: {}, + }), + ]; + + const safeNotifications = UserNotification.cleanupCorruptData(notifications); + expect(safeNotifications.length).to.equal(1); + expect(safeNotifications[0].type).to.equal('STREAK_ACHIEVEMENT'); + expect(safeNotifications[0].id).to.equal('111'); // Keep first one + }); }); }); diff --git a/test/api/v3/integration/challenges/POST-challenges_challengeId_join.test.js b/test/api/v3/integration/challenges/POST-challenges_challengeId_join.test.js index fad086cd4f..40c5784a70 100644 --- a/test/api/v3/integration/challenges/POST-challenges_challengeId_join.test.js +++ b/test/api/v3/integration/challenges/POST-challenges_challengeId_join.test.js @@ -5,6 +5,8 @@ import { createAndPopulateGroup, translate as t, } from '../../../../helpers/api-integration/v3'; +import { model as Group } from '../../../../../website/server/models/group'; +import { TAVERN_ID } from '../../../../../website/common/script/constants'; describe('POST /challenges/:challengeId/join', () => { it('returns error when challengeId is not a valid UUID', async () => { @@ -27,6 +29,37 @@ describe('POST /challenges/:challengeId/join', () => { }); }); + context('public Guild', () => { + let group; + let groupLeader; + let members; + let challenge; + before(async () => { + ({ group, groupLeader, members } = await createAndPopulateGroup({ + groupDetails: { + name: 'test group', + type: 'guild', + privacy: 'private', + }, + members: 1, + upgradeToGroupPlan: true, + })); + challenge = await generateChallenge(groupLeader, group); + // Creation API is shut down, we need to simulate an extant public group + await Group.updateOne({ _id: group._id }, { $set: { privacy: 'public' }, $unset: { 'purchased.plan': 1 } }).exec(); + }); + + it('returns error when challengeId is in an old public Guild', async () => { + const authorizedUser = members[0]; // eslint-disable-line prefer-destructuring + + await expect(authorizedUser.post(`/challenges/${challenge._id}/join`)).to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('challengeNotFound'), + }); + }); + }); + context('Joining a valid challenge', () => { let groupLeader; let group; @@ -66,6 +99,15 @@ describe('POST /challenges/:challengeId/join', () => { expect(res.name).to.equal(challenge.name); }); + it('succeeds when it\'s a Tavern challenge, even if the user isn\'t a "member" of Tavern', async () => { + const tavern = await groupLeader.get(`/groups/${TAVERN_ID}`); + const tavernChallenge = await generateChallenge(groupLeader, tavern, { prize: 1 }); + const generalUser = await generateUser(); + + const res = await generalUser.post(`/challenges/${tavernChallenge._id}/join`); + expect(res.name).to.equal(tavernChallenge.name); + }); + it('returns challenge data', async () => { const res = await authorizedUser.post(`/challenges/${challenge._id}/join`); diff --git a/test/api/v3/integration/chat/GET-chat.test.js b/test/api/v3/integration/chat/GET-chat.test.js index 47c11a870f..29ccac1c84 100644 --- a/test/api/v3/integration/chat/GET-chat.test.js +++ b/test/api/v3/integration/chat/GET-chat.test.js @@ -62,9 +62,9 @@ describe('GET /groups/:groupId/chat', () => { it('returns error if user attempts to fetch a sunset Guild', async () => { await expect(user.get(`/groups/${group._id}/chat`)).to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('featureRetired'), + code: 404, + error: 'NotFound', + message: t('groupNotFound'), }); }); }); diff --git a/test/api/v3/integration/chat/POST-chat.like.test.js b/test/api/v3/integration/chat/POST-chat.like.test.js index 6bbe3c0415..0159ca1ad9 100644 --- a/test/api/v3/integration/chat/POST-chat.like.test.js +++ b/test/api/v3/integration/chat/POST-chat.like.test.js @@ -121,9 +121,9 @@ describe('POST /chat/:chatId/like', () => { await expect(user.post(`/groups/${groupWithChat._id}/chat/${message.message.id}/like`)) .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('featureRetired'), + code: 404, + error: 'NotFound', + message: t('groupNotFound'), }); }); }); diff --git a/test/api/v3/integration/dataexport/GET-export_avatar-memberId.html.test.js b/test/api/v3/integration/dataexport/GET-export_avatar-memberId.html.test.js deleted file mode 100644 index d75d0ead25..0000000000 --- a/test/api/v3/integration/dataexport/GET-export_avatar-memberId.html.test.js +++ /dev/null @@ -1,35 +0,0 @@ -import { v4 as generateUUID } from 'uuid'; -import { - generateUser, - translate as t, -} from '../../../../helpers/api-integration/v3'; - -xdescribe('GET /export/avatar-:memberId.html', () => { - let user; - - before(async () => { - user = await generateUser(); - }); - - it('validates req.params.memberId', async () => { - await expect(user.get('/export/avatar-:memberId.html')).to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('invalidReqParams'), - }); - }); - - it('handles non-existing members', async () => { - const dummyId = generateUUID(); - await expect(user.get(`/export/avatar-${dummyId}.html`)).to.eventually.be.rejected.and.eql({ - code: 404, - error: 'NotFound', - message: t('userWithIDNotFound', { userId: dummyId }), - }); - }); - - it('returns an html page', async () => { - const res = await user.get(`/export/avatar-${user._id}.html`); - expect(res.substring(0, 100).indexOf('')).to.equal(0); - }); -}); diff --git a/test/api/v3/integration/dataexport/GET-export_avatar-memberId.png.test.js b/test/api/v3/integration/dataexport/GET-export_avatar-memberId.png.test.js deleted file mode 100644 index a46ed695c6..0000000000 --- a/test/api/v3/integration/dataexport/GET-export_avatar-memberId.png.test.js +++ /dev/null @@ -1,3 +0,0 @@ -// TODO how to test this route since it points to a file on AWS s3? - -describe('GET /export/avatar-:memberId.png', () => {}); diff --git a/test/api/v3/integration/dataexport/GET-export_inbox.html.test.js b/test/api/v3/integration/dataexport/GET-export_inbox.html.test.js index b9f9b96fff..d153828688 100644 --- a/test/api/v3/integration/dataexport/GET-export_inbox.html.test.js +++ b/test/api/v3/integration/dataexport/GET-export_inbox.html.test.js @@ -38,7 +38,7 @@ describe('GET /export/inbox.html', () => { it('renders the markdown messages as html', async () => { const res = await user.get('/export/inbox.html'); - expect(res).to.include('img class="habitica-emoji"'); + expect(res).to.include('😄'); expect(res).to.include('

Hello!

'); expect(res).to.include('
  • list 1
  • '); }); @@ -46,7 +46,7 @@ describe('GET /export/inbox.html', () => { it('sorts messages from newest to oldest', async () => { const res = await user.get('/export/inbox.html'); - const emojiPosition = res.indexOf('img class="habitica-emoji"'); + const emojiPosition = res.indexOf('😄'); const headingPosition = res.indexOf('

    Hello!

    '); const listPosition = res.indexOf('
  • list 1
  • '); diff --git a/test/common/libs/habiticaMarkdown.test.js b/test/common/libs/habiticaMarkdown.test.js new file mode 100644 index 0000000000..2ee91da77b --- /dev/null +++ b/test/common/libs/habiticaMarkdown.test.js @@ -0,0 +1,67 @@ +import md from 'habitica-markdown'; + +describe('habiticaMarkdown emoji plugin', () => { + it('renders standard emoji as Unicode', () => { + const result = md.render(':smile:'); + expect(result).to.include('😄'); + expect(result).not.to.include('img'); + }); + + it('renders thumbsup emoji as Unicode', () => { + const result = md.render(':thumbsup:'); + expect(result).to.include('👍'); + }); + + it('renders +1 emoji as Unicode', () => { + const result = md.render(':+1:'); + expect(result).to.include('👍'); + }); + + it('renders melior as an img tag', () => { + const result = md.render(':melior:'); + expect(result).to.include(' { + const result = md.render('[:smile: link](http://example.com)'); + expect(result).to.include(':smile: link'); + expect(result).not.to.include('😄'); + }); + + it('converts emoji outside of links normally', () => { + const result = md.render(':smile: [link](http://example.com)'); + expect(result).to.include('😄'); + expect(result).to.include('link'); + }); + + it('leaves removed custom emoji (bowtie) as literal text', () => { + const result = md.render(':bowtie:'); + expect(result).to.include(':bowtie:'); + expect(result).not.to.include('img'); + }); + + it('leaves unknown shortcodes as literal text', () => { + const result = md.render(':nonexistent_emoji_xyz:'); + expect(result).to.include(':nonexistent_emoji_xyz:'); + }); + + it('renders new emoji not in the old dataset', () => { + const result = md.render(':yawning_face:'); + expect(result).to.include('🥱'); + }); + + it('supports unsafeHTMLRender', () => { + const result = md.unsafeHTMLRender('bold :smile:'); + expect(result).to.include('bold'); + expect(result).to.include('😄'); + }); + + it('supports renderWithMentions', () => { + const result = md.renderWithMentions(':smile: @testuser', { userName: 'testuser' }); + expect(result).to.include('😄'); + expect(result).to.include('at-text'); + expect(result).to.include('at-highlight'); + }); +}); diff --git a/test/common/ops/rebirth.js b/test/common/ops/rebirth.js index 5193126c6a..d7accbfd43 100644 --- a/test/common/ops/rebirth.js +++ b/test/common/ops/rebirth.js @@ -211,22 +211,32 @@ describe('shared.ops.rebirth', () => { expect(user.achievements.rebirthLevel).to.equal(2); }); - it('does not increment rebirth achievements when level is lower than previous', async () => { + it('increments rebirth achievements even when level is lower than previous', async () => { user.stats.lvl = 2; user.achievements.rebirths = 1; user.achievements.rebirthLevel = 3; await rebirth(user); - expect(user.achievements.rebirths).to.equal(1); + expect(user.achievements.rebirths).to.equal(2); expect(user.achievements.rebirthLevel).to.equal(3); }); - it('always increments rebirth achievements when level is MAX_LEVEL', async () => { + it('updates rebirthLevel when current level is higher than previous', async () => { + user.stats.lvl = 5; + user.achievements.rebirths = 1; + user.achievements.rebirthLevel = 3; + + await rebirth(user); + + expect(user.achievements.rebirths).to.equal(2); + expect(user.achievements.rebirthLevel).to.equal(5); + }); + + it('increments rebirth achievements when level is MAX_LEVEL', async () => { user.stats.lvl = MAX_LEVEL; user.achievements.rebirths = 1; - // this value is not actually possible (actually capped at MAX_LEVEL) but makes a good test - user.achievements.rebirthLevel = MAX_LEVEL + 1; + user.achievements.rebirthLevel = MAX_LEVEL; await rebirth(user); @@ -234,11 +244,10 @@ describe('shared.ops.rebirth', () => { expect(user.achievements.rebirthLevel).to.equal(MAX_LEVEL); }); - it('always increments rebirth achievements when level is greater than MAX_LEVEL', async () => { + it('increments rebirth achievements when level is greater than MAX_LEVEL', async () => { user.stats.lvl = MAX_LEVEL + 1; user.achievements.rebirths = 1; - // this value is not actually possible (actually capped at MAX_LEVEL) but makes a good test - user.achievements.rebirthLevel = MAX_LEVEL + 2; + user.achievements.rebirthLevel = MAX_LEVEL; await rebirth(user); diff --git a/test/content/aprilFools.test.js b/test/content/aprilFools.test.js new file mode 100644 index 0000000000..64fc100df6 --- /dev/null +++ b/test/content/aprilFools.test.js @@ -0,0 +1,41 @@ +import { getMatchingSwap, makeSubstitutionMap } from '../../website/common/script/content/constants/aprilFools'; + +describe('April Fools', () => { + describe('getMatchingSwap', () => { + it('returns Veggie for 2020', () => { + const swap = getMatchingSwap(new Date('2020-04-01')); + expect(swap).to.equal('Veggie'); + }); + it('returns Alien for 2026', () => { + const swap = getMatchingSwap(new Date('2026-04-01')); + expect(swap).to.equal('Alien'); + }); + it('Cycles through swaps correctly', () => { + const swap = getMatchingSwap(new Date('2027-04-01')); + expect(swap).to.equal('Veggie'); + }); + }); + + describe('makeSubstitutionMap', () => { + it('returns correct substitution for Veggie', () => { + const substitutions = makeSubstitutionMap('Veggie'); + expect(substitutions.pets['Pet-Wolf-']).to.equal('Pet-Wolf-Veggie'); + expect(substitutions.pets['Pet-TigerCub-']).to.equal('Pet-TigerCub-Veggie'); + expect(substitutions.pets['Pet-Yarn-']).to.equal('Pet-BearCub-Veggie'); + expect(substitutions.pets.default).to.equal('Pet-Dragon-Veggie'); + expect(substitutions.pets.noPet).to.equal('Pet-Wolf-Veggie'); + expect(substitutions.pets.noPetIOS).to.equal('Pet-TigerCub-Veggie'); + expect(substitutions.pets.noPetAndroid).to.equal('Pet-Cactus-Veggie'); + }); + + it('returns correct substitution for Cryptid', () => { + const substitutions = makeSubstitutionMap('Cryptid'); + expect(substitutions.pets['Pet-Fox-']).to.equal('Pet-Fox-Cryptid'); + expect(substitutions.pets['Pet-FlyingPig-']).to.equal('Pet-FlyingPig-Cryptid'); + expect(substitutions.pets['Pet-Yarn-']).to.equal('Pet-BearCub-Cryptid'); + expect(substitutions.pets.default).to.equal('Pet-Dragon-Cryptid'); + expect(substitutions.pets.noPet).to.equal('Pet-Wolf-Cryptid'); + expect(substitutions.pets.noPetAndroid).to.equal('Pet-Cactus-Cryptid'); + }); + }); +}); diff --git a/website/client/package-lock.json b/website/client/package-lock.json index 3d7b531d1a..d1fdf37c7a 100644 --- a/website/client/package-lock.json +++ b/website/client/package-lock.json @@ -23,7 +23,7 @@ "eslint-config-habitrpg": "6.2.0", "eslint-plugin-mocha": "5.3.0", "eslint-plugin-vue": "7.20.0", - "habitica-markdown": "^3.0.0", + "habitica-markdown": "^4.0.0", "hellojs": "^1.20.0", "intro.js": "^7.2.0", "jquery": "^3.7.1", @@ -6929,69 +6929,17 @@ "license": "ISC" }, "node_modules/habitica-markdown": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-3.0.0.tgz", - "integrity": "sha512-rw1LJ5Vsjx8sfjNa4e2wFuZf5eqqyb5/kfZXPxqfMMgJCCgIhWStDqY3nIclnpGWpemlKd+qbdh2rLiLgm9kng==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-4.1.0.tgz", + "integrity": "sha512-iqiFT8BbuWIrrs6v9eIXSG7UfWOBdJVGDi9FjGiFFOe8+dOPi62yyXhm81vrx79/5Y2s+jG+7LItyaemD310uQ==", "license": "GPL-3.0", "dependencies": { - "habitica-markdown-emoji": "1.2.4", - "markdown-it": "10.0.0", - "markdown-it-link-attributes": "3.0.0", - "markdown-it-linkify-images": "^1.1.1" + "markdown-it": "^14.0.0", + "markdown-it-emoji": "^2.0.2", + "markdown-it-link-attributes": "^4.0.1", + "markdown-it-linkify-images": "^3.0.0" } }, - "node_modules/habitica-markdown-emoji": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/habitica-markdown-emoji/-/habitica-markdown-emoji-1.2.4.tgz", - "integrity": "sha512-UV0AxpDToldFQULuhTxC1y4sdNTApaIOh7ZuV/92HCPmCGkv3DAlHtYE67OmCqLVfs26HWAGVJaU3+OEnW3gjg==", - "license": "GPL-3.0", - "dependencies": { - "markdown-it-emoji": "^1.1.1" - } - }, - "node_modules/habitica-markdown/node_modules/entities": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", - "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", - "license": "BSD-2-Clause" - }, - "node_modules/habitica-markdown/node_modules/linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", - "license": "MIT", - "dependencies": { - "uc.micro": "^1.0.1" - } - }, - "node_modules/habitica-markdown/node_modules/markdown-it": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", - "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "entities": "~2.0.0", - "linkify-it": "^2.0.0", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/habitica-markdown/node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "license": "MIT" - }, - "node_modules/habitica-markdown/node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "license": "MIT" - }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -8084,50 +8032,62 @@ } }, "node_modules/markdown-it-emoji": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz", - "integrity": "sha512-QCz3Hkd+r5gDYtS2xsFXmBYrgw6KuWcJZLCEkdfAuwzZbShCmCfta+hwAMq4NX/4xPzkSHduMKgMkkPUJxSXNg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-2.0.2.tgz", + "integrity": "sha512-zLftSaNrKuYl0kR5zm4gxXjHaOI3FAOEaloKmRA5hijmJZvSjmxcokOLlzycb/HXlUFWzXqpIEoyEMCE4i9MvQ==", "license": "MIT" }, "node_modules/markdown-it-link-attributes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/markdown-it-link-attributes/-/markdown-it-link-attributes-3.0.0.tgz", - "integrity": "sha512-B34ySxVeo6MuEGSPCWyIYryuXINOvngNZL87Mp7YYfKIf6DcD837+lXA8mo6EBbauKsnGz22ZH0zsbOiQRWTNg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/markdown-it-link-attributes/-/markdown-it-link-attributes-4.0.1.tgz", + "integrity": "sha512-pg5OK0jPLg62H4k7M9mRJLT61gUp9nvG0XveKYHMOOluASo9OEF13WlXrpAp2aj35LbedAy3QOCgQCw0tkLKAQ==", "license": "MIT" }, "node_modules/markdown-it-linkify-images": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/markdown-it-linkify-images/-/markdown-it-linkify-images-1.1.1.tgz", - "integrity": "sha512-1IEmAaAjIgAwY+tZI0sxDXdy9QKHutj5cN0lH2JBiSZt+2NYKrWRJj0cloQW3OFIfP2MLFA1E+6OLJhXPiLgNw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-linkify-images/-/markdown-it-linkify-images-3.0.0.tgz", + "integrity": "sha512-Vs5yGJa5MWjFgytzgtn8c1U6RcStj3FZKhhx459U8dYbEE5FTWZ6mMRkYMiDlkFO0j4VCsQT1LT557bY0ETgtg==", "license": "MIT", "dependencies": { - "markdown-it": "^8.4.2" + "markdown-it": "^13.0.1" } }, + "node_modules/markdown-it-linkify-images/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, "node_modules/markdown-it-linkify-images/node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "license": "BSD-2-Clause" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, "node_modules/markdown-it-linkify-images/node_modules/linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", + "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", "license": "MIT", "dependencies": { "uc.micro": "^1.0.1" } }, "node_modules/markdown-it-linkify-images/node_modules/markdown-it": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", - "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.2.tgz", + "integrity": "sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==", "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "entities": "~1.1.1", - "linkify-it": "^2.0.0", + "argparse": "^2.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" }, diff --git a/website/client/package.json b/website/client/package.json index 1cff12c077..0703d588b2 100644 --- a/website/client/package.json +++ b/website/client/package.json @@ -28,7 +28,7 @@ "eslint-config-habitrpg": "6.2.0", "eslint-plugin-mocha": "5.3.0", "eslint-plugin-vue": "7.20.0", - "habitica-markdown": "^3.0.0", + "habitica-markdown": "^4.0.0", "hellojs": "^1.20.0", "intro.js": "^7.2.0", "jquery": "^3.7.1", diff --git a/website/client/src/app.vue b/website/client/src/app.vue index a49c648541..e454bf53e2 100644 --- a/website/client/src/app.vue +++ b/website/client/src/app.vue @@ -229,6 +229,11 @@ export default { } return Promise.resolve(error); } + if (error.response.status === 404 + && error.response.config.method === 'get' + && error.response.config.url.indexOf('/api/v4/groups/party') !== -1) { + return Promise.reject(error); + } } const errorData = error.response.data; diff --git a/website/client/src/assets/css/sprites.css b/website/client/src/assets/css/sprites.css index 1c21bf44f1..aa64fe9ef5 100755 --- a/website/client/src/assets/css/sprites.css +++ b/website/client/src/assets/css/sprites.css @@ -1,57 +1,3 @@ -.quest_lostMasterclasser4 { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/quest_lostMasterclasser4.gif") no-repeat; - width: 219px; - height: 219px; -} - -.quest_windup { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/quest_windup.gif") no-repeat; - width: 219px; - height: 219px; -} - -.quest_solarSystem { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/quest_solarSystem.gif") no-repeat; - width: 219px; - height: 219px; -} - -.quest_virtualpet { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/quest_virtualpet.gif") no-repeat; - width: 219px; - height: 219px; -} - -.Pet_HatchingPotion_Dessert, .Pet_HatchingPotion_Veggie, .Pet_HatchingPotion_Windup, -.Pet_HatchingPotion_VirtualPet, .Pet_HatchingPotion_Fungi, .Pet_HatchingPotion_Cryptid { - width: 68px; - height: 68px; -} - -.Pet_HatchingPotion_Dessert { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Dessert.gif") no-repeat; -} - -.Pet_HatchingPotion_Veggie { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Veggie.gif") no-repeat; -} - -.Pet_HatchingPotion_Windup { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Windup.gif") no-repeat; -} - -.Pet_HatchingPotion_VirtualPet { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_VirtualPet.gif") no-repeat; -} - -.Pet_HatchingPotion_Fungi { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Fungi.gif") no-repeat; -} - -.Pet_HatchingPotion_Cryptid { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Cryptid.gif") no-repeat; -} - .Gems { display:inline-block; margin-right:5px; @@ -80,6 +26,7 @@ margin-left: -3px; margin-top: -18px; } + .slim_armor_special_0, .broad_armor_special_0, .shield_special_0 { width: 90px; height: 90px; @@ -87,7 +34,6 @@ /* Critical */ .weapon_special_critical { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_special_critical.gif") no-repeat; width: 90px; height: 90px; margin-left:-12px; @@ -98,6 +44,7 @@ .weapon_special_1 { margin-left: -12px; } + .broad_armor_special_1, .slim_armor_special_1, .head_special_1 { width: 90px; height: 90px; @@ -106,36 +53,15 @@ .back_special_heroicAureole { width: 114px; height: 90px; - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/back_special_heroicAureole.gif") no-repeat; } -.head_special_0 { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-ShadeHelmet.gif") no-repeat; -} .head_special_1 { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/ContributorOnly-Equip-CrystalHelmet.gif") no-repeat; margin-top: 3px; } -.broad_armor_special_0,.slim_armor_special_0 { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-ShadeArmor.gif") no-repeat; -} -.broad_armor_special_1,.slim_armor_special_1 { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/ContributorOnly-Equip-CrystalArmor.gif") no-repeat; -} - -.shield_special_0 { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Shield-TormentedSkull.gif") no-repeat; -} - -.weapon_special_0 { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Weapon-DarkSoulsBlade.gif") no-repeat; -} - .Pet-Wolf-Cerberus { width: 105px; height: 72px; - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Pet-CerberusPup.gif") no-repeat; } .broad_armor_special_ks2019, .slim_armor_special_ks2019, .eyewear_special_ks2019, .head_special_ks2019, .shield_special_ks2019 { @@ -143,36 +69,17 @@ height: 120px; } -.broad_armor_special_ks2019, .slim_armor_special_ks2019 { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-MythicGryphonArmor.gif") no-repeat; -} - -.eyewear_special_ks2019 { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-MythicGryphonVisor.gif") no-repeat; -} - -.head_special_ks2019 { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-MythicGryphonHelm.gif") no-repeat; -} - -.shield_special_ks2019 { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-MythicGryphonShield.gif") no-repeat; -} - .weapon_special_ks2019 { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-MythicGryphonGlaive.gif") no-repeat; width: 120px; height: 120px; } .Pet-Gryphon-Gryphatrice { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Pet-Gryphatrice.gif") no-repeat; width: 81px; height: 99px; } .Pet-Gryphatrice-Jubilant { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Gryphatrice-Jubilant.gif") no-repeat; width: 81px; height: 96px; } @@ -182,39 +89,11 @@ height: 135px; } -.Mount_Head_Gryphon-Gryphatrice { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Mount-Head-Gryphatrice.gif") no-repeat; -} - -.Mount_Body_Gryphon-Gryphatrice { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Mount-Body-Gryphatrice.gif") no-repeat; -} - -.Mount_Head_Dragon-Hydra { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Mount_Head_Dragon-Hydra.gif") no-repeat; -} - -.Mount_Body_Dragon-Hydra { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Mount_Body_Dragon-Hydra.gif") no-repeat; -} - .background_airship, .background_clocktower, .background_steamworks { width: 141px; height: 147px; } -.background_airship { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_airship.gif") no-repeat; -} - -.background_clocktower { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_clocktower.gif") no-repeat; -} - -.background_steamworks { - background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_steamworks.gif") no-repeat; -} - [class*="Mount_Head_"], [class*="Mount_Body_"] { margin-top:18px; /* Sprite accommodates 105x123 box */ diff --git a/website/client/src/assets/css/sprites/spritesmith-main.css b/website/client/src/assets/css/sprites/spritesmith-main.css index 455fa8a366..3b9c721c49 100644 --- a/website/client/src/assets/css/sprites/spritesmith-main.css +++ b/website/client/src/assets/css/sprites/spritesmith-main.css @@ -1060,6 +1060,11 @@ width: 141px; height: 147px; } +.background_elven_citadel { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_elven_citadel.png'); + width: 141px; + height: 147px; +} .background_enchanted_music_room { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_enchanted_music_room.png'); width: 141px; @@ -1796,6 +1801,11 @@ width: 141px; height: 147px; } +.background_on_a_strange_planet { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_on_a_strange_planet.png'); + width: 141px; + height: 147px; +} .background_on_tree_branch { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_on_tree_branch.png'); width: 141px; @@ -1931,6 +1941,11 @@ width: 141px; height: 147px; } +.background_riding_a_comet { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_riding_a_comet.png'); + width: 141px; + height: 147px; +} .background_rime_ice { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_rime_ice.png'); width: 141px; @@ -2427,6 +2442,11 @@ width: 141px; height: 147px; } +.background_waterfall_with_rainbow { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_waterfall_with_rainbow.png'); + width: 141px; + height: 147px; +} .background_wedding_arch { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_wedding_arch.png'); width: 141px; @@ -29800,6 +29820,11 @@ width: 114px; height: 90px; } +.broad_armor_armoire_handstandOutfit { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_handstandOutfit.png'); + width: 114px; + height: 90px; +} .broad_armor_armoire_hattersSuit { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_hattersSuit.png'); width: 114px; @@ -30075,6 +30100,11 @@ width: 114px; height: 90px; } +.broad_armor_armoire_softYellowSuit { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_softYellowSuit.png'); + width: 114px; + height: 90px; +} .broad_armor_armoire_springPetalYukata { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_springPetalYukata.png'); width: 114px; @@ -30385,6 +30415,11 @@ width: 114px; height: 90px; } +.head_armoire_floppyYellowHat { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_floppyYellowHat.png'); + width: 114px; + height: 90px; +} .head_armoire_flutteryWig { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_flutteryWig.png'); width: 114px; @@ -30705,6 +30740,11 @@ width: 114px; height: 90px; } +.head_armoire_verdantArmingCap { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_verdantArmingCap.png'); + width: 114px; + height: 90px; +} .head_armoire_vermilionArcherHelm { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_vermilionArcherHelm.png'); width: 90px; @@ -31120,6 +31160,11 @@ width: 114px; height: 90px; } +.shield_armoire_softYellowPillow { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_softYellowPillow.png'); + width: 114px; + height: 90px; +} .shield_armoire_spanishGuitar { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_spanishGuitar.png'); width: 114px; @@ -31170,6 +31215,11 @@ width: 114px; height: 90px; } +.shield_armoire_verdantBanner { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_verdantBanner.png'); + width: 114px; + height: 90px; +} .shield_armoire_vikingShield { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_vikingShield.png'); width: 90px; @@ -31440,6 +31490,11 @@ width: 114px; height: 90px; } +.slim_armor_armoire_handstandOutfit { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_handstandOutfit.png'); + width: 114px; + height: 90px; +} .slim_armor_armoire_hattersSuit { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_hattersSuit.png'); width: 114px; @@ -31715,6 +31770,11 @@ width: 114px; height: 90px; } +.slim_armor_armoire_softYellowSuit { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_softYellowSuit.png'); + width: 114px; + height: 90px; +} .slim_armor_armoire_springPetalYukata { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_springPetalYukata.png'); width: 114px; @@ -34125,11 +34185,21 @@ width: 114px; height: 90px; } +.back_mystery_202605 { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/back_mystery_202605.png'); + width: 114px; + height: 90px; +} .broad_armor_mystery_202512 { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_mystery_202512.png'); width: 114px; height: 90px; } +.broad_armor_mystery_202604 { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_mystery_202604.png'); + width: 114px; + height: 90px; +} .head_mystery_202512 { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_mystery_202512.png'); width: 114px; @@ -34140,11 +34210,31 @@ width: 114px; height: 90px; } +.head_mystery_202603 { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_mystery_202603.png'); + width: 114px; + height: 90px; +} +.head_mystery_202604 { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_mystery_202604.png'); + width: 114px; + height: 90px; +} +.shield_mystery_202605 { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_mystery_202605.png'); + width: 114px; + height: 90px; +} .slim_armor_mystery_202512 { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_mystery_202512.png'); width: 114px; height: 90px; } +.slim_armor_mystery_202604 { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_mystery_202604.png'); + width: 114px; + height: 90px; +} .weapon_mystery_202512 { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_mystery_202512.png'); width: 114px; @@ -34155,6 +34245,11 @@ width: 114px; height: 90px; } +.weapon_mystery_202603 { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_mystery_202603.png'); + width: 114px; + height: 90px; +} .back_mystery_201402 { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/back_mystery_201402.png'); width: 90px; @@ -36275,6 +36370,26 @@ width: 114px; height: 90px; } +.broad_armor_special_spring2026Healer { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_special_spring2026Healer.png'); + width: 114px; + height: 90px; +} +.broad_armor_special_spring2026Mage { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_special_spring2026Mage.png'); + width: 114px; + height: 90px; +} +.broad_armor_special_spring2026Rogue { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_special_spring2026Rogue.png'); + width: 114px; + height: 90px; +} +.broad_armor_special_spring2026Warrior { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_special_spring2026Warrior.png'); + width: 114px; + height: 90px; +} .broad_armor_special_springHealer { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_special_springHealer.png'); width: 90px; @@ -36595,6 +36710,26 @@ width: 114px; height: 90px; } +.head_special_spring2026Healer { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_special_spring2026Healer.png'); + width: 114px; + height: 90px; +} +.head_special_spring2026Mage { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_special_spring2026Mage.png'); + width: 114px; + height: 90px; +} +.head_special_spring2026Rogue { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_special_spring2026Rogue.png'); + width: 114px; + height: 90px; +} +.head_special_spring2026Warrior { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_special_spring2026Warrior.png'); + width: 114px; + height: 90px; +} .head_special_springHealer { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_special_springHealer.png'); width: 90px; @@ -36780,6 +36915,21 @@ width: 114px; height: 90px; } +.shield_special_spring2026Healer { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_special_spring2026Healer.png'); + width: 114px; + height: 90px; +} +.shield_special_spring2026Rogue { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_special_spring2026Rogue.png'); + width: 114px; + height: 90px; +} +.shield_special_spring2026Warrior { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_special_spring2026Warrior.png'); + width: 114px; + height: 90px; +} .shield_special_springHealer { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_special_springHealer.png'); width: 90px; @@ -37015,6 +37165,26 @@ width: 114px; height: 90px; } +.slim_armor_special_spring2026Healer { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_special_spring2026Healer.png'); + width: 114px; + height: 90px; +} +.slim_armor_special_spring2026Mage { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_special_spring2026Mage.png'); + width: 114px; + height: 90px; +} +.slim_armor_special_spring2026Rogue { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_special_spring2026Rogue.png'); + width: 114px; + height: 90px; +} +.slim_armor_special_spring2026Warrior { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_special_spring2026Warrior.png'); + width: 114px; + height: 90px; +} .slim_armor_special_springHealer { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_special_springHealer.png'); width: 90px; @@ -37255,6 +37425,26 @@ width: 114px; height: 90px; } +.weapon_special_spring2026Healer { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_special_spring2026Healer.png'); + width: 114px; + height: 90px; +} +.weapon_special_spring2026Mage { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_special_spring2026Mage.png'); + width: 114px; + height: 90px; +} +.weapon_special_spring2026Rogue { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_special_spring2026Rogue.png'); + width: 114px; + height: 90px; +} +.weapon_special_spring2026Warrior { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_special_spring2026Warrior.png'); + width: 114px; + height: 90px; +} .weapon_special_springHealer { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_special_springHealer.png'); width: 90px; @@ -53038,6 +53228,11 @@ width: 81px; height: 99px; } +.Pet-BearCub-Alien { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-BearCub-Alien.png'); + width: 81px; + height: 99px; +} .Pet-BearCub-Amber { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-BearCub-Amber.png'); width: 81px; @@ -53528,6 +53723,11 @@ width: 81px; height: 99px; } +.Pet-Cactus-Alien { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Cactus-Alien.png'); + width: 81px; + height: 99px; +} .Pet-Cactus-Amber { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Cactus-Amber.png'); width: 81px; @@ -54318,6 +54518,11 @@ width: 81px; height: 99px; } +.Pet-Dragon-Alien { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Dragon-Alien.png'); + width: 81px; + height: 99px; +} .Pet-Dragon-Amber { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Dragon-Amber.png'); width: 81px; @@ -54813,6 +55018,11 @@ width: 81px; height: 99px; } +.Pet-FlyingPig-Alien { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-FlyingPig-Alien.png'); + width: 81px; + height: 99px; +} .Pet-FlyingPig-Amber { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-FlyingPig-Amber.png'); width: 81px; @@ -55148,6 +55358,11 @@ width: 81px; height: 99px; } +.Pet-Fox-Alien { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Fox-Alien.png'); + width: 81px; + height: 99px; +} .Pet-Fox-Amber { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Fox-Amber.png'); width: 81px; @@ -55928,6 +56143,11 @@ width: 81px; height: 99px; } +.Pet-LionCub-Alien { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-LionCub-Alien.png'); + width: 81px; + height: 99px; +} .Pet-LionCub-Amber { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-LionCub-Amber.png'); width: 81px; @@ -56533,6 +56753,11 @@ width: 81px; height: 99px; } +.Pet-PandaCub-Alien { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-PandaCub-Alien.png'); + width: 81px; + height: 99px; +} .Pet-PandaCub-Amber { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-PandaCub-Amber.png'); width: 81px; @@ -57928,6 +58153,11 @@ width: 81px; height: 99px; } +.Pet-TigerCub-Alien { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-TigerCub-Alien.png'); + width: 81px; + height: 99px; +} .Pet-TigerCub-Amber { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-TigerCub-Amber.png'); width: 81px; @@ -58573,6 +58803,11 @@ width: 81px; height: 99px; } +.Pet-Wolf-Alien { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Wolf-Alien.png'); + width: 81px; + height: 99px; +} .Pet-Wolf-Amber { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Wolf-Amber.png'); width: 81px; diff --git a/website/client/src/assets/images/rebirth-orb.png b/website/client/src/assets/images/rebirth-orb.png new file mode 100644 index 0000000000..daf89541bf Binary files /dev/null and b/website/client/src/assets/images/rebirth-orb.png differ diff --git a/website/client/src/assets/scss/markdown.scss b/website/client/src/assets/scss/markdown.scss index 19f9e45bc1..de59f1237e 100644 --- a/website/client/src/assets/scss/markdown.scss +++ b/website/client/src/assets/scss/markdown.scss @@ -58,6 +58,11 @@ h3.markdown { img { max-width: 100%; } + + .emoji-native { + font-size: 0.85em; + vertical-align: middle; + } blockquote { padding: 0 16px; diff --git a/website/client/src/assets/svg/purple-waves.svg b/website/client/src/assets/svg/purple-waves.svg new file mode 100644 index 0000000000..737de7058a --- /dev/null +++ b/website/client/src/assets/svg/purple-waves.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/website/client/src/components/achievements/rebirth.vue b/website/client/src/components/achievements/rebirth.vue index 174b5cd4c3..4bb514c9c9 100644 --- a/website/client/src/components/achievements/rebirth.vue +++ b/website/client/src/components/achievements/rebirth.vue @@ -1,53 +1,228 @@ - + + diff --git a/website/client/src/components/creatorIntro.vue b/website/client/src/components/creatorIntro.vue index e0e566cdd5..67d358a221 100644 --- a/website/client/src/components/creatorIntro.vue +++ b/website/client/src/components/creatorIntro.vue @@ -187,7 +187,8 @@
    + +
    + + +
    +
    +
    + + +
    +
    + + + + + + + diff --git a/website/client/src/components/groups/chat.vue b/website/client/src/components/groups/chat.vue index d1b0e7ae4d..2d242863bc 100644 --- a/website/client/src/components/groups/chat.vue +++ b/website/client/src/components/groups/chat.vue @@ -41,6 +41,14 @@ :chat="group.chat" @select="selectedAutocomplete" /> +
    @@ -90,6 +98,7 @@ import { MAX_MESSAGE_LENGTH } from '@/../../common/script/constants'; import externalLinks from '../../mixins/externalLinks'; import autocomplete from '../chat/autoComplete'; +import emojiAutoComplete from '../chat/emojiAutoComplete'; import communityGuidelines from './communityGuidelines'; import chatMessages from '../chat/chatMessages'; import { mapState } from '@/libs/store'; @@ -102,6 +111,7 @@ export default { }, components: { autocomplete, + emojiAutoComplete, communityGuidelines, chatMessages, }, diff --git a/website/client/src/components/groups/group.vue b/website/client/src/components/groups/group.vue index a70fe8b90d..044acd7c9d 100644 --- a/website/client/src/components/groups/group.vue +++ b/website/client/src/components/groups/group.vue @@ -25,53 +25,61 @@
    -
    -
    -
    - {{ group.memberCount | abbrNum }} -
    - {{ $t('memberList') }} +
    +
    +
    +
    +
    + {{ group.memberCount | abbrNum }} +
    +
    + {{ $t('memberList') }} +
    -
    - {{ group.balance * 4 }} -
    - {{ $t('guildBank') }} +
    +
    +
    + {{ group.balance * 4 }} +
    +
    + {{ $t('guildBank') }} +
    @@ -128,35 +136,57 @@ } .item-with-icon { + display: inline-block; border-radius: 2px; - background-color: #ffffff; + background-color: $white; box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12); - padding: 1em; - text-align: center; - min-width: 120px; + margin-left: 1em; + width: 120px; height: 76px; - margin-right: 1rem; + text-align: center; + font-size: 20px; + vertical-align: bottom; + overflow: hidden; + position: relative; - &:last-of-type { - margin-left: 0.5rem; + .box-content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + width: 100%; } - .svg-icon.shield, .svg-icon.gem { - width: 28px; - height: auto; - margin: 0 auto; + .icon-number-row { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 0.1em; + + .number { + font-size: 18px; + font-weight: normal; + margin-left: 0.2em; + } + } + + .svg-icon { + width: 24px; + height: 24px; display: inline-block; vertical-align: bottom; - margin-right: 0.5em; } - .number { - font-size: 22px; - font-weight: bold; - } - - .label { - margin-top: .5em; + .details { + font-size: 11px; + color: $gray-200; + width: 100%; + padding: 0 4px; + line-height: 1.1; + word-break: break-word; + max-height: 2.2em; + overflow: visible; } } @@ -215,11 +245,6 @@ .icon-row { margin-top: 1em; justify-content: flex-end; - - .number { - font-size: 22px; - font-weight: bold; - } } .chat-row { diff --git a/website/client/src/components/inventory/stable/petItem.vue b/website/client/src/components/inventory/stable/petItem.vue index 3a10977ebd..9e52c9eda6 100644 --- a/website/client/src/components/inventory/stable/petItem.vue +++ b/website/client/src/components/inventory/stable/petItem.vue @@ -182,12 +182,10 @@ export default { return 'GreyedOut'; }, imageName () { - const foolEvent = this.currentEventList?.find(event => moment() - .isBetween(event.start, event.end) && event.aprilFools); - if (this.isOwned() && foolEvent) { - if (this.isSpecial()) return `stable_${this.foolPet(this.item.key, foolEvent.aprilFools)}`; - const petString = `${this.item.eggKey}-${this.item.key}`; - return `stable_${this.foolPet(petString, foolEvent.aprilFools)}`; + const substitutionEvent = this.currentEventList?.find(event => moment() + .isBetween(event.start, event.end) && event.spriteSubstitutions); + if (this.isOwned() && substitutionEvent && substitutionEvent.spriteSubstitutions.pets) { + return `stable_${this.foolPet(`Pet-${this.item.key}`, substitutionEvent.spriteSubstitutions.pets)}`; } if (this.isOwned() || (this.mountOwned() && this.isHatchable())) { diff --git a/website/client/src/components/news/modal.vue b/website/client/src/components/news/modal.vue index 8301238dea..2bd06331d3 100644 --- a/website/client/src/components/news/modal.vue +++ b/website/client/src/components/news/modal.vue @@ -10,6 +10,9 @@ >
    { + this.textbox.setSelectionRange(newCaret, newCaret); + this.textbox.focus(); + }); }, async addTag (name) { const tagResult = await this.createTag({ name }); diff --git a/website/client/src/components/tasks/user.vue b/website/client/src/components/tasks/user.vue index 4de6ff3963..3af37856de 100644 --- a/website/client/src/components/tasks/user.vue +++ b/website/client/src/components/tasks/user.vue @@ -80,9 +80,17 @@ v-html="icons.drag" >
    @@ -134,6 +149,15 @@
    +