diff --git a/package-lock.json b/package-lock.json index df98b51368..1312c3946c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3605,20 +3605,22 @@ "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" }, "body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", "requires": { "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "dependencies": { "debug": { @@ -3629,6 +3631,28 @@ "ms": "2.0.0" } }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3642,10 +3666,26 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, "qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" } } }, @@ -10386,9 +10426,9 @@ "integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is=" }, "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + "version": "2.29.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz", + "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==" }, "moment-recur": { "version": "1.0.7", @@ -12100,16 +12140,33 @@ "integrity": "sha512-8DVFOe89rreyut/vzwBI7vgXJynyYoYnH5XogtAKs0F/neAbCTTglXxSJ7fZeZamcFXZDvMidCBvps4KM+1srw==" }, "raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "requires": { "bytes": "3.1.2", - "http-errors": "1.8.1", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -12117,6 +12174,11 @@ "requires": { "safer-buffer": ">= 2.1.2 < 3" } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" } } }, @@ -13746,9 +13808,9 @@ "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==" }, "superagent": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.1.1.tgz", - "integrity": "sha512-CQ2weSS6M+doIwwYFoMatklhRbx6sVNdB99OEJ5czcP3cng76Ljqus694knFWgOj3RkrtxZqIgpe6vhe0J7QWQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.1.2.tgz", + "integrity": "sha512-o9/fP6dww7a4xmEF5a484o2rG34UUGo8ztDlv7vbCWuqPhpndMi0f7eXxdlryk5U12Kzy46nh8eNpLAJ93Alsg==", "requires": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.3", @@ -13764,9 +13826,9 @@ }, "dependencies": { "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } diff --git a/package.json b/package.json index 97eb8ee1cb..1c11c7dfec 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "apidoc": "^0.51.0", "apple-auth": "^1.0.7", "bcrypt": "^5.0.1", - "body-parser": "^1.19.2", + "body-parser": "^1.20.0", "bootstrap": "^4.6.0", "compression": "^1.7.4", "cookie-session": "^2.0.0", @@ -47,7 +47,7 @@ "lodash": "^4.17.21", "merge-stream": "^2.0.0", "method-override": "^3.0.0", - "moment": "^2.29.1", + "moment": "^2.29.2", "moment-recur": "^1.0.7", "mongoose": "^5.13.7", "morgan": "^1.10.0", @@ -68,7 +68,7 @@ "rimraf": "^3.0.2", "short-uuid": "^4.2.0", "stripe": "^8.212.0", - "superagent": "^7.1.1", + "superagent": "^7.1.2", "universal-analytics": "^0.5.3", "useragent": "^2.1.9", "uuid": "^8.3.2", diff --git a/test/api/unit/middlewares/cronMiddleware.js b/test/api/unit/middlewares/cronMiddleware.js index 873bbb70a7..fea4384c26 100644 --- a/test/api/unit/middlewares/cronMiddleware.js +++ b/test/api/unit/middlewares/cronMiddleware.js @@ -128,6 +128,22 @@ describe('cron middleware', () => { }); }); + it('runs cron if previous cron was incomplete', async () => { + user.lastCron = moment(new Date()).subtract({ days: 1 }); + user.auth.timestamps.loggedin = moment(new Date()).subtract({ days: 4 }); + const now = new Date(); + await user.save(); + + await new Promise((resolve, reject) => { + cronMiddleware(req, res, err => { + if (err) return reject(err); + expect(moment(now).isSame(user.lastCron, 'day')); + expect(moment(now).isSame(user.auth.timestamps.loggedin, 'day')); + return resolve(); + }); + }); + }); + it('updates user.auth.timestamps.loggedin and lastCron', async () => { user.lastCron = moment(new Date()).subtract({ days: 2 }); const now = new Date(); @@ -293,4 +309,33 @@ describe('cron middleware', () => { }); }); }); + + it('cron should not run more than once', async () => { + user.lastCron = moment(new Date()).subtract({ days: 2 }); + await user.save(); + + sandbox.spy(cronLib, 'cron'); + + await Promise.all([new Promise((resolve, reject) => { + cronMiddleware(req, res, err => { + if (err) return reject(err); + return resolve(); + }); + }), new Promise((resolve, reject) => { + cronMiddleware(req, res, err => { + if (err) return reject(err); + return resolve(); + }); + }), new Promise((resolve, reject) => { + setTimeout(() => { + cronMiddleware(req, res, err => { + if (err) return reject(err); + return resolve(); + }); + }, 400); + }), + ]); + + expect(cronLib.cron).to.be.calledOnce; + }); }); diff --git a/test/api/unit/models/user.test.js b/test/api/unit/models/user.test.js index 27c61485d9..d559ba0025 100644 --- a/test/api/unit/models/user.test.js +++ b/test/api/unit/models/user.test.js @@ -811,6 +811,16 @@ describe('User Model', () => { expect(daysMissed).to.eql(5); }); + it('correctly handles a cron that did not complete', () => { + const now = moment(); + user.lastCron = moment(now).subtract(2, 'days'); + user.auth.timestamps.loggedIn = moment(now).subtract(5, 'days'); + + const { daysMissed } = user.daysUserHasMissed(now); + + expect(daysMissed).to.eql(5); + }); + it('uses timezone from preferences to calculate days missed', () => { const now = moment('2017-07-08 01:00:00Z'); user.lastCron = moment('2017-07-04 13:00:00Z'); diff --git a/website/client/src/assets/css/sprites/spritesmith-main.css b/website/client/src/assets/css/sprites/spritesmith-main.css index 8c6f3565ab..4d79e34852 100644 --- a/website/client/src/assets/css/sprites/spritesmith-main.css +++ b/website/client/src/assets/css/sprites/spritesmith-main.css @@ -633,6 +633,11 @@ width: 141px; height: 147px; } +.background_blossoming_trees { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_blossoming_trees.png'); + width: 141px; + height: 147px; +} .background_blue { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_blue.png'); width: 141px; @@ -903,6 +908,16 @@ width: 60px; height: 60px; } +.background_flower_shop { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_flower_shop.png'); + width: 141px; + height: 147px; +} +.customize-option.background_flower_shop { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_flower_shop.png'); + width: 60px; + height: 60px; +} .background_flowering_prairie { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_flowering_prairie.png'); width: 141px; @@ -1633,6 +1648,11 @@ width: 141px; height: 147px; } +.background_springtime_lake { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_springtime_lake.png'); + width: 141px; + height: 147px; +} .background_stable { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_stable.png'); width: 141px; @@ -2103,6 +2123,11 @@ width: 68px; height: 68px; } +.icon_background_blossoming_trees { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_blossoming_trees.png'); + width: 68px; + height: 68px; +} .icon_background_blue { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_blue.png'); width: 68px; @@ -2378,11 +2403,21 @@ width: 60px; height: 60px; } -.icon_background_flowering_prairie { - background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_flowering_prairie.png'); +.icon_background_flower_shop { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_flower_shop.png'); + width: 68px; + height: 68px; +} +.customize-option.icon_background_flower_shop { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_flower_shop.png'); width: 60px; height: 60px; } +.icon_background_flowering_prairie { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_flowering_prairie.png'); + width: 68px; + height: 68px; +} .customize-option.icon_background_flowering_prairie { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_flowering_prairie.png'); width: 60px; @@ -3108,6 +3143,11 @@ width: 68px; height: 68px; } +.icon_background_springtime_lake { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_springtime_lake.png'); + width: 68px; + height: 68px; +} .icon_background_stable { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_stable.png'); width: 68px; @@ -16963,6 +17003,11 @@ width: 114px; height: 90px; } +.broad_armor_armoire_strawRaincoat { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_strawRaincoat.png'); + width: 114px; + height: 90px; +} .broad_armor_armoire_stripedSwimsuit { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_stripedSwimsuit.png'); width: 90px; @@ -17383,6 +17428,11 @@ width: 117px; height: 120px; } +.head_armoire_strawRainHat { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_strawRainHat.png'); + width: 114px; + height: 90px; +} .head_armoire_swanFeatherCrown { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_swanFeatherCrown.png'); width: 90px; @@ -18123,6 +18173,11 @@ width: 68px; height: 68px; } +.shop_armor_armoire_strawRaincoat { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_armor_armoire_strawRaincoat.png'); + width: 68px; + height: 68px; +} .shop_armor_armoire_stripedSwimsuit { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_armor_armoire_stripedSwimsuit.png'); width: 68px; @@ -18558,6 +18613,11 @@ width: 68px; height: 68px; } +.shop_head_armoire_strawRainHat { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_head_armoire_strawRainHat.png'); + width: 68px; + height: 68px; +} .shop_head_armoire_swanFeatherCrown { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_head_armoire_swanFeatherCrown.png'); width: 68px; @@ -19708,6 +19768,11 @@ width: 114px; height: 90px; } +.slim_armor_armoire_strawRaincoat { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_strawRaincoat.png'); + width: 114px; + height: 90px; +} .slim_armor_armoire_stripedSwimsuit { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_stripedSwimsuit.png'); width: 90px; diff --git a/website/client/src/components/appFooter.vue b/website/client/src/components/appFooter.vue index 61ba4d08e4..8157e2abd8 100644 --- a/website/client/src/components/appFooter.vue +++ b/website/client/src/components/appFooter.vue @@ -84,8 +84,8 @@
  • {{ $t('reportBug') }} @@ -224,7 +224,7 @@
    - © 2022 Habitica. All rights reserved. + © {{ currentYear }} Habitica. All rights reserved.
    {{ $t('email') }} + >{{ $t('emailOrUsername') }}
    diff --git a/website/client/src/components/bugReportModal.vue b/website/client/src/components/bugReportModal.vue index 91fda2a859..92d096264d 100644 --- a/website/client/src/components/bugReportModal.vue +++ b/website/client/src/components/bugReportModal.vue @@ -12,12 +12,18 @@ {{ $t('reportBug') }} -
    +
    {{ $t('reportBugHeaderDescribe') }}
    - +
    @@ -34,7 +40,10 @@ > {{ $t('email') }} -
    +
    {{ $t('reportEmailText') }}
    -
    +
    {{ $t('reportEmailError') }}
    @@ -55,7 +67,10 @@ -
    +
    {{ $t('reportDescriptionText') }}