Compare commits

...

257 Commits

Author SHA1 Message Date
Phillip Thelen
192d649ffa fix giving gear to contributors 2025-01-23 17:30:31 +01:00
Phillip Thelen
e7aae55eca Give contributors gear immediately 2025-01-21 11:10:29 +01:00
Phillip Thelen
36b03613e1 adjust contributor tests 2025-01-21 11:10:16 +01:00
Phillip Thelen
2de9a16a2c adjust gems per tier 2025-01-21 11:02:19 +01:00
negue
895241b7fa show date tooltip above system / skill messages 2025-01-20 21:08:55 +01:00
negue
2535fd7095 Combined Message Pages/Redesign (#15310)
* split component prepare new views / states

* extract empty and disabled state as components

* fix empty state mail icon

* first logic switching between modes, move page to /private-messages/index.vue

* extract autoCompleteHelper.js

* style header + start new message input

* style plus button + focus input

* state logic, types for sanity

* WIP PM new Message started

* add /members/username test

* first design changes to messageCard

* delete private message or chat - based on the mode

* copy as todo

* mention links to modal

* report chat or private message

* WIP likeButton

* likeButton styling

* hide like on private message cards

* fix unit test

* replace copy as todo - to just a copy to clipboard

* style changes

* menu position + like button width

* dropdown items background + like font

* fix like button padding

* move api endpoints and tests around to group inbox methods  + like for inbox private messages

* restyle system messages

* Dropdown Radius and Padding

* WIP system messages

* fix lint

* copy delta commit of allowing liking own private messages

* enable liking private messages

* fix menu non hovered item icon color

* fix import path

* ignore background on system messages

* requested changes + migration

* update migration to update the unique id to some messages and delete the duplicates

* migration based on users pagination

* fix(migration): use Promise.all

* change to bulkWrites per User, and all messages in one run (of a user)

* check for array

* use rest operator ...

* skip sorting to get the users

* remove migration, disable like for private messages without uniqueMessageId

* lean+bulkWrite for likes, add time checks for like and auth for further debugging

* add a limit 2 get the messages by uniqueId

* Adding a simple server start script

* remove pinned nodemon dep

* fix inbox controller/tests

* fix / requested style changes

* fix empty state padding /

* hide avatar weapons on messages - fix avatar spacing on messages

* Hourglass Simplification (#15323)

* begin removing obsolete tests

* begin refactoring

* update cron tests

* cleanup

* finish basic implementation of new logic

* add more subscription tests

* subscription test improvements

* return nextHourglassDate again

* fix gem limit

* fix(test): short circuit this.

* fix(admin): correct logic and style for shrimple subs

* WIP(frontend): draft of main subs page view

* fix hourglass count

* Fix hourglass logic for upgrades

* fix admin panel display

* WIP(subs): extant Stripe state

* fix admin panel strings

* fix missing transaction type

* add new field for cumulative subscription count

* show date for hourglass bonus if it was received

* fix test

* feat(subscription): max Gems progress readout

* fix(css): correct and refactor heights and selection states

* fix(subs): correct border-radius and redirect

* fix(stripe): correct redirect after success

* Admin panel display fixes

* don’t give additional HG for new sub if they already got one this month

* fix issue with promo hourglasses

* fix(subscription): update layout when gifting

* fix(subscriptions): more gift layout revisions

* fix(subscriptions): minor visual updates

* fix(subs): pass autoRenews through Stripe

* fix(subs): gifts DON't renew

* fix(lint): unnecessary ternary

* fix(lint): do negate object ig

* fix(subs): try again on gifts

* fix(subs): unhovery and un-12-monthy

* fix bug with incorrectly giving HG bonus

* remove only

* fix test

* fix test

* fix(subs): also redirect to subs after gift sub

* fix(subs): fix typeError

* fix(g1g1): don't try to find Gems promo during bogo

---------

Co-authored-by: Phillip Thelen <phillip@habitica.com>
Co-authored-by: Kalista Payne <sabe@habitica.com>

* chore(sprites): update subproject

* fix(layout): tighten cancellation note

* fix(subs): Google wording and HG escape

* chore(testing): fake g1g1 dates

* fix(subs): don't hide HG preview entirely

* fix(subs): center next hourglass message

* working validatedTextInput.vue within start-new-conversation-input-header.vue 🎉

* fix(git): remove changes from old develop

* Revert "fix(git): remove changes from old develop"

This reverts commit 0e30f7df00.

* fix(git): no actually just this file i guesss

* adding an empty loading state, hiding

* fought the avatar arch nemesis again

* fix chatMessages (party chat) message spacing

* move disabled text back to above the input area - re-enable input area

* show disabled private messages top panel

* fix font color

* fixing uiStates - removing disabled - moving the own user check to the last

* fix(lint): add missing prop defaults

* fix(lint): object default should be fn

* fix(chat): correct grammar in error

---------

Co-authored-by: SabreCat <sabe@habitica.com>
Co-authored-by: Kalista Payne <sabrecat@gmail.com>
Co-authored-by: Phillip Thelen <phillip@habitica.com>
2025-01-16 16:52:24 -06:00
Kalista Payne
30f1820a49 5.32.5 2025-01-09 09:02:14 -06:00
Weblate
3bb6c391af Translated using Weblate (German)
Currently translated at 98.0% (3190 of 3255 strings)

Translated using Weblate (German)

Currently translated at 97.8% (3185 of 3255 strings)

Translated using Weblate (German)

Currently translated at 97.7% (3181 of 3255 strings)

Translated using Weblate (German)

Currently translated at 100.0% (261 of 261 strings)

Translated using Weblate (German)

Currently translated at 97.5% (3176 of 3255 strings)

Co-authored-by: Katharina <katharinaanna.wilding@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translation: Habitica/Gear
Translation: Habitica/Subscriber
2025-01-09 14:46:54 +01:00
Kalista Payne
a0383c785a Squashed commit of the following:
commit 5d3713008dc3041f63b23b22196e1ed79fab45bd
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Wed Jan 8 14:39:27 2025 -0600

    fix(text): pet Pet

commit 3ff5d7afeb517ae3d0933d8e18045b24575bb90b
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Wed Jan 8 14:34:02 2025 -0600

    fix(links): remove unnecessary style and icon

commit 5023dd9258aa0f9416daa212ed1249db2e5d5fe4
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Wed Jan 8 09:43:17 2025 -0600

    fix(links): update task modal markdown help

commit 81ebb279cd881d1c560668c3cb03600d85b9f5b9
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Wed Jan 8 09:26:11 2025 -0600

    fix(misc): clean up some layout and one more link

commit 2fd216f01531052aa769738f8fe1956ca6943822
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Tue Jan 7 16:09:28 2025 -0600

    fix(links): remove/revise some outdated wiki links
2025-01-08 15:18:06 -06:00
Kalista Payne
99790c05f4 5.32.4 2025-01-06 16:22:26 -06:00
Weblate
fc5fec9bfe Merge branch 'origin/develop' into Weblate. 2025-01-06 23:18:24 +01:00
Phillip Thelen
9db5d4116d Fix availability of december background (#15378) 2025-01-06 15:57:57 -06:00
Weblate
6676e94ef6 Translated using Weblate (Spanish (Latin America))
Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 95.7% (134 of 140 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Italian)

Currently translated at 9.4% (23 of 243 strings)

Translated using Weblate (Italian)

Currently translated at 99.3% (887 of 893 strings)

Translated using Weblate (Italian)

Currently translated at 99.3% (887 of 893 strings)

Translated using Weblate (Russian)

Currently translated at 91.4% (2978 of 3255 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.2% (177 of 182 strings)

Translated using Weblate (French)

Currently translated at 100.0% (261 of 261 strings)

Translated using Weblate (French)

Currently translated at 99.2% (259 of 261 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (261 of 261 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3255 of 3255 strings)

Co-authored-by: Chase7-Diaphragm0-Jeeringly7-Smartly2-Drainer5 <linguists-commonwealth@silkylegs.aleeas.com>
Co-authored-by: Gabrielle Renoir <cococherierenoir@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Kesabirria <kebsebastian@gmail.com>
Co-authored-by: Oscar Rodríguez Díaz <alexoscarcrd@gmail.com>
Co-authored-by: Raquel Pantojo de Souza Bachour <raquel.pantojo@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Val <3qes0hnzh@mozmail.com>
Co-authored-by: Vitaliia Reinberg <vitalia.reynberg@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/es/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/es_419/
Translation: Habitica/Backgrounds
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2025-01-06 22:52:42 +01:00
Kalista Payne
723adceb25 chore(migrations): cute default and updateOne 2025-01-06 15:43:07 -06:00
Kalista Payne
440d06da4a 5.32.3 2025-01-03 12:51:57 -06:00
Weblate
0ea84668a8 Merge branch 'origin/develop' into Weblate. 2025-01-03 19:49:25 +01:00
Kalista Payne
5893d8b9bb fix(subs): revise benefit strings for clarity 2025-01-03 12:22:51 -06:00
Weblate
2c799b9c07 Translated using Weblate (Spanish)
Currently translated at 100.0% (3255 of 3255 strings)

Translated using Weblate (German)

Currently translated at 97.1% (3161 of 3255 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (893 of 893 strings)

Co-authored-by: Anna Tunger <anna.tunger@icloud.com>
Co-authored-by: Diego Alejandro Rios Vasquez <diegovasquezcolombia@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translation: Habitica/Backgrounds
Translation: Habitica/Gear
2025-01-03 04:41:47 +01:00
Kalista Payne
1550d9b4ee 5.32.2 2025-01-02 11:22:51 -06:00
Weblate
ade812b86d Merge branch 'origin/develop' into Weblate. 2025-01-02 18:19:49 +01:00
Phillip Thelen
62e6fbef61 Fix content end date if already in new year (#15376)
* Fix content end date if already in new year

* fix test
2025-01-02 11:15:04 -06:00
Weblate
67a0f8b65a Translated using Weblate (Korean)
Currently translated at 70.4% (629 of 893 strings)

Translated using Weblate (Korean)

Currently translated at 91.6% (153 of 167 strings)

Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: widesky8 <widesky20@naver.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ko/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
2025-01-01 03:17:52 +01:00
Kalista Payne
aa432022d3 fix(script): replace deprecated update function 2024-12-30 14:58:52 -06:00
Kalista Payne
86fb3c1fd1 5.32.1 2024-12-30 09:23:08 -06:00
Weblate
ff2b4add8b Translated using Weblate (German)
Currently translated at 96.1% (3130 of 3255 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Korean)

Currently translated at 96.4% (110 of 114 strings)

Translated using Weblate (Korean)

Currently translated at 49.4% (90 of 182 strings)

Translated using Weblate (Korean)

Currently translated at 91.0% (152 of 167 strings)

Translated using Weblate (Korean)

Currently translated at 54.8% (1784 of 3255 strings)

Translated using Weblate (Korean)

Currently translated at 65.3% (170 of 260 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (German)

Currently translated at 95.8% (3119 of 3255 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Slovak)

Currently translated at 71.0% (577 of 812 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (394 of 394 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 79.3% (207 of 261 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 92.1% (398 of 432 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 4.9% (12 of 243 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 94.3% (182 of 193 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 95.4% (105 of 110 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 86.5% (773 of 893 strings)

Translated using Weblate (Russian)

Currently translated at 36.6% (89 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 36.6% (89 of 243 strings)

Translated using Weblate (German)

Currently translated at 95.6% (3115 of 3255 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 77.7% (189 of 243 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (893 of 893 strings)

Translated using Weblate (German)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (German)

Currently translated at 95.1% (3096 of 3255 strings)

Translated using Weblate (French)

Currently translated at 100.0% (261 of 261 strings)

Translated using Weblate (German)

Currently translated at 100.0% (261 of 261 strings)

Translated using Weblate (French)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (German)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3255 of 3255 strings)

Translated using Weblate (German)

Currently translated at 93.8% (3056 of 3255 strings)

Translated using Weblate (French)

Currently translated at 100.0% (893 of 893 strings)

Translated using Weblate (German)

Currently translated at 100.0% (893 of 893 strings)

Co-authored-by: Boni hahaha <chanrouber@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Hikari <sss840127@gmail.com>
Co-authored-by: Irina  Shcherbinina <cat3dcat007@gmail.com>
Co-authored-by: Katharina <katharinaanna.wilding@gmail.com>
Co-authored-by: Lancelot Liu <me@lancy.dev>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Wyatt <1697570085@qq.com>
Co-authored-by: razil <boss.razmarin@gmail.com>
Co-authored-by: 리슈레이 <rishyurei@gmail.com>
Co-authored-by: 횬 <gkfpxldk0424@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/character/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/de/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/zh_Hant/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Settings
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2024-12-30 16:06:20 +01:00
Kalista Payne
4ba73dfbec fix(notifications): don't error on same page 2024-12-19 15:33:23 -06:00
dependabot[bot]
e675ea9bd1 Bump send and express in /website/client (#15324)
Bumps [send](https://github.com/pillarjs/send) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `send` from 0.18.0 to 0.19.0
- [Release notes](https://github.com/pillarjs/send/releases)
- [Changelog](https://github.com/pillarjs/send/blob/master/HISTORY.md)
- [Commits](https://github.com/pillarjs/send/compare/0.18.0...0.19.0)

Updates `express` from 4.19.2 to 4.21.0
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.0/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.21.0)

---
updated-dependencies:
- dependency-name: send
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Natalie <78037386+CuriousMagpie@users.noreply.github.com>
2024-12-19 14:19:16 -05:00
dependabot[bot]
9c27d86ced Bump body-parser and express (#15319)
Bumps [body-parser](https://github.com/expressjs/body-parser) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `body-parser` from 1.20.2 to 1.20.3
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/1.20.2...1.20.3)

Updates `express` from 4.19.2 to 4.20.0
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.20.0)

---
updated-dependencies:
- dependency-name: body-parser
  dependency-type: direct:production
- dependency-name: express
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Natalie <78037386+CuriousMagpie@users.noreply.github.com>
2024-12-19 14:19:04 -05:00
dependabot[bot]
58ee81adfc Bump serve-static and express (#15318)
Bumps [serve-static](https://github.com/expressjs/serve-static) to 1.16.0 and updates ancestor dependency [express](https://github.com/expressjs/express). These dependencies need to be updated together.


Updates `serve-static` from 1.15.0 to 1.16.0
- [Release notes](https://github.com/expressjs/serve-static/releases)
- [Changelog](https://github.com/expressjs/serve-static/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/serve-static/compare/v1.15.0...1.16.0)

Updates `express` from 4.19.2 to 4.20.0
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.20.0)

---
updated-dependencies:
- dependency-name: serve-static
  dependency-type: indirect
- dependency-name: express
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Natalie <78037386+CuriousMagpie@users.noreply.github.com>
2024-12-19 14:18:51 -05:00
dependabot[bot]
32c9904a6e Bump cookie and express (#15338)
Bumps [cookie](https://github.com/jshttp/cookie) to 0.7.1 and updates ancestor dependency [express](https://github.com/expressjs/express). These dependencies need to be updated together.


Updates `cookie` from 0.6.0 to 0.7.1
- [Release notes](https://github.com/jshttp/cookie/releases)
- [Commits](https://github.com/jshttp/cookie/compare/v0.6.0...v0.7.1)

Updates `express` from 4.19.2 to 4.21.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.21.1)

---
updated-dependencies:
- dependency-name: cookie
  dependency-type: indirect
- dependency-name: express
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-19 13:43:21 -05:00
dependabot[bot]
b86e0a1549 Bump send and express (#15339)
Bumps [send](https://github.com/pillarjs/send) to 0.19.0 and updates ancestor dependency [express](https://github.com/expressjs/express). These dependencies need to be updated together.


Updates `send` from 0.18.0 to 0.19.0
- [Release notes](https://github.com/pillarjs/send/releases)
- [Changelog](https://github.com/pillarjs/send/blob/master/HISTORY.md)
- [Commits](https://github.com/pillarjs/send/compare/0.18.0...0.19.0)

Updates `express` from 4.19.2 to 4.21.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.21.1)

---
updated-dependencies:
- dependency-name: send
  dependency-type: indirect
- dependency-name: express
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-19 13:42:48 -05:00
dependabot[bot]
154ac9bb38 chore(deps): bump mongoose from 7.6.8 to 7.8.3 (#15374)
Bumps [mongoose](https://github.com/Automattic/mongoose) from 7.6.8 to 7.8.3.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/7.8.3/CHANGELOG.md)
- [Commits](https://github.com/Automattic/mongoose/compare/7.6.8...7.8.3)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-19 13:28:20 -05:00
dependabot[bot]
a97060445a chore(deps): bump cookie and express in /website/client (#15375)
Bumps [cookie](https://github.com/jshttp/cookie) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `cookie` from 0.6.0 to 0.7.1
- [Release notes](https://github.com/jshttp/cookie/releases)
- [Commits](https://github.com/jshttp/cookie/compare/v0.6.0...v0.7.1)

Updates `express` from 4.19.2 to 4.21.2
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.2/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.21.2)

---
updated-dependencies:
- dependency-name: cookie
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-19 13:28:01 -05:00
Kalista Payne
26b59de1de feat(migration): revised NYE script 2024-12-18 18:01:16 -06:00
Kalista Payne
21c8b00ef6 chore(subproj): update habitica-images 2024-12-18 17:33:34 -06:00
Kalista Payne
c25b7293bb 5.32.0 2024-12-17 15:00:55 -06:00
Weblate
15e078cb34 Translated using Weblate (German)
Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Polish)

Currently translated at 22.2% (54 of 243 strings)

Translated using Weblate (Polish)

Currently translated at 72.6% (189 of 260 strings)

Translated using Weblate (Bulgarian)

Currently translated at 64.0% (107 of 167 strings)

Co-authored-by: Jan Hesko-Kołodziński <janheski@gmail.com>
Co-authored-by: Matthieu <matthieu-ds@hotmail.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Дмитрий <dimaprohor570@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/de/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/pl/
Translation: Habitica/Achievements
Translation: Habitica/Faq
Translation: Habitica/Limited
Translation: Habitica/Settings
2024-12-17 18:09:20 +01:00
Natalie
f7bb17202b January 2025 Content Build (#15371)
* chore: add spritesheet

* chore: update spritesheet

* chore: add January subscriber set

* chore: add January subscriber items

* chore: add January background and enchanted armoire gear

* chore: fixing dumb typos

* chore: fix another typo

* chore: and another dumb typo

* chore: fix release date for armoire items

* fix(grammar): articles

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2024-12-16 11:54:09 -06:00
Kalista Payne
213b7696c5 5.31.2 2024-12-16 11:25:58 -06:00
Weblate
fe5c95316b Translated using Weblate (Turkish)
Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (German)

Currently translated at 94.0% (3054 of 3247 strings)

Translated using Weblate (German)

Currently translated at 93.6% (3041 of 3247 strings)

Translated using Weblate (German)

Currently translated at 93.6% (3041 of 3247 strings)

Translated using Weblate (German)

Currently translated at 93.4% (3034 of 3247 strings)

Translated using Weblate (German)

Currently translated at 93.3% (3032 of 3247 strings)

Translated using Weblate (German)

Currently translated at 100.0% (812 of 812 strings)

Translated using Weblate (German)

Currently translated at 93.3% (3030 of 3247 strings)

Translated using Weblate (German)

Currently translated at 98.8% (803 of 812 strings)

Translated using Weblate (German)

Currently translated at 93.2% (3028 of 3247 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (394 of 394 strings)

Translated using Weblate (German)

Currently translated at 93.1% (3026 of 3247 strings)

Translated using Weblate (German)

Currently translated at 93.1% (3024 of 3247 strings)

Translated using Weblate (German)

Currently translated at 92.9% (3019 of 3247 strings)

Translated using Weblate (German)

Currently translated at 92.9% (3019 of 3247 strings)

Translated using Weblate (German)

Currently translated at 97.7% (794 of 812 strings)

Translated using Weblate (Portuguese)

Currently translated at 98.2% (387 of 394 strings)

Translated using Weblate (Portuguese)

Currently translated at 68.8% (179 of 260 strings)

Translated using Weblate (German)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (German)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (German)

Currently translated at 97.2% (790 of 812 strings)

Translated using Weblate (Polish)

Currently translated at 58.2% (53 of 91 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (German)

Currently translated at 99.2% (258 of 260 strings)

Translated using Weblate (Turkish)

Currently translated at 59.8% (533 of 890 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.7% (3077 of 3247 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (German)

Currently translated at 99.1% (113 of 114 strings)

Translated using Weblate (German)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (German)

Currently translated at 92.6% (3009 of 3247 strings)

Translated using Weblate (German)

Currently translated at 98.8% (257 of 260 strings)

Translated using Weblate (German)

Currently translated at 98.6% (426 of 432 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3247 of 3247 strings)

Translated using Weblate (German)

Currently translated at 92.4% (3003 of 3247 strings)

Translated using Weblate (Hebrew)

Currently translated at 75.8% (182 of 240 strings)

Translated using Weblate (Dutch)

Currently translated at 66.1% (172 of 260 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (German)

Currently translated at 98.4% (256 of 260 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Spanish)

Currently translated at 99.7% (3239 of 3247 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (394 of 394 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (890 of 890 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Spanish)

Currently translated at 97.6% (254 of 260 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3247 of 3247 strings)

Translated using Weblate (Turkish)

Currently translated at 96.2% (231 of 240 strings)

Translated using Weblate (German)

Currently translated at 92.2% (2996 of 3247 strings)

Translated using Weblate (Dutch)

Currently translated at 22.2% (54 of 243 strings)

Translated using Weblate (Dutch)

Currently translated at 53.8% (49 of 91 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (394 of 394 strings)

Translated using Weblate (Spanish)

Currently translated at 89.6% (233 of 260 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Spanish)

Currently translated at 99.6% (259 of 260 strings)

Translated using Weblate (Spanish)

Currently translated at 88.0% (229 of 260 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (Russian)

Currently translated at 35.8% (87 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 35.8% (87 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 99.7% (393 of 394 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.4% (188 of 193 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (890 of 890 strings)

Translated using Weblate (Hebrew)

Currently translated at 74.5% (179 of 240 strings)

Translated using Weblate (German)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (German)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (German)

Currently translated at 98.2% (112 of 114 strings)

Translated using Weblate (German)

Currently translated at 92.0% (2989 of 3247 strings)

Translated using Weblate (German)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (German)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Hebrew)

Currently translated at 75.4% (181 of 240 strings)

Translated using Weblate (French)

Currently translated at 99.9% (3246 of 3247 strings)

Translated using Weblate (French)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (French)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (French)

Currently translated at 99.3% (3225 of 3247 strings)

Translated using Weblate (French)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (French)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (French)

Currently translated at 100.0% (394 of 394 strings)

Translated using Weblate (Turkish)

Currently translated at 53.0% (138 of 260 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Turkish)

Currently translated at 68.5% (296 of 432 strings)

Translated using Weblate (Turkish)

Currently translated at 52.9% (1720 of 3247 strings)

Translated using Weblate (Turkish)

Currently translated at 83.3% (200 of 240 strings)

Translated using Weblate (Turkish)

Currently translated at 69.3% (563 of 812 strings)

Translated using Weblate (Turkish)

Currently translated at 65.9% (60 of 91 strings)

Translated using Weblate (Turkish)

Currently translated at 59.5% (530 of 890 strings)

Translated using Weblate (Turkish)

Currently translated at 79.2% (206 of 260 strings)

Translated using Weblate (German)

Currently translated at 91.7% (2980 of 3247 strings)

Translated using Weblate (Russian)

Currently translated at 29.2% (71 of 243 strings)

Translated using Weblate (German)

Currently translated at 100.0% (394 of 394 strings)

Translated using Weblate (Russian)

Currently translated at 99.4% (885 of 890 strings)

Translated using Weblate (German)

Currently translated at 100.0% (890 of 890 strings)

Translated using Weblate (German)

Currently translated at 91.7% (2979 of 3247 strings)

Translated using Weblate (German)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (German)

Currently translated at 100.0% (890 of 890 strings)

Translated using Weblate (German)

Currently translated at 100.0% (890 of 890 strings)

Translated using Weblate (Slovak)

Currently translated at 83.3% (95 of 114 strings)

Translated using Weblate (Slovak)

Currently translated at 99.7% (393 of 394 strings)

Translated using Weblate (Slovak)

Currently translated at 43.7% (73 of 167 strings)

Translated using Weblate (Spanish)

Currently translated at 87.6% (228 of 260 strings)

Co-authored-by: Alison Alex <spamkari@hotmail.com>
Co-authored-by: Antje Schubert <antje.schubert96@web.de>
Co-authored-by: Elizaveta <lizka4231@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Irina  Shcherbinina <cat3dcat007@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: João Santos Reis <joaosreis@outlook.pt>
Co-authored-by: Julia Bacik <bacikjulia@gmail.com>
Co-authored-by: Kasper van der Linden <kasper.van.der.linden@gmail.com>
Co-authored-by: Katharina <katharinaanna.wilding@gmail.com>
Co-authored-by: Kenvinn <kevinsavio514@gmail.com>
Co-authored-by: Marie Blosse--Gilbin <mbgil@hotmail.fr>
Co-authored-by: Maya <mayabaciu1@gmail.com>
Co-authored-by: Maya B <mayabaciu1@gmail.com>
Co-authored-by: Sand <weblate@sandhydraulik.de>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Yaşar Efe Çelik <yasar.123.sevda@gmail.com>
Co-authored-by: YuyingLiang <standingfish.malina@gmail.com>
Co-authored-by: razil <boss.razmarin@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/character/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/character/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/de/
Translate-URL: https://translate.habitica.com/projects/habitica/content/es/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/content/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/de/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/es/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/he/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/de/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/es/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/de/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/de/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/de/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/de/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/es/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/zh_Hans/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Settings
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2024-12-15 18:52:35 +01:00
Kalista Payne
54617f8583 Squashed commit of the following:
commit a1f44b855cff2b54992cdca81b9dd2f67c5de20a
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Wed Dec 4 15:28:54 2024 -0600

    fix(g1g1): pass promo data thru profile flow

commit a9923f882f3294ba22e1dff9497e6f74b0d12eab
Merge: 05793922b2 75c9731ca4
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Tue Dec 3 13:53:28 2024 -0600

    Merge branch 'develop' into sabrecat/g1g1-success

commit 05793922b2a3c0f9dd206b61beefb927b00859e4
Merge: b0bbc10457 d6c47e7e81
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Tue Nov 19 12:21:42 2024 -0600

    Merge branch 'develop' into sabrecat/g1g1-success

commit b0bbc10457b0558faeaf02305210c8b0d5ed5839
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Mon Nov 4 16:26:30 2024 -0600

    feat(gifts): add dynamic G1G1 success text
    also clean out some more Amazon code
2024-12-12 12:45:14 -06:00
Phillip Thelen
75c9731ca4 filter stripe webhooks for correct server (#15320)
Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2024-11-26 12:24:23 -06:00
Kalista Payne
31afc45744 5.31.1 2024-11-22 09:20:27 -06:00
Kalista Payne
f6466b161b Squashed commit of the following:
commit ea8512bc2eeda30c4d983fb43bfcb33ea39093e9
Merge: 2bb7683cfd a36114e904
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Nov 21 12:47:11 2024 -0600

    Merge branch 'develop' into ui-drawer-fix

commit 2bb7683cfd
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Tue Nov 12 15:46:55 2024 -0500

    fix food centering and error centering at very small widths

commit fcabf03978
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Fri Nov 1 14:44:16 2024 -0400

    grid size update

commit 47a531c1aa
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Thu Oct 31 16:17:22 2024 -0400

    centered error messages on pet drawer

commit 14d2872392
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Thu Oct 24 15:47:46 2024 -0400

    center no food/no saddle messages in drawer

commit 49d1d10a4f
Merge: 6a805bdc45 f1993db0fa
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Thu Oct 24 14:04:16 2024 -0400

    Merge branch 'develop' into ui-drawer-fix

commit 6a805bdc45
Author: Alex <alex.kliger@gmail.com>
Date:   Sat Nov 19 09:41:21 2022 -0500

    fix(ui): correct crowding in title of drawer component that causes drawer toggle icon to become obstructed
2024-11-21 12:55:00 -06:00
Kalista Payne
a36114e904 Squashed commit of the following:
commit 4d88df1c381c4136cfe4780b6b5464d9c984bb31
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Wed Oct 30 14:24:53 2024 -0400

    fixing active/focus states

commit 630f74db6365625af210de81d577d43945f62df4
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Wed Oct 23 13:25:14 2024 -0400

    sign-up/login/debug menu updates

commit f85ef1c58e367219923c46aeebe77d1aa846b6f3
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Tue Oct 15 13:56:36 2024 -0400

    more color standardization

commit cd2ee3350fc51016cc75fb799debeefa0b1d9b64
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Tue Oct 15 13:39:02 2024 -0400

    updates per comments

commit e91371522bb9a6dae5c6d02eb6f16f0b632f7a24
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Fri Oct 4 14:25:45 2024 -0400

    class selection active state

commit 4fcdca1eb07eb67aacd84feb1c9357d52fd6f23f
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Thu Oct 3 14:31:00 2024 -0400

    fixing a few more buttons

commit 475e21aae7733cfa072f828c559cc0930f0ad878
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Wed Oct 2 15:57:25 2024 -0400

    line-heights converted to unitless multipliers

commit c5dca257274a60b02e1f12e3c6daf103cec53096
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Fri Sep 27 13:48:20 2024 -0400

    second pass

commit 685f4d4d64b8b234afbb702d1b8a52d4670d3825
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Thu Sep 26 16:58:59 2024 -0400

    first pass
2024-11-21 12:40:05 -06:00
dependabot[bot]
529f856ab9 Bump cross-spawn from 7.0.3 to 7.0.5 (#15365)
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.5.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.5)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 13:31:49 -05:00
Kalista Payne
9077e66973 fix(notifications): adjust z-index by @CuriousMagpie 2024-11-21 12:22:45 -06:00
Kalista Payne
a47a96b70d Squashed commit of the following:
commit 44583e224c771bab4b75758372e8b051a5346f72
Merge: 1436d8d5ef d6c47e7e81
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Tue Nov 19 12:33:34 2024 -0600

    Merge branch 'develop' into sabrecat/hourglass-afterward

commit 1436d8d5ef5b672425513e9c5886ca533594134f
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Tue Nov 19 10:44:53 2024 -0600

    fix(faq): update Hourglass explainer
2024-11-21 12:17:58 -06:00
Kalista Payne
8a94e88786 fix(migrations): correct linting errors 2024-11-21 12:07:51 -06:00
Kalista Payne
b3aa236d3d 5.31.0 2024-11-21 11:52:48 -06:00
Kalista Payne
4dd58ad89e fix(event): turkey migration update 2024-11-21 11:52:11 -06:00
Weblate
317f7ab598 Translated using Weblate (French)
Currently translated at 100.0% (890 of 890 strings)

Translated using Weblate (German)

Currently translated at 92.5% (2971 of 3211 strings)

Translated using Weblate (German)

Currently translated at 97.1% (789 of 812 strings)

Translated using Weblate (German)

Currently translated at 100.0% (887 of 887 strings)

Co-authored-by: Marie Blosse--Gilbin <mbgil@hotmail.fr>
Co-authored-by: Sand <weblate@sandhydraulik.de>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translation: Habitica/Backgrounds
Translation: Habitica/Gear
Translation: Habitica/Questscontent
2024-11-21 15:59:10 +01:00
Kalista Payne
d6c47e7e81 Squashed commit of the following:
commit 7de39d868b
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Mon Oct 14 13:05:03 2024 -0400

    gem text clickable
2024-11-19 12:09:39 -06:00
Kalista Payne
1ed61a3d3d December 2024 and Winter Wonderland 2025 build (#15358)
* feat(content): Winter 2025 build by @CuriousMagpie

* fix(content): add more WW

* fix(content): add more WW

* chore(subproj): update habitica-images

* fix(gala): December 2024 is Winter 2025

* fix(content): lint, background typo, 0 index month

* fix(content): add missing mystery set name

* fix(content): roll back erroneous month indexing

* fix(tests): no 13th month, consider releases in schedule test

* update gear strings

* fix(seasonal): show quest modal

* fix(seasonal): use category-item component

* chore(subproj): update habitica-images

---------

Co-authored-by: CuriousMagpie <eilatan@gmail.com>
2024-11-19 11:44:22 -06:00
Kalista Payne
5c734cfa00 chore(subproj): update images 2024-11-19 09:42:23 -06:00
Kalista Payne
07f485a654 chore(migrations): add subs migration 2024-11-19 09:38:05 -06:00
Kalista Payne
ae76271469 5.30.0 2024-11-19 09:11:17 -06:00
Weblate
c8a8ecbe1f Translated using Weblate (German)
Currently translated at 91.4% (2938 of 3211 strings)

Translated using Weblate (German)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (German)

Currently translated at 100.0% (887 of 887 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (French)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (Turkish)

Currently translated at 28.8% (70 of 243 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (393 of 393 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Turkish)

Currently translated at 57.6% (511 of 887 strings)

Translated using Weblate (Indonesian)

Currently translated at 94.1% (835 of 887 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (French)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (French)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3211 of 3211 strings)

Translated using Weblate (French)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (French)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (French)

Currently translated at 100.0% (393 of 393 strings)

Translated using Weblate (French)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (French)

Currently translated at 98.7% (240 of 243 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (French)

Currently translated at 97.1% (236 of 243 strings)

Translated using Weblate (French)

Currently translated at 97.1% (236 of 243 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (French)

Currently translated at 100.0% (887 of 887 strings)

Translated using Weblate (Slovak)

Currently translated at 51.0% (122 of 239 strings)

Translated using Weblate (Slovak)

Currently translated at 55.2% (143 of 259 strings)

Translated using Weblate (Korean)

Currently translated at 70.9% (629 of 887 strings)

Translated using Weblate (Korean)

Currently translated at 89.8% (150 of 167 strings)

Translated using Weblate (Korean)

Currently translated at 89.8% (150 of 167 strings)

Translated using Weblate (Polish)

Currently translated at 77.1% (199 of 258 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 86.9% (771 of 887 strings)

Translated using Weblate (Ukrainian)

Currently translated at 60.4% (1940 of 3211 strings)

Co-authored-by: Binny <45uipcik@duck.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Jimly Asshiddiqy <j_mly@ymail.com>
Co-authored-by: Lapin <sirocuro01@gmail.com>
Co-authored-by: Marie Blosse--Gilbin <mbgil@hotmail.fr>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Sand <weblate@sandhydraulik.de>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Yaşar Efe Çelik <yasar.123.sevda@gmail.com>
Co-authored-by: ZELIAL <nalfetic@gmail.com>
Co-authored-by: ana <coposmit@gmail.com>
Co-authored-by: 장은서 <eunseo5207@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/id/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/character/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/de/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/es/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/es/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/sk/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Npc
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Rebirth
Translation: Habitica/Settings
Translation: Habitica/Subscriber
2024-11-19 06:46:04 +01:00
Kalista Payne
fbf69a4a34 Squashed commit of the following:
commit dd0a410fa6c3741dc0d6793283cf4df3c37790a5
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Mon Nov 4 14:24:30 2024 -0600

    fix(subs): center next hourglass message

commit 72d92ffd76bb43fee8ba2bbabd211e595afbd664
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Fri Nov 1 14:17:59 2024 -0500

    fix(subs): don't hide HG preview entirely

commit ea0ecb0c3d519ed3d5c42266367eaaa7283ac5de
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Fri Nov 1 13:01:06 2024 -0500

    fix(subs): Google wording and HG escape

commit 2bd2c69e18e37c8c8c7106c62f186c372d25c5d2
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Fri Nov 1 09:25:30 2024 -0500

    fix(layout): tighten cancellation note

commit eb2fc40d24
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Oct 24 15:41:43 2024 -0500

    fix(g1g1): don't try to find Gems promo during bogo

commit d3eea86bd7
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Oct 24 15:00:09 2024 -0500

    fix(subs): fix typeError

commit e3ae9a2d67
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Oct 24 13:57:27 2024 -0500

    fix(subs): also redirect to subs after gift sub

commit 690163a0de
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Oct 23 16:42:38 2024 +0200

    fix test

commit 2ad7541fc0
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Oct 23 16:34:52 2024 +0200

    fix test

commit 7e337a9e59
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Oct 23 11:54:15 2024 +0200

    remove only

commit 7462b8a57f
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Oct 23 11:51:25 2024 +0200

    fix bug with incorrectly giving HG bonus

commit acd6183e95
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Mon Oct 21 17:22:26 2024 -0500

    fix(subs): unhovery and un-12-monthy

commit 935e9fd6ec
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Fri Oct 18 14:50:17 2024 -0500

    fix(subs): try again on gifts

commit 6e1fb7df38
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Oct 17 18:19:20 2024 -0500

    fix(lint): do negate object ig

commit 71d434b94e
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Oct 17 18:15:11 2024 -0500

    fix(lint): unnecessary ternary

commit b90b0bb9c3
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Oct 17 17:34:24 2024 -0500

    fix(subs): gifts DON't renew

commit 19469304c5
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Oct 17 17:13:29 2024 -0500

    fix(subs): pass autoRenews through Stripe

commit 6819e7b7e5
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Oct 17 16:03:25 2024 -0500

    fix(subscriptions): minor visual updates

commit 74633b5e5e
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Wed Oct 16 17:27:09 2024 -0500

    fix(subscriptions): more gift layout revisions

commit a90ccb89de
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Wed Oct 16 15:37:50 2024 -0500

    fix(subscription): update layout when gifting

commit c24b2db8dc
Author: Phillip Thelen <phillip@habitica.com>
Date:   Mon Oct 14 16:11:46 2024 +0200

    fix issue with promo hourglasses

commit 7a61c72b47
Author: Phillip Thelen <phillip@habitica.com>
Date:   Mon Oct 14 15:59:40 2024 +0200

    don’t give additional HG for new sub if they already got one this month

commit f14cb09026
Author: Phillip Thelen <phillip@habitica.com>
Date:   Mon Oct 14 10:38:01 2024 +0200

    Admin panel display fixes

commit f4cff698cf
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Oct 3 17:58:59 2024 -0500

    fix(stripe): correct redirect after success

commit c468b58f3f
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Oct 3 17:35:37 2024 -0500

    fix(subs): correct border-radius and redirect

commit 78fb9e31d6
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Wed Oct 2 17:41:49 2024 -0500

    fix(css): correct and refactor heights and selection states

commit e2babe8053
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Mon Sep 30 16:45:29 2024 -0500

    feat(subscription): max Gems progress readout

commit 61af8302a3
Author: Phillip Thelen <phillip@habitica.com>
Date:   Fri Sep 27 15:11:22 2024 +0200

    fix test

commit ef8ff0ea9e
Author: Phillip Thelen <phillip@habitica.com>
Date:   Fri Sep 27 14:14:44 2024 +0200

    show date for hourglass bonus if it was received

commit 4bafafdc8d
Author: Phillip Thelen <phillip@habitica.com>
Date:   Fri Sep 27 14:12:52 2024 +0200

    add new field for cumulative subscription count

commit 30096247b7
Author: Phillip Thelen <phillip@habitica.com>
Date:   Fri Sep 27 13:39:49 2024 +0200

    fix missing transaction type

commit 70872651b0
Author: Phillip Thelen <phillip@habitica.com>
Date:   Fri Sep 27 13:31:40 2024 +0200

    fix admin panel strings

commit f3398db65f
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Sep 26 23:11:16 2024 -0500

    WIP(subs): extant Stripe state

commit c6b2020109
Author: Phillip Thelen <phillip@habitica.com>
Date:   Thu Sep 26 11:41:55 2024 +0200

    fix admin panel display

commit d9afc96d2d
Author: Phillip Thelen <phillip@habitica.com>
Date:   Thu Sep 26 11:40:16 2024 +0200

    Fix hourglass logic for upgrades

commit 6e2c8eeb64
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Sep 25 17:48:54 2024 +0200

    fix hourglass count

commit cd752fbdce
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Fri Sep 20 12:24:21 2024 -0500

    WIP(frontend): draft of main subs page view

commit 0102b29d59
Author: Kalista Payne <sabe@habitica.com>
Date:   Wed Sep 18 15:29:08 2024 -0500

    fix(admin): correct logic and style for shrimple subs

commit 5469a5c5c3
Author: Kalista Payne <sabe@habitica.com>
Date:   Wed Sep 18 15:07:36 2024 -0500

    fix(test): short circuit this.

commit 526193ee6c
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Sep 18 14:42:06 2024 +0200

    fix gem limit

commit 19cf1636aa
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Aug 13 17:00:40 2024 +0200

    return nextHourglassDate again

commit eea36e3ed5
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Aug 13 13:11:22 2024 +0200

    subscription test improvements

commit ca78e74330
Author: Phillip Thelen <phillip@habitica.com>
Date:   Mon Aug 12 15:46:15 2024 +0200

    add more subscription tests

commit f4c4f93a08
Author: Phillip Thelen <phillip@habitica.com>
Date:   Fri Aug 9 13:35:22 2024 +0200

    finish basic implementation of new logic

commit e036742048
Author: Phillip Thelen <phillip@habitica.com>
Date:   Fri Aug 9 11:37:44 2024 +0200

    cleanup

commit 6431865688
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Aug 7 05:41:18 2024 -0400

    update cron tests

commit 930d875ae9
Author: Phillip Thelen <phillip@habitica.com>
Date:   Thu Aug 8 10:36:50 2024 +0200

    begin refactoring

commit 96623608d0
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Aug 6 16:28:16 2024 +0200

    begin removing obsolete tests
2024-11-14 12:31:57 -06:00
Kalista Payne
7f38ffe676 5.29.3 2024-11-12 11:52:41 -06:00
Kalista Payne
a0e0c392e9 chore(subproj): update habitica-images 2024-11-12 11:52:37 -06:00
Kalista Payne
573e472077 fix(faq): minor line edits 2024-11-07 14:42:05 -06:00
Kalista Payne
955d22278d Sabrecat/hourglass preview (#15355)
* feat(subscriptions): pre rollout announcement

* fix(faq): more h4 and p, less ul li

* fix(faq): a little less purple

* fix(subs): update change preview date
2024-11-07 12:14:29 -06:00
Kalista Payne
171ee93108 chore(subproj): update images 2024-11-07 11:36:57 -06:00
Kalista Payne
5fb0560f0b fix(migration): updateOne 2024-10-30 11:13:23 -05:00
Kalista Payne
88b616e206 5.29.2 2024-10-30 08:48:01 -05:00
Kalista Payne
08829425cb feat(event): notifications for Habitoween 2024 2024-10-30 08:47:57 -05:00
Kalista Payne
1dbd2bf0dc 5.29.1 2024-10-29 12:27:02 -05:00
Weblate
157f98b331 Translated using Weblate (Bulgarian)
Currently translated at 53.7% (1725 of 3211 strings)

Translated using Weblate (Bulgarian)

Currently translated at 12.2% (23 of 188 strings)

Translated using Weblate (German)

Currently translated at 94.0% (764 of 812 strings)

Translated using Weblate (French)

Currently translated at 100.0% (238 of 238 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3211 of 3211 strings)

Translated using Weblate (French)

Currently translated at 99.7% (3203 of 3211 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Polish)

Currently translated at 98.8% (165 of 167 strings)

Translated using Weblate (Russian)

Currently translated at 34.0% (64 of 188 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (238 of 238 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3211 of 3211 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (393 of 393 strings)

Translated using Weblate (French)

Currently translated at 99.7% (885 of 887 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (887 of 887 strings)

Translated using Weblate (Polish)

Currently translated at 98.2% (164 of 167 strings)

Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Paulina Pogorzelska <trebusze@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Yanis Rafi <yanis.rafi89@gmail.com>
Co-authored-by: immam <immament@gmail.com>
Co-authored-by: razil <boss.razmarin@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/character/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/content/es/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Questscontent
Translation: Habitica/Subscriber
2024-10-29 14:27:17 +01:00
Natalie
3d689837d6 Fix: Select class modal now shows base items (#15335)
* select class modal now shows base items, some spacing fixes

* update margin unit

* another smol fix

* two smol CSS updates

* em to rem

* margin update
2024-10-28 14:08:31 -05:00
Phillip Thelen
2b76bbe0db don’t filter mystery sets from mobile content (#15348) 2024-10-28 14:07:31 -05:00
Kalista Payne
e75db79b50 5.29.0 2024-10-24 15:56:42 -05:00
Kalista Payne
60919671ea chore(git): update subproject 2024-10-24 15:56:36 -05:00
Weblate
bca21c1cf0 Merge branch 'origin/develop' into Weblate. 2024-10-24 22:51:43 +02:00
Natalie
f1993db0fa November Content Update (#15342)
* add sprites

* November 2024 Content Release

* update November content

* chore(git): reset spritesheet

* updated spritesheet

* fix(backgrounds): typo

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2024-10-23 09:49:35 -05:00
Natalie
7351c16578 Update Puppy adjective (#15344)
* update armorArmoireBasketballUniformNotes to correct stat display

* correct typo in Puppy pet adjective

---------

Co-authored-by: Sabe Jones <sabe@habitica.com>
2024-10-23 09:46:56 -05:00
Weblate
5bc8f5dd64 Translated using Weblate (Korean)
Currently translated at 93.8% (107 of 114 strings)

Translated using Weblate (Korean)

Currently translated at 76.3% (620 of 812 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 12.2% (23 of 188 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (393 of 393 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (884 of 884 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Italian)

Currently translated at 90.6% (2902 of 3201 strings)

Translated using Weblate (Italian)

Currently translated at 99.7% (882 of 884 strings)

Co-authored-by: Changwook Kang <rkdrns417@naver.com>
Co-authored-by: Emily Brown <mockingjay.ballad@gmail.com>
Co-authored-by: Omar Bertolla <scaram@icloud.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/it/
Translate-URL: https://translate.habitica.com/projects/habitica/character/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/content/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/death/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/it/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ko/
Translation: Habitica/Backgrounds
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Death
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Pets
Translation: Habitica/Questscontent
2024-10-22 11:32:23 +02:00
Kalista Payne
20517cd0b2 5.28.9 2024-10-17 14:48:54 -05:00
Weblate
9a4081c54b Translated using Weblate (Dutch)
Currently translated at 72.6% (314 of 432 strings)

Translated using Weblate (Italian)

Currently translated at 90.1% (2886 of 3201 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Italian)

Currently translated at 89.4% (2862 of 3201 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Italian)

Currently translated at 89.2% (2858 of 3201 strings)

Translated using Weblate (Italian)

Currently translated at 87.4% (2799 of 3201 strings)

Translated using Weblate (German)

Currently translated at 93.9% (763 of 812 strings)

Translated using Weblate (Slovak)

Currently translated at 94.5% (227 of 240 strings)

Translated using Weblate (Portuguese)

Currently translated at 94.7% (126 of 133 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Portuguese)

Currently translated at 98.2% (386 of 393 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese)

Currently translated at 89.0% (98 of 110 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.7% (864 of 884 strings)

Translated using Weblate (Italian)

Currently translated at 99.7% (882 of 884 strings)

Translated using Weblate (Italian)

Currently translated at 99.7% (882 of 884 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese)

Currently translated at 98.5% (138 of 140 strings)

Translated using Weblate (Portuguese)

Currently translated at 62.5% (5 of 8 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.9% (235 of 240 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.8% (178 of 182 strings)

Translated using Weblate (Portuguese)

Currently translated at 40.4% (76 of 188 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.9% (385 of 393 strings)

Translated using Weblate (Portuguese)

Currently translated at 93.8% (830 of 884 strings)

Translated using Weblate (Portuguese)

Currently translated at 98.8% (165 of 167 strings)

Translated using Weblate (Portuguese)

Currently translated at 68.7% (178 of 259 strings)

Translated using Weblate (German)

Currently translated at 93.8% (762 of 812 strings)

Translated using Weblate (German)

Currently translated at 91.7% (2937 of 3201 strings)

Translated using Weblate (Hungarian)

Currently translated at 65.0% (281 of 432 strings)

Translated using Weblate (Hungarian)

Currently translated at 53.1% (1701 of 3201 strings)

Translated using Weblate (German)

Currently translated at 100.0% (393 of 393 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Dutch)

Currently translated at 94.4% (371 of 393 strings)

Translated using Weblate (Slovak)

Currently translated at 88.7% (213 of 240 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (393 of 393 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.6% (422 of 432 strings)

Translated using Weblate (Turkish)

Currently translated at 8.5% (16 of 188 strings)

Translated using Weblate (Turkish)

Currently translated at 70.2% (182 of 259 strings)

Translated using Weblate (Turkish)

Currently translated at 53.5% (127 of 237 strings)

Translated using Weblate (Turkish)

Currently translated at 96.4% (110 of 114 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Turkish)

Currently translated at 66.6% (288 of 432 strings)

Translated using Weblate (Turkish)

Currently translated at 7.9% (15 of 188 strings)

Translated using Weblate (Turkish)

Currently translated at 95.8% (185 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Turkish)

Currently translated at 70.2% (182 of 259 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.6% (413 of 432 strings)

Co-authored-by: DC <demidc22@gmail.com>
Co-authored-by: Donato Suozzi <donatosuozzi@gmail.com>
Co-authored-by: Emanuel Silva <azuthrax@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: ForbiddenFigs <sorautai@outlook.com>
Co-authored-by: Kacchan <h.mrena97@gmail.com>
Co-authored-by: Omar Bertolla <scaram@icloud.com>
Co-authored-by: Sefa Uğurlu <ugurlusefa2@gmail.com>
Co-authored-by: Skyler Lalieu <skyler.lalieu@outlook.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/it/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/character/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/character/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/character/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/
Translate-URL: https://translate.habitica.com/projects/habitica/content/de/
Translate-URL: https://translate.habitica.com/projects/habitica/content/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/it/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/zh_Hans/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2024-10-17 21:38:35 +02:00
Kalista Payne
97e0b31a3d Date format corrections (#15333)
* fix(cron): record nextDue as ISO string

* fix(cron): nextDue is an array

* fix(dates): record history in ISO
2024-10-15 11:59:52 -05:00
Kalista Payne
af17930314 5.28.8 2024-10-03 14:27:03 -05:00
Weblate
094b19f289 Translated using Weblate (Indonesian)
Currently translated at 100.0% (237 of 237 strings)

Translated using Weblate (Hungarian)

Currently translated at 62.7% (271 of 432 strings)

Translated using Weblate (Indonesian)

Currently translated at 94.3% (834 of 884 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Hungarian)

Currently translated at 74.5% (193 of 259 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.8% (137 of 140 strings)

Translated using Weblate (Korean)

Currently translated at 55.7% (1784 of 3201 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (Korean)

Currently translated at 48.3% (88 of 182 strings)

Translated using Weblate (Korean)

Currently translated at 79.6% (106 of 133 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.5% (138 of 140 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (393 of 393 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.2% (179 of 188 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 94.0% (764 of 812 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (237 of 237 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.5% (3187 of 3201 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 94.3% (766 of 812 strings)

Translated using Weblate (German)

Currently translated at 97.9% (423 of 432 strings)

Translated using Weblate (Hungarian)

Currently translated at 58.5% (157 of 268 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 94.2% (407 of 432 strings)

Translated using Weblate (German)

Currently translated at 97.6% (422 of 432 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.9% (3168 of 3201 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (812 of 812 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.9% (3168 of 3201 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.6% (267 of 268 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.9% (3168 of 3201 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.2% (179 of 188 strings)

Translated using Weblate (Spanish)

Currently translated at 98.3% (799 of 812 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (392 of 393 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (German)

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (237 of 237 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 6.3% (12 of 188 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Spanish)

Currently translated at 98.0% (796 of 812 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (393 of 393 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (French)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (French)

Currently translated at 100.0% (237 of 237 strings)

Translated using Weblate (German)

Currently translated at 98.7% (234 of 237 strings)

Translated using Weblate (German)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3201 of 3201 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Russian)

Currently translated at 98.9% (389 of 393 strings)

Translated using Weblate (French)

Currently translated at 100.0% (393 of 393 strings)

Translated using Weblate (Czech)

Currently translated at 95.2% (159 of 167 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (236 of 236 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3201 of 3201 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 75.0% (141 of 188 strings)

Translated using Weblate (Spanish)

Currently translated at 97.9% (795 of 812 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (390 of 390 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2935 of 3201 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 7.4% (14 of 188 strings)

Translated using Weblate (French)

Currently translated at 100.0% (812 of 812 strings)

Translated using Weblate (German)

Currently translated at 93.7% (761 of 812 strings)

Translated using Weblate (French)

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (Italian)

Currently translated at 94.2% (830 of 881 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3201 of 3201 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2935 of 3201 strings)

Translated using Weblate (Czech)

Currently translated at 11.7% (22 of 188 strings)

Translated using Weblate (Czech)

Currently translated at 74.8% (601 of 803 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 90.9% (100 of 110 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 70.5% (2254 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.8% (2933 of 3193 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 6.9% (13 of 188 strings)

Co-authored-by: Deleted User <noreply+1163@weblate.org>
Co-authored-by: Donato Suozzi <donatosuozzi@gmail.com>
Co-authored-by: Efren Ivan Quispe Mendez <efren98lp@gmail.com>
Co-authored-by: ForbiddenFigs <sorautai@outlook.com>
Co-authored-by: Ivan Mamaev <kozar.pavel.007@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Kacchan <h.mrena97@gmail.com>
Co-authored-by: Kristyna Zakova <kristynazakova@gmail.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Oliver Jeute <ojeute@freenet.de>
Co-authored-by: Shivam Ravi <shivam.ravi222@outlook.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: elly john <catboo@tutamail.com>
Co-authored-by: leechorong <lebenbbb@gmail.com>
Co-authored-by: onta <ontabotak@aol.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/es/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/id/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/it/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/character/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/
Translate-URL: https://translate.habitica.com/projects/habitica/content/
Translate-URL: https://translate.habitica.com/projects/habitica/content/es/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/
Translate-URL: https://translate.habitica.com/projects/habitica/death/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/front/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/es/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/de/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/es/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/es/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/id/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/zh_Hans/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Death
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Spells
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2024-10-03 05:22:35 +02:00
Natalie
8e54cef68b Remove smartbanner (#15329)
* remove smartbanner

* remove smartbanner.js dependency
2024-10-01 17:25:58 -05:00
DesBlock
1df8d5832f Enable Docker Replication Sets (#15298)
- Enable docker replication sets using command
- Initialize replication set using heartbeat and then use replication set status as heartbeat.
- Prevent client from starting until mongo is in a state stable for connections.
2024-09-24 11:22:22 -05:00
Kalista Payne
0542008b7f 5.28.7 2024-09-23 08:37:24 -05:00
Kalista Payne
ffa89202e6 Squashed commit of the following:
commit 22dc77c029
Author: Kalista Payne <sabe@habitica.com>
Date:   Tue Sep 17 20:34:00 2024 -0500

    fix(event): remove UTC wording

commit c72b5dbbee
Author: Kalista Payne <sabe@habitica.com>
Date:   Tue Sep 17 20:23:49 2024 -0500

    fix(sale): use native JS for showing tz info
2024-09-23 08:37:18 -05:00
Phillip Thelen
1203cbbad8 Change set for new armoire items (#15328) 2024-09-19 15:47:59 -05:00
Kalista Payne
f9fb463128 October Prebuild (#15325)
* Add october background

* Add armoire

* Add subscriber gear

* add new pet

* improve quest tests

* Fixes from gear switchup

* use new sprite system for quest images

* fixes

* fix quest image alignment

* add missing string

* fix(style): lint warnings and typo

---------

Co-authored-by: Phillip Thelen <phillip@habitica.com>
Co-authored-by: Sabe Jones <sabe@habitica.com>
2024-09-17 16:13:51 -05:00
Sabe Jones
ea398f6294 5.28.6 2024-09-17 10:23:12 -05:00
Weblate
5f41042826 Translated using Weblate (Hungarian)
Currently translated at 72.2% (187 of 259 strings)

Translated using Weblate (Russian)

Currently translated at 92.9% (2969 of 3193 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.0% (3066 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.7% (2931 of 3193 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3193 of 3193 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (French)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (French)

Currently translated at 99.8% (3187 of 3193 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.5% (775 of 803 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.4% (376 of 390 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (German)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (German)

Currently translated at 99.6% (267 of 268 strings)

Translated using Weblate (German)

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (German)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Hungarian)

Currently translated at 67.5% (175 of 259 strings)

Translated using Weblate (French)

Currently translated at 99.4% (3175 of 3193 strings)

Translated using Weblate (Hungarian)

Currently translated at 71.3% (573 of 803 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (French)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (German)

Currently translated at 91.7% (2929 of 3193 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (390 of 390 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.4% (388 of 390 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.4% (388 of 390 strings)

Co-authored-by: Antoine Lejeune <antoinelejeune1988@hotmail.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Kacchan <h.mrena97@gmail.com>
Co-authored-by: KlaptykPaperu <alairamiors@gmail.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Noah März <maerznoah@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Yanis Rafi <yanis.rafi89@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/character/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/content/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/death/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/inventory/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/de/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/de/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/bg/
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Death
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Inventory
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Overview
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Settings
Translation: Habitica/Spells
2024-09-17 17:22:17 +02:00
Sabe Jones
486b7d4da1 fix(footer): short circuit user for timetravel 2024-09-16 09:47:21 -05:00
Sabe Jones
91b47e56ff Squashed commit of the following:
commit 04fbddfd9a
Author: Sabe Jones <sabe@habitica.com>
Date:   Fri Aug 30 16:55:12 2024 -0500

    fix(groups): remove outdated group FAQ modal

commit a7ffdc9593
Author: Sabe Jones <sabe@habitica.com>
Date:   Fri Aug 30 16:34:03 2024 -0500

    fix(groups): don't spawn Justin during Groups onboarding

commit c8205de6c7
Author: Sabe Jones <sabe@habitica.com>
Date:   Fri Aug 30 16:03:03 2024 -0500

    fix(groups): correct static page account creation flow

commit 700718bd54
Author: Sabe Jones <sabe@habitica.com>
Date:   Fri Aug 23 17:47:28 2024 -0500

    chore(payments): start retiring Amazon Payments

commit 0df75b771a
Author: Sabe Jones <sabe@habitica.com>
Date:   Tue Aug 20 10:34:28 2024 -0500

    fix(groups): don't use DO NOT USE modal

commit aed7ff5f47
Author: Sabe Jones <sabe@habitica.com>
Date:   Mon Aug 19 19:40:46 2024 -0500

    refactor(groups): rearrange some CSS for better semantics

commit fd743265cf
Author: Sabe Jones <sabe@habitica.com>
Date:   Fri Aug 16 18:11:47 2024 -0500

    fix(groups): add missing upgrade workflow pieces

commit ae4469703d
Author: Sabe Jones <sabe@habitica.com>
Date:   Thu Aug 15 10:32:36 2024 -0500

    WIP(groups): style and HTML corrections
    Also workflows for static and non-upgrade logged-in scenarios

commit c6a468dabc
Author: Sabe Jones <sabe@habitica.com>
Date:   Tue Aug 13 10:58:43 2024 -0500

    WIP(groups): refactored and revised landing designs
2024-09-11 15:02:10 -05:00
Sabe Jones
9934e59629 Squashed commit of the following:
commit ddcc3a87451f60f6bc50759c56d8872b4e82496a
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Thu Sep 5 12:42:32 2024 -0400

    update mixin to add onlyOwner and quest title

commit bc1f75270bb4207a352fc9a24dad23e03d3f94c2
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Wed Sep 4 16:15:42 2024 -0400

    popover fix to difficulty but breaks img and quest title
2024-09-11 14:59:53 -05:00
Sabe Jones
50cc66d51c 5.28.5 2024-09-11 14:36:33 -05:00
Weblate
936c9dc4f3 Merge branch 'origin/develop' into Weblate. 2024-09-11 21:35:52 +02:00
Sabe Jones
946ade5da1 Merge commit from fork
Co-authored-by: Sabe Jones <sabe@habitica.com>
2024-09-11 13:53:03 -05:00
Sabe Jones
80068a3674 5.28.4 2024-09-09 12:24:20 -05:00
Sabe Jones
d7c9a7874b fix(dailies): remove broken tz plugin 2024-09-09 12:20:55 -05:00
Weblate
768e5b3f5b Translated using Weblate (Hungarian)
Currently translated at 70.7% (568 of 803 strings)

Translated using Weblate (French)

Currently translated at 100.0% (236 of 236 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (French)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (French)

Currently translated at 99.3% (3172 of 3193 strings)

Translated using Weblate (French)

Currently translated at 100.0% (803 of 803 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2927 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2926 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2926 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2926 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2926 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2926 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2926 of 3193 strings)

Translated using Weblate (German)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (French)

Currently translated at 100.0% (390 of 390 strings)

Translated using Weblate (French)

Currently translated at 100.0% (881 of 881 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.3% (774 of 803 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (390 of 390 strings)

Translated using Weblate (Hungarian)

Currently translated at 92.3% (360 of 390 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Hungarian)

Currently translated at 80.8% (76 of 94 strings)

Co-authored-by: Antoine Lejeune <antoinelejeune1988@hotmail.com>
Co-authored-by: Kacchan <h.mrena97@gmail.com>
Co-authored-by: Leonardo Broca <leo.brokka@gmail.com>
Co-authored-by: Noah März <maerznoah@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: datschka <datschka@gmx.at>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/de/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/uk/
Translation: Habitica/Backgrounds
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Gear
Translation: Habitica/Limited
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2024-09-09 19:16:50 +02:00
Sabe Jones
f3320d9ae3 5.28.3 2024-09-06 09:20:12 -05:00
Weblate
d4538b0909 Merge branch 'origin/develop' into Weblate. 2024-09-06 16:14:39 +02:00
Weblate
676ee74f19 Translated using Weblate (Hungarian)
Currently translated at 70.2% (66 of 94 strings)

Translated using Weblate (Hungarian)

Currently translated at 91.5% (357 of 390 strings)

Co-authored-by: Kacchan <h.mrena97@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/hu/
Translation: Habitica/Content
Translation: Habitica/Quests
2024-09-06 16:14:31 +02:00
Sabe Jones
9059f227fa Subscriber drops fix (#15313)
* fix(drops): include needed data to double cap

* fix(lint): whitespace and commas

* Add tests for drop cap

Signed-off-by: Sabe Jones <sabe@habitica.com>

---------

Signed-off-by: Sabe Jones <sabe@habitica.com>
Co-authored-by: Sabe Jones <sabe@habitica.com>
Co-authored-by: Phillip Thelen <phillip@habitica.com>
2024-09-06 08:56:07 -05:00
Sabe Jones
6a14d0f3f3 5.28.2 2024-09-05 11:41:57 -05:00
Weblate
3e5c623125 Merge branch 'origin/develop' into Weblate. 2024-09-05 18:37:08 +02:00
CuriousMagpie
e559fb7e4b update tests 2024-09-05 18:08:53 +02:00
CuriousMagpie
88a1cfb689 fix some of the events.test.js errors 2024-09-05 18:08:53 +02:00
CuriousMagpie
f12c4e75e6 fix pi day 2024-09-05 18:08:53 +02:00
CuriousMagpie
90f08c58cd fixing dates and offsets 2024-09-05 18:08:53 +02:00
CuriousMagpie
f6aa96c64c fix events that span Dec/Jan 2024-09-05 18:08:53 +02:00
CuriousMagpie
2b04a1b50c modified world event dates, added gem promos and g1g1 2024-09-05 18:08:53 +02:00
Weblate
7297fb5241 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Danish)

Currently translated at 92.9% (106 of 114 strings)

Translated using Weblate (Danish)

Currently translated at 87.5% (7 of 8 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Danish)

Currently translated at 67.9% (290 of 427 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2925 of 3193 strings)

Translated using Weblate (Danish)

Currently translated at 57.2% (1829 of 3193 strings)

Translated using Weblate (Danish)

Currently translated at 84.1% (202 of 240 strings)

Translated using Weblate (Danish)

Currently translated at 91.7% (167 of 182 strings)

Translated using Weblate (Danish)

Currently translated at 6.3% (12 of 188 strings)

Translated using Weblate (Danish)

Currently translated at 75.0% (603 of 803 strings)

Translated using Weblate (Danish)

Currently translated at 53.8% (49 of 91 strings)

Translated using Weblate (Hungarian)

Currently translated at 90.0% (351 of 390 strings)

Translated using Weblate (Danish)

Currently translated at 95.7% (45 of 47 strings)

Translated using Weblate (Danish)

Currently translated at 94.3% (182 of 193 strings)

Translated using Weblate (Danish)

Currently translated at 62.5% (162 of 259 strings)

Co-authored-by: Kacchan <h.mrena97@gmail.com>
Co-authored-by: TRB <bagelillekage@gmail.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/character/da/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/da/
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/da/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/da/
Translate-URL: https://translate.habitica.com/projects/habitica/front/da/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/da/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/da/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/da/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/da/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/da/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/da/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/da/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/da/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/da/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/uk/
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Loginincentives
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Settings
Translation: Habitica/Tasks
2024-09-05 11:50:10 +02:00
Sabe Jones
98c5a68a8c fix(event): add missing promo field 2024-09-04 13:33:59 -05:00
Phillip Thelen
8e643747f8 Fix tests being dependant on NEW_MYSTERY_ITEMS notification (#15288)
* Fix tests being dependant on NEW_MYSTERY_ITEMS notification

* remove only

* don’t actually need to check notification count

* fix tests

---------

Co-authored-by: Sabe Jones <sabrecat@gmail.com>
2024-09-03 18:21:00 -05:00
Sabe Jones
2483e19bee Fix 500 errors coming from Google scripts (#15237)
* fix issue with userFields options

* remove only

---------

Co-authored-by: Phillip Thelen <phillip@habitica.com>
2024-09-03 18:15:35 -05:00
Phillip Thelen
f9d3c6ed48 Add script to notify on heroku deploys (#15286)
* add script for heroku to notify about a deploy

* add emoji to server name

* add fallback for when script is run outside of git repo

* make script use bash

* remove exit
2024-09-03 18:14:22 -05:00
dependabot[bot]
09a0e75351 Bump webpack from 5.89.0 to 5.94.0 in /website/client (#15305)
Bumps [webpack](https://github.com/webpack/webpack) from 5.89.0 to 5.94.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.89.0...v5.94.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-03 18:11:49 -05:00
dependabot[bot]
644edc5b76 Bump micromatch from 4.0.5 to 4.0.8 in /website/client (#15306)
Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.5 to 4.0.8.
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.5...4.0.8)

---
updated-dependencies:
- dependency-name: micromatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-03 18:11:35 -05:00
negue
a64b994376 Adding a simple server start script (#15309)
* Adding a simple server start script

* remove pinned nodemon dep
2024-09-03 18:09:22 -05:00
Sabe Jones
fb626ebf7e Squashed commit of the following:
commit 613e6af7f0dfa3862dff117fadbb7194a29b8dbd
Author: Sabe Jones <sabe@habitica.com>
Date:   Tue Sep 3 16:57:02 2024 -0500

    feat(promo): add canonical dates for gem sales

commit 8e2fbebf3c663b0d5af5c10f3019726dbed1ca31
Author: Sabe Jones <sabe@habitica.com>
Date:   Fri Aug 23 17:08:20 2024 -0500

    fix(gems): correct Gem amounts during sale

commit 85853c697ba8eb5e6d0e053e1fe1eab295028f23
Author: Sabe Jones <sabe@habitica.com>
Date:   Fri Aug 16 16:57:02 2024 -0500

    fix(event): show user timezone to compare to UTC

commit a0a312a315cb2efad0db9370109cb036a8af0b0a
Author: Sabe Jones <sabe@habitica.com>
Date:   Fri Aug 16 15:49:08 2024 -0500

    refactor(promo): add UTC parenthetical
2024-09-03 17:03:21 -05:00
Sabe Jones
dd334f487e 5.28.1 2024-09-03 16:27:04 -05:00
Weblate
cd5c86fb69 Translated using Weblate (Hungarian)
Currently translated at 85.6% (334 of 390 strings)

Translated using Weblate (German)

Currently translated at 91.5% (2923 of 3193 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 2.6% (5 of 188 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 86.9% (766 of 881 strings)

Translated using Weblate (German)

Currently translated at 91.4% (2919 of 3193 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (German)

Currently translated at 91.3% (2917 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.3% (2917 of 3193 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (German)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (German)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (German)

Currently translated at 100.0% (387 of 387 strings)

Translated using Weblate (German)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 98.6% (869 of 881 strings)

Translated using Weblate (Russian)

Currently translated at 29.7% (56 of 188 strings)

Translated using Weblate (Russian)

Currently translated at 98.9% (386 of 390 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (German)

Currently translated at 91.1% (2912 of 3193 strings)

Co-authored-by: Andrey <andrey.martinich@gmail.com>
Co-authored-by: Angela Yulenis Ramos Carreño <yulieeniss@gmail.com>
Co-authored-by: DaniilBerkut <danilen2472@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Kacchan <h.mrena97@gmail.com>
Co-authored-by: Noah März <maerznoah@gmail.com>
Co-authored-by: TOMA Mitsuru <toma0001@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/character/de/
Translate-URL: https://translate.habitica.com/projects/habitica/content/de/
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/de/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/front/de/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/ja/
Translation: Habitica/Backgrounds
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Npc
Translation: Habitica/Settings
2024-09-03 20:57:16 +02:00
Phillip Thelen
7878761b6f Fix debug footer layout (#15308) 2024-08-30 08:29:56 -05:00
Phillip Thelen
d3b63abdd3 🧑‍💼🎛️ Overhaul (#15270)
* Add option to search for users by email or username in admin panel

* Make Admin panel design more consistent

* fix test

* fix width of items

* escape regex for searching users

* load own user when pressing enter on empty field

* add styling for warning buttons

* improve sub styling

* fix checkbox alignment in admin panel

* Unify date preview display

* Fix bottom button display

* admin panel display improvements

* remove autocannon file

* search improvements

* time travel button display fix

* fix loading spinner

* fix sorting

* Split email search into multiple queries

* fix email search

* remove console

* fix line break
2024-08-29 09:15:45 -05:00
Weblate
23fad37205 Merge branch 'origin/develop' into Weblate. 2024-08-29 15:43:45 +02:00
Sabe Jones
88558e6b98 fix(rebirth): add missing await #15300 @CuriousMagpie 2024-08-29 08:42:26 -05:00
Sabe Jones
a84ee8497b 5.28.0 2024-08-29 08:39:46 -05:00
Sabe Jones
d560ee2da1 chore(subproject): update habitica-images 2024-08-29 08:39:41 -05:00
Weblate
fd3fce110e Translated using Weblate (Hungarian)
Currently translated at 90.9% (121 of 133 strings)

Translated using Weblate (German)

Currently translated at 99.2% (266 of 268 strings)

Translated using Weblate (German)

Currently translated at 91.1% (2910 of 3193 strings)

Translated using Weblate (Hungarian)

Currently translated at 77.4% (103 of 133 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 91.8% (238 of 259 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Polish)

Currently translated at 65.1% (172 of 264 strings)

Translated using Weblate (Polish)

Currently translated at 55.2% (1765 of 3193 strings)

Translated using Weblate (Polish)

Currently translated at 99.4% (181 of 182 strings)

Translated using Weblate (German)

Currently translated at 98.7% (233 of 236 strings)

Translated using Weblate (German)

Currently translated at 100.0% (881 of 881 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Japanese)

Currently translated at 97.8% (3085 of 3153 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (235 of 235 strings)

Translated using Weblate (Japanese)

Currently translated at 96.0% (763 of 794 strings)

Translated using Weblate (Japanese)

Currently translated at 99.5% (874 of 878 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (390 of 390 strings)

Translated using Weblate (Japanese)

Currently translated at 97.8% (184 of 188 strings)

Translated using Weblate (German)

Currently translated at 99.4% (187 of 188 strings)

Translated using Weblate (Turkish)

Currently translated at 94.7% (108 of 114 strings)

Translated using Weblate (Turkish)

Currently translated at 69.0% (295 of 427 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Turkish)

Currently translated at 92.8% (155 of 167 strings)

Translated using Weblate (German)

Currently translated at 98.9% (90 of 91 strings)

Translated using Weblate (German)

Currently translated at 96.7% (88 of 91 strings)

Translated using Weblate (German)

Currently translated at 94.5% (86 of 91 strings)

Translated using Weblate (German)

Currently translated at 82.4% (75 of 91 strings)

Translated using Weblate (German)

Currently translated at 76.9% (70 of 91 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (German)

Currently translated at 69.2% (63 of 91 strings)

Translated using Weblate (German)

Currently translated at 92.2% (2908 of 3153 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Hungarian)

Currently translated at 92.3% (168 of 182 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (German)

Currently translated at 92.1% (2906 of 3153 strings)

Translated using Weblate (Hungarian)

Currently translated at 81.4% (136 of 167 strings)

Co-authored-by: Demir <feoxay32@gmail.com>
Co-authored-by: Dorota <dorakonopna3102@gmail.com>
Co-authored-by: Hanafi <naflizo@gmail.com>
Co-authored-by: Kacchan <h.mrena97@gmail.com>
Co-authored-by: Noah März <maerznoah@gmail.com>
Co-authored-by: TOMA Mitsuru <toma0001@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/de/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/de/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/id/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/front/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/front/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/de/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/id/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Subscriber
2024-08-29 11:53:26 +02:00
Natalie
1bce2b0e28 Orb of Rebirth fix (#15300)
* add await to buyModal.vue, small cosmetic improvement

* attempt to fix t.response

* removing attempt to fix t.response

* add break to asyncResource.js to prevent t.response error

* revert asyncResource.js change
2024-08-28 16:11:01 -05:00
Sabe Jones
06a59bfe03 fix(tasks): always prune __v and add id (#15301)
Co-authored-by: Sabe Jones <sabe@habitica.com>
2024-08-28 15:05:34 -05:00
Sabe Jones
83a430afad fix broken test (#15304)
Co-authored-by: Phillip Thelen <phillip@habitica.com>
2024-08-28 14:39:52 -05:00
Natalie
949f638b6e 2024-09 Content Prebuild (#15295)
* 202409 subscriber gear

* 2024-09 enchanted armoire gear

* 2024 fall festival gear

* 2024-09 background

* 2024-09 pet quest

* typos and update featuredItems.js

* quest and typo fix

* fix subscriber set name

* text amendments

* update quest title
2024-08-23 12:47:25 -05:00
dependabot[bot]
2b2193e9ce Bump axios from 1.6.8 to 1.7.4 (#15299)
Bumps [axios](https://github.com/axios/axios) from 1.6.8 to 1.7.4.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.6.8...v1.7.4)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-20 12:53:58 -04:00
Sabe Jones
0709bada87 5.27.4 2024-08-20 09:58:54 -05:00
Weblate
506586b74c Translated using Weblate (Russian)
Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (German)

Currently translated at 92.1% (2904 of 3153 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Russian)

Currently translated at 99.7% (876 of 878 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Hungarian)

Currently translated at 79.0% (132 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 99.1% (113 of 114 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Hungarian)

Currently translated at 78.4% (131 of 167 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Hungarian)

Currently translated at 71.2% (119 of 167 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Indonesian)

Currently translated at 90.7% (235 of 259 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 96.0% (763 of 794 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.9% (762 of 794 strings)

Translated using Weblate (German)

Currently translated at 92.0% (2902 of 3153 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (3153 of 3153 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.7% (760 of 794 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.5% (3108 of 3153 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (235 of 235 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3153 of 3153 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Russian)

Currently translated at 98.2% (863 of 878 strings)

Translated using Weblate (German)

Currently translated at 91.9% (2898 of 3153 strings)

Translated using Weblate (Russian)

Currently translated at 97.2% (854 of 878 strings)

Translated using Weblate (Indonesian)

Currently translated at 95.7% (225 of 235 strings)

Translated using Weblate (Indonesian)

Currently translated at 96.4% (110 of 114 strings)

Translated using Weblate (Indonesian)

Currently translated at 99.2% (132 of 133 strings)

Translated using Weblate (Indonesian)

Currently translated at 99.4% (187 of 188 strings)

Translated using Weblate (French)

Currently translated at 100.0% (794 of 794 strings)

Translated using Weblate (Indonesian)

Currently translated at 97.9% (189 of 193 strings)

Translated using Weblate (German)

Currently translated at 91.7% (2894 of 3153 strings)

Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Gleb <imejloman@gmail.com>
Co-authored-by: Hanafi <naflizo@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Kacchan <h.mrena97@gmail.com>
Co-authored-by: Koldo Almandoz Forcen <koldo.almandoz.forcen@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Static Meteor <josusantaeufemiaiglesias@gmail.com>
Co-authored-by: TORIKI <toriki.cn@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Vasily Maslyukov <vasily.maslyukov@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Widy Nuryadi <jamesxandrosneutron@gmail.com>
Co-authored-by: Юрий Артамонов <zilberstein2211@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/character/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/character/id/
Translate-URL: https://translate.habitica.com/projects/habitica/death/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/es/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/id/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/es/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/id/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/id/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/id/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/es/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/id/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/id/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/hu/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Death
Translation: Habitica/Defaulttasks
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Settings
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2024-08-20 16:52:12 +02:00
Phillip Thelen
99b2ee273f Fix issues with task scoring and in-app-reward retrieval (#15294)
* remove obsolete class and computed

* correctly load equipped gear

* load purchased for in app rewards
2024-08-15 10:38:20 -05:00
Phillip Thelen
aa6e536851 always load users version field 2024-08-14 11:59:05 -05:00
Natalie
2a2c1af7ba Add rage button to debug menu (#15291)
* + Rage

* tinkering

* remove if statement wrapper and modify error message

* add test cases

* more work on test cases

* adding contexts to test cases

* test(debug): fix up tests

* fix(lint): whisepate

---------

Co-authored-by: Sabe Jones <sabe@habitica.com>
2024-08-13 00:34:37 -05:00
Sabe Jones
48e381d702 fix(CI): force apt update before fetching libkrb5-dev 2024-08-12 17:36:01 -05:00
Phillip Thelen
9aafd76746 Improve the performance of some frequently used API calls (#15251)
* use lean for getting task lists

* Only load necessary user data for group-plans call

Also don’t make a db request for groups if the user is in none

* Only load necessary user fields for in app rewards

* Optimize updateStore by not checking every item

* Only load necessary user data for task scoring

* improve performance of inbox request calls

* merge fix

* fix scoring task call

* add quests to scoring call

* fix showing official pinned items

* also load achievements
2024-08-12 16:45:35 -05:00
Sabe Jones
0069af78a3 5.27.3 2024-08-12 15:29:07 -05:00
Weblate
c25fe7eb3d Merge branch 'origin/develop' into Weblate. 2024-08-12 22:22:07 +02:00
Weblate
b9a9013685 Translated using Weblate (French)
Currently translated at 99.7% (792 of 794 strings)

Translated using Weblate (French)

Currently translated at 100.0% (387 of 387 strings)

Translated using Weblate (German)

Currently translated at 91.7% (2892 of 3153 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Turkish)

Currently translated at 92.2% (154 of 167 strings)

Translated using Weblate (Russian)

Currently translated at 99.5% (425 of 427 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.2% (764 of 794 strings)

Translated using Weblate (Russian)

Currently translated at 94.1% (2969 of 3153 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3153 of 3153 strings)

Translated using Weblate (French)

Currently translated at 99.2% (788 of 794 strings)

Translated using Weblate (German)

Currently translated at 98.7% (232 of 235 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2890 of 3153 strings)

Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Vasily Maslyukov <vasily.maslyukov@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: billy <kreideraine@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/character/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translation: Habitica/Achievements
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Groups
Translation: Habitica/Questscontent
Translation: Habitica/Subscriber
2024-08-12 21:35:26 +02:00
Phillip Thelen
54d075e4fd Correctly load bootstrap radio component (#15293) 2024-08-12 10:53:35 -05:00
Phillip Thelen
1c40044525 better timezone handling 2024-08-09 14:26:49 -05:00
Phillip Thelen
5784694dc9 fix loading user data in app.vue (#15292)
* fix handling user version

* fix notification display
2024-08-09 08:36:06 -05:00
Phillip Thelen
7af4a6ff11 load sounds from aws (#15249) 2024-08-06 15:04:16 -05:00
Phillip Thelen
a601be0666 Don’t load browser-script again if it’s already the correct language (#15263) 2024-08-06 15:03:26 -05:00
Phillip Thelen
1be169a105 Reduce size of client js bundles (#15264)
* add packages

* Only include the needed parts of BootstrapVue

* remove yargs from client

* treeshake validator library

* formatting

* fix import
2024-08-06 12:53:44 -05:00
Phillip Thelen
6b02af69f2 Refactor the root App to load less data for front page visits (#15265)
* refactor root app to not load everything when visiting landing page

# Conflicts:
#	website/client/src/app.vue

* fix lint

* fix hiding loading screen

* fix showing snackbars when not logged in

* remove console
2024-08-06 12:49:14 -05:00
Phillip Thelen
1fe4bd2de7 Improve rate limiting (#15272)
* Improve rate limiting

* make rate limiter config names more consistent

* fix tests and add new one

* correct math
2024-08-06 12:45:27 -05:00
Sabe Jones
afd00a8ab6 Remove and clean up unused invite notif (#15279)
* fix(notifications): remove and clean up unused invite notif

* fix(lint): remove unused const

* refactor(invites): updateMany in migration, don't load inviter unless needed

* fix(lint): remove extra whitespace

* fix(groups): remove more broken inviter logic

---------

Co-authored-by: Sabe Jones <sabe@habitica.com>
2024-08-06 12:43:24 -05:00
Phillip Thelen
63918b3c20 Fix schedule using wrong month at the beginning hours of month (#15290)
* Fix schedule using wrong month at the beginning hours of month

* fix broken test

* fix switchover for time based matchers

* Fix scheduling issue related to timezones

* Fix end date creating issues
2024-08-06 12:35:05 -05:00
Sabe Jones
6293a4b936 5.27.2 2024-08-06 12:05:46 -05:00
Weblate
44502092ad Translated using Weblate (French)
Currently translated at 97.8% (184 of 188 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3153 of 3153 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 96.2% (181 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 88.8% (167 of 188 strings)

Translated using Weblate (French)

Currently translated at 100.0% (235 of 235 strings)

Translated using Weblate (French)

Currently translated at 99.8% (3148 of 3153 strings)

Translated using Weblate (Indonesian)

Currently translated at 87.7% (165 of 188 strings)

Translated using Weblate (French)

Currently translated at 99.1% (787 of 794 strings)

Translated using Weblate (German)

Currently translated at 95.3% (757 of 794 strings)

Translated using Weblate (German)

Currently translated at 99.4% (385 of 387 strings)

Translated using Weblate (German)

Currently translated at 100.0% (878 of 878 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Russian)

Currently translated at 96.0% (763 of 794 strings)

Translated using Weblate (Portuguese)

Currently translated at 35.1% (66 of 188 strings)

Translated using Weblate (Portuguese)

Currently translated at 25.5% (48 of 188 strings)

Co-authored-by: Catarina Rocha <caticalhau312@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Hanafi <naflizo@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/content/de/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/es/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/id/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translation: Habitica/Backgrounds
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Questscontent
Translation: Habitica/Subscriber
2024-08-06 19:04:21 +02:00
Natalie
ce0e8284fe Update static navbar on mobile (#15289)
* update logo on static pages (mobile)

* remove typo
2024-08-02 09:43:32 -05:00
Phillip Thelen
15f104ddd0 Add a timeout to mongoldb connections (#15258)
* Add option to set a socket timeout for mongodb requests

* Handle mongodb timeouts better

* add default to config
2024-08-01 10:28:54 -05:00
Natalie
7f6ae8ffbf Navigation Bar Visual Improvements (#15278)
* update privacy policy

* Fix serving memoized content

* add missing info to mystery item strings

* add missing string info to July mystery items

* fix food

* melior updates - loading screen & menu bar

* updates to currency tray spacing

* table styling

* one last table style

* add margin adjustment

* fix spacing

* clean up spacing styles

* remove table rounded corners

* correct faq tyops

* spacing updates

* fix web challenge instructions

* beach umbrella you are not shady except when you are

* fix mobile margin weirdness

* update wording on answers

* mobile spacing

* add chart showing details of item 1.4

* revert unrelated changes

* melior centering and icon spacing

* remove unnecessary files

* rejiggering spacing

* remove errant comma

* remove unnecessary files

* remove duplicated class

* remove duplicated class

* ...and more spacing

* notification bubble

* notification icon & currency alignment

* fix(CSS): clean up some unnecessary classes

---------

Co-authored-by: Phillip Thelen <phillip@habitica.com>
Co-authored-by: Sabe Jones <sabe@habitica.com>
2024-08-01 10:06:21 -05:00
Phillip Thelen
b2ecfb5a32 fix date dependatent time travel test (#15285) 2024-08-01 09:16:58 -05:00
Natalie
fa6ba8b668 update armorArmoireBasketballUniformNotes to correct stat display (#15262)
Co-authored-by: Sabe Jones <sabe@habitica.com>
2024-08-01 09:13:18 -05:00
Natalie
826dffc794 FAQ Typos (#15282)
* update privacy policy

* Fix serving memoized content

* add missing info to mystery item strings

* add missing string info to July mystery items

* fix food

* melior updates - loading screen & menu bar

* updates to currency tray spacing

* table styling

* one last table style

* add margin adjustment

* fix spacing

* clean up spacing styles

* remove table rounded corners

* correct faq tyops

* spacing updates

* fix web challenge instructions

* update wording on answers

* revert unrelated changes

---------

Co-authored-by: Phillip Thelen <phillip@habitica.com>
Co-authored-by: Sabe Jones <sabe@habitica.com>
2024-08-01 09:11:49 -05:00
Sabe Jones
688190ac4a 5.27.1 2024-08-01 08:37:23 -05:00
Sabe Jones
4909a3b537 Merge branch 'develop' into release 2024-08-01 08:37:14 -05:00
Weblate
64e2150f44 Translated using Weblate (Portuguese)
Currently translated at 24.4% (46 of 188 strings)

Translated using Weblate (Japanese)

Currently translated at 97.8% (3085 of 3153 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.6% (109 of 114 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.4% (3105 of 3153 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 79.2% (149 of 188 strings)

Translated using Weblate (Portuguese)

Currently translated at 23.4% (44 of 188 strings)

Translated using Weblate (Portuguese)

Currently translated at 23.4% (44 of 188 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 94.7% (752 of 794 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (387 of 387 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese)

Currently translated at 95.3% (184 of 193 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (878 of 878 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (878 of 878 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.2% (764 of 794 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Indonesian)

Currently translated at 76.5% (144 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 76.5% (144 of 188 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Co-authored-by: Catarina Rocha <caticalhau312@gmail.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Hanafi <naflizo@gmail.com>
Co-authored-by: Muhammad Hanafi <Cytricgame@gmail.com>
Co-authored-by: Márcio Ramos Corrêa <marcio.ramos.correa@gmail.com>
Co-authored-by: Pierre Huang <3541262043@qq.com>
Co-authored-by: Static Meteor <josusantaeufemiaiglesias@gmail.com>
Co-authored-by: TOMA Mitsuru <toma0001@gmail.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/character/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/character/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/id/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/uk/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Tasks
2024-08-01 15:36:26 +02:00
Sabe Jones
3f7abc459c fix(lint): clean up migration for ongoing use 2024-07-30 15:43:29 -05:00
Sabe Jones
618cdafd10 fix(lint): clean up migration for ongoing use 2024-07-30 15:42:54 -05:00
Sabe Jones
e7b37d0378 chore(migration): set up for Naming Day 2024 2024-07-30 15:13:33 -05:00
Sabe Jones
3f3e2525d2 chore(migration): set up for Naming Day 2024 2024-07-30 15:12:56 -05:00
Phillip Thelen
765e08f999 fix displaying if a pet is hatchable (#15281) 2024-07-30 12:16:43 -05:00
dependabot[bot]
598bc29647 Bump fast-xml-parser from 4.3.4 to 4.4.1 (#15280)
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 4.3.4 to 4.4.1.
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v4.3.4...v4.4.1)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sabe Jones <sabe@habitica.com>
2024-07-29 17:24:11 -05:00
Sabe Jones
39ccddfb1c fix(customization): restore broken popovers 2024-07-29 16:17:33 -05:00
Sabe Jones
108214a217 fix(lint): remove console statement 2024-07-29 14:15:47 -05:00
Sabe Jones
271f40e355 Squashed commit of the following:
commit 8ed95731cb
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Jul 23 17:59:56 2024 +0200

    fix hatched dialog

commit 53242ad96c
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Jul 23 17:38:13 2024 +0200

    fix popover not showing

commit ce4bfd25bd
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Jul 3 17:28:30 2024 +0200

    move item popover to own component

commit 2e6a300c46
Author: Phillip Thelen <phillip@habitica.com>
Date:   Mon Jul 1 18:48:24 2024 +0200

    make scaled sprites look nice

commit a3cbadb8c2
Author: Phillip Thelen <phillip@habitica.com>
Date:   Mon Jul 1 18:48:17 2024 +0200

    fix hatching dialog

commit 0e5126df5e
Author: Phillip Thelen <phillip@habitica.com>
Date:   Mon Jul 1 18:48:12 2024 +0200

    fix popover alignment

commit 7362af9236
Author: Phillip Thelen <phillip@habitica.com>
Date:   Fri Jun 28 17:07:03 2024 +0200

    fix item display

commit cf353efdb7
Author: Phillip Thelen <phillip@habitica.com>
Date:   Fri Jun 28 16:59:13 2024 +0200

    fix pet display

commit caf0cba9f2
Author: Phillip Thelen <phillip@habitica.com>
Date:   Fri Jun 28 15:24:39 2024 +0200

    fix background icon display

commit 3b06febc01
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Jun 26 12:30:04 2024 +0200

    fix sprites for notifications

commit 160b2debdc
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Jun 26 10:35:13 2024 +0200

    fix gear display in profile

commit b200a2f17d
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Jun 26 10:28:11 2024 +0200

    fix sprites for keys to the kennel

commit 3614e7a8fb
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Jun 26 10:28:00 2024 +0200

    fix sprites on avatar customization

commit 35f993d055
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Jun 26 09:18:41 2024 +0200

    fix hover icons

commit 28fc80115e
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Jun 11 16:50:37 2024 +0200

    remove console

commit b041c67679
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Jun 11 15:18:44 2024 +0200

    more lint fixes

commit f4261d0440
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Jun 11 15:18:16 2024 +0200

    fix lint

commit 878ee8f77b
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Jun 11 13:23:08 2024 +0200

    support gifs

commit aac24715aa
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Jun 11 13:15:52 2024 +0200

    move avatar customization to sprites

commit f4d3663130
Author: Phillip Thelen <phillip@habitica.com>
Date:   Fri Jun 7 17:25:19 2024 +0200

    Move more sprites out of css

commit 6e6b4c981a
Author: Phillip Thelen <phillip@habitica.com>
Date:   Fri Jun 7 16:59:30 2024 +0200

    add new sprite to item and shopItem component

commit 8712413f5d
Author: Phillip Thelen <phillip@habitica.com>
Date:   Fri Jun 7 16:37:24 2024 +0200

    use new sprites for pets list

commit 1172893826
Author: Phillip Thelen <phillip@habitica.com>
Date:   Wed Jun 5 09:43:51 2024 +0200

    Begin building new system for loading sprites
2024-07-29 14:10:27 -05:00
Phillip Thelen
e801547580 Seasonal gear fix (#15255)
* cleanup unneeded season definitions

* assign first winter seasonal gear right season

* add missing winter definition

* Fix enddate for winter galas

* fix lint

* fix halloween sprites

* set season

* fix loading habitoween sprites

* add missing customization shop sprites

* Fix test

* update customization shop sprites
2024-07-29 10:30:23 -05:00
Phillip Thelen
fc11941186 Steampunk gear should not have an end date (#15256) 2024-07-29 10:29:42 -05:00
Sabe Jones
882fad3113 5.27.0 2024-07-29 10:04:59 -05:00
Weblate
6168492711 Translated using Weblate (French)
Currently translated at 99.7% (386 of 387 strings)

Translated using Weblate (French)

Currently translated at 100.0% (878 of 878 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (235 of 235 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3153 of 3153 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (794 of 794 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (387 of 387 strings)

Translated using Weblate (Spanish)

Currently translated at 99.7% (876 of 878 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.9% (129 of 133 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.5% (3067 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 78.1% (147 of 188 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.2% (764 of 794 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.7% (108 of 114 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.8% (261 of 264 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.2% (3055 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 76.5% (144 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 39.3% (74 of 188 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.2% (764 of 794 strings)

Translated using Weblate (Spanish)

Currently translated at 99.3% (789 of 794 strings)

Translated using Weblate (Indonesian)

Currently translated at 79.1% (72 of 91 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (384 of 384 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (377 of 377 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.6% (163 of 167 strings)

Translated using Weblate (Japanese)

Currently translated at 96.8% (760 of 785 strings)

Co-authored-by: Alex Kalergi <nitrogenphospat@proton.me>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Muhammad Hanafi <Cytricgame@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: TOMA Mitsuru <toma0001@gmail.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/id/
Translate-URL: https://translate.habitica.com/projects/habitica/content/es/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/id/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/es/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/uk/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Npc
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2024-07-29 17:03:35 +02:00
Natalie
2aade9aaa6 August 2024 Content Prebuild (#15277)
* add sprites

* 2024-08 content build

* fixing stuff

* update boss rage effects

* update quest rage text and releaseDates.js

* update shop-featuredItems.js

* fricken fracken beach umbrella

* more beach umbrella

* remove egregious typo
2024-07-25 12:55:38 -05:00
Sabe Jones
4789946c4e 5.26.3 2024-07-25 08:53:26 -05:00
Sabe Jones
a69d8877c9 Merge branch 'develop' into release 2024-07-25 08:51:26 -05:00
Weblate
7cc0c3bc57 Translated using Weblate (German)
Currently translated at 91.8% (2888 of 3143 strings)

Translated using Weblate (Slovak)

Currently translated at 97.2% (177 of 182 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (785 of 785 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Slovak)

Currently translated at 92.3% (12 of 13 strings)

Translated using Weblate (German)

Currently translated at 91.7% (2883 of 3143 strings)

Translated using Weblate (Slovak)

Currently translated at 96.7% (176 of 182 strings)

Translated using Weblate (Indonesian)

Currently translated at 33.5% (63 of 188 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 2.6% (5 of 188 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.4% (765 of 785 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (384 of 384 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (377 of 377 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Indonesian)

Currently translated at 32.9% (62 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 29.2% (55 of 188 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2881 of 3143 strings)

Translated using Weblate (Slovak)

Currently translated at 93.9% (171 of 182 strings)

Translated using Weblate (French)

Currently translated at 100.0% (785 of 785 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Turkish)

Currently translated at 80.4% (107 of 133 strings)

Translated using Weblate (Turkish)

Currently translated at 7.9% (15 of 188 strings)

Translated using Weblate (Russian)

Currently translated at 95.6% (751 of 785 strings)

Translated using Weblate (French)

Currently translated at 98.3% (772 of 785 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2879 of 3143 strings)

Translated using Weblate (French)

Currently translated at 98.0% (770 of 785 strings)

Co-authored-by: Adithya Mahadev. B <adithyamahadev2521@gmail.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Ece Ekinci <plutonium3613@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Hanafi <naflizo@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/character/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/character/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/id/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/es/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/uk/
Translation: Habitica/Achievements
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Npc
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Spells
Translation: Habitica/Tasks
2024-07-25 15:49:36 +02:00
Sabe Jones
fb78495a1b feat(avatar): popovers for customization 2024-07-24 16:38:58 -05:00
Phillip Thelen
22def5111f fix purchasing wacky potions (#15276) 2024-07-24 10:35:26 -05:00
Phillip Thelen
6e91d51def Fix some scheduling issues (#15274)
* fix logic for time travelers schedule

* fix potion availability

* fix tests
2024-07-23 11:33:59 -05:00
Sabe Jones
f3b8a4e931 5.26.2 2024-07-21 13:05:07 -05:00
Weblate
69afa52beb Merge branch 'origin/develop' into Weblate. 2024-07-21 20:04:10 +02:00
Weblate
f09a39d27c Translated using Weblate (Chinese (Simplified))
Currently translated at 99.6% (872 of 875 strings)

Translated using Weblate (German)

Currently translated at 91.5% (2877 of 3143 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Spanish)

Currently translated at 98.2% (771 of 785 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.9% (3046 of 3143 strings)

Translated using Weblate (Portuguese)

Currently translated at 57.0% (1793 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.4% (765 of 785 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Italian)

Currently translated at 87.5% (7 of 8 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Italian)

Currently translated at 86.1% (368 of 427 strings)

Translated using Weblate (Italian)

Currently translated at 11.1% (21 of 188 strings)

Translated using Weblate (Italian)

Currently translated at 94.2% (740 of 785 strings)

Translated using Weblate (Italian)

Currently translated at 54.9% (50 of 91 strings)

Translated using Weblate (Italian)

Currently translated at 95.7% (45 of 47 strings)

Translated using Weblate (Italian)

Currently translated at 97.4% (188 of 193 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Italian)

Currently translated at 74.9% (194 of 259 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (German)

Currently translated at 91.4% (2875 of 3143 strings)

Translated using Weblate (Italian)

Currently translated at 94.6% (828 of 875 strings)

Translated using Weblate (Italian)

Currently translated at 94.0% (823 of 875 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 76.5% (144 of 188 strings)

Translated using Weblate (German)

Currently translated at 91.4% (2873 of 3143 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (Czech)

Currently translated at 85.7% (114 of 133 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (German)

Currently translated at 91.3% (2871 of 3143 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (1 of 1 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.4% (21 of 22 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (German)

Currently translated at 91.2% (2869 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (French)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (German)

Currently translated at 98.4% (131 of 133 strings)

Translated using Weblate (French)

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (French)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3143 of 3143 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.5% (239 of 240 strings)

Translated using Weblate (French)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (German)

Currently translated at 98.7% (237 of 240 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (384 of 384 strings)

Translated using Weblate (Slovak)

Currently translated at 97.9% (189 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Hebrew)

Currently translated at 92.8% (155 of 167 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.4% (781 of 785 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Portuguese)

Currently translated at 65.3% (153 of 234 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.8% (261 of 264 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.9% (3046 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 76.5% (144 of 188 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 76.5% (144 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 28.1% (53 of 188 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.4% (781 of 785 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (377 of 377 strings)

Translated using Weblate (Portuguese)

Currently translated at 98.1% (377 of 384 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.6% (163 of 167 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.6% (258 of 259 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (German)

Currently translated at 91.2% (2867 of 3143 strings)

Translated using Weblate (Ukrainian)

Currently translated at 98.4% (773 of 785 strings)

Translated using Weblate (French)

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (French)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Ukrainian)

Currently translated at 61.7% (1940 of 3143 strings)

Translated using Weblate (Portuguese)

Currently translated at 57.0% (1793 of 3143 strings)

Translated using Weblate (German)

Currently translated at 91.1% (2865 of 3143 strings)

Translated using Weblate (Japanese)

Currently translated at 99.4% (3085 of 3102 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (384 of 384 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Ukrainian)

Currently translated at 61.7% (1940 of 3143 strings)

Translated using Weblate (German)

Currently translated at 91.0% (2863 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (Slovak)

Currently translated at 74.9% (588 of 785 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (German)

Currently translated at 95.6% (109 of 114 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (German)

Currently translated at 91.0% (2861 of 3143 strings)

Translated using Weblate (German)

Currently translated at 98.7% (231 of 234 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (German)

Currently translated at 99.2% (262 of 264 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3143 of 3143 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (384 of 384 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (233 of 233 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (286 of 286 strings)

Translated using Weblate (German)

Currently translated at 90.9% (2857 of 3143 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.4% (382 of 384 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.4% (382 of 384 strings)

Translated using Weblate (Slovak)

Currently translated at 97.6% (375 of 384 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (German)

Currently translated at 99.7% (383 of 384 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.8% (3043 of 3143 strings)

Translated using Weblate (German)

Currently translated at 90.8% (2855 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.7% (3041 of 3143 strings)

Translated using Weblate (French)

Currently translated at 99.6% (3132 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (377 of 377 strings)

Translated using Weblate (French)

Currently translated at 100.0% (384 of 384 strings)

Translated using Weblate (Indonesian)

Currently translated at 91.3% (799 of 875 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (German)

Currently translated at 98.9% (380 of 384 strings)

Translated using Weblate (Indonesian)

Currently translated at 95.3% (184 of 193 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.7% (377 of 378 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Indonesian)

Currently translated at 94.0% (157 of 167 strings)

Translated using Weblate (Japanese)

Currently translated at 99.4% (192 of 193 strings)

Translated using Weblate (French)

Currently translated at 99.0% (3114 of 3143 strings)

Translated using Weblate (German)

Currently translated at 90.7% (2853 of 3143 strings)

Translated using Weblate (French)

Currently translated at 100.0% (785 of 785 strings)

Translated using Weblate (Indonesian)

Currently translated at 94.0% (157 of 167 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (German)

Currently translated at 98.5% (421 of 427 strings)

Translated using Weblate (French)

Currently translated at 98.7% (3105 of 3143 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (French)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (German)

Currently translated at 98.4% (190 of 193 strings)

Translated using Weblate (French)

Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Turkish)

Currently translated at 5.8% (11 of 188 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (785 of 785 strings)

Translated using Weblate (German)

Currently translated at 98.4% (773 of 785 strings)

Translated using Weblate (German)

Currently translated at 65.9% (60 of 91 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (189 of 189 strings)

Translated using Weblate (Spanish)

Currently translated at 99.7% (873 of 875 strings)

Translated using Weblate (German)

Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (773 of 773 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (868 of 868 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3143 of 3143 strings)

Co-authored-by: Alex G <powerali18@gmail.com>
Co-authored-by: Antonio Spinelli <tonicospinelli@users.noreply.translate.habitica.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Elisa Bailo <bailoelisa5@gmail.com>
Co-authored-by: Eva <kornmann.eva@gmx.de>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Isabela de Carvalho <isabela.c.escritora@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Justcallme rye <Blizzardscf32@gmail.com>
Co-authored-by: Miguel Rocha de Macedo Amaral <miguelrmamaral@gmail.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Paolo Bizzarri <pibizza@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: TOMA Mitsuru <toma0001@gmail.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Vinicius Rodrigues <suburbanizar@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Yan <ariamao0802@gmail.com>
Co-authored-by: billypat <kreideraine@gmail.com>
Co-authored-by: diamondmonster09 <diamondmonster09@gmail.com>
Co-authored-by: tamara <tab.bravani@gmail.com>
Co-authored-by: 过客是个铁憨憨 <1811304592@qq.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/he/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/id/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/it/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/id/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/it/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/character/de/
Translate-URL: https://translate.habitica.com/projects/habitica/character/es/
Translate-URL: https://translate.habitica.com/projects/habitica/character/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/character/id/
Translate-URL: https://translate.habitica.com/projects/habitica/character/it/
Translate-URL: https://translate.habitica.com/projects/habitica/character/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/character/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/character/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/character/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/de/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/it/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/content/de/
Translate-URL: https://translate.habitica.com/projects/habitica/content/es/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/content/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/it/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/id/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/front/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/de/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/es/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/it/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/de/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/es/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/id/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/es/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/it/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/merch/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/de/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/es/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/it/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/de/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/es/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/es/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/it/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/it/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/it/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/tr/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Merch
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Settings
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2024-07-21 20:03:59 +02:00
Phillip Thelen
2e71963fbf Update schedule.js (#15273)
Add SandSculpture for July
2024-07-21 12:58:28 -05:00
Sabe Jones
f740a92fb7 Update README.md
fix awkward line breaks
2024-07-19 17:37:31 -05:00
Sabe Jones
21e7ddea16 Update README.md 2024-07-19 17:36:26 -05:00
Sabe Jones
923d90cf22 Merge branch 'sabrecat/postfixes' into develop 2024-07-17 17:43:05 -05:00
Sabe Jones
b386a1917d Merge branch 'sabrecat/more-api-sunset' into develop 2024-07-17 17:42:28 -05:00
Sabe Jones
4a5427b2b2 Merge branch 'develop' into release 2024-07-03 14:23:53 -05:00
Sabe Jones
04554c5309 5.26.1 2024-07-03 14:23:37 -05:00
Phillip Thelen
5ef88b5c56 Update releaseDates.js 2024-07-03 18:52:23 +02:00
Sabe Jones
1f2397b81a fix(quests): adjust margins 2024-07-02 17:13:40 -05:00
Weblate
892c4934d5 Translated using Weblate (Japanese)
Currently translated at 99.2% (3080 of 3103 strings)

Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/
Translation: Habitica/Gear
2024-07-01 22:51:54 +02:00
Sabe Jones
60d5aaaaa6 fix(memoize): enumerate food 2024-07-01 09:02:12 -05:00
Sabe Jones
f506b840ed fix(lint): add missing whitespace for curlies 2024-06-28 23:34:32 -05:00
Sabe Jones
f357750d88 test(chat): coverage for new sunsets 2024-06-28 23:29:58 -05:00
Sabe Jones
a2dbe68338 Merge branch 'release' into sabrecat/more-api-sunset 2024-06-28 22:20:07 -05:00
Sabe Jones
491d2cfab1 fix(modals): various tweks 2024-06-28 22:03:45 -05:00
Sabe Jones
fa91abb739 Merge branch 'develop' into sabrecat/postfixes 2024-06-28 21:28:39 -05:00
Sabe Jones
6b46d04537 Merge branch 'develop' into release 2024-06-28 11:27:58 -05:00
Sabe Jones
b90457c04f fix(test): correct hatching potion test 2024-06-28 11:20:21 -05:00
Sabe Jones
379d98a91e fix(test): correct schedule test 2024-06-28 11:09:09 -05:00
Sabe Jones
07352480cd Merge remote-tracking branch 'origin/phillip/memoize-me' into develop 2024-06-28 10:26:38 -05:00
Phillip Thelen
a6ff8e095a fix naming 2024-06-28 10:21:03 -05:00
Phillip Thelen
1fb44bbe73 fix naming 2024-06-28 10:20:11 -05:00
Phillip Thelen
5323849f90 fix naming 2024-06-28 17:16:45 +02:00
Sabe Jones
ffd2b4b76f fix(quests): modest Bundle revisions 2024-06-27 11:41:39 -05:00
Sabe Jones
c50ed843fb Merge branch 'release' into sabrecat/postfixes 2024-06-27 10:03:30 -05:00
Sabe Jones
850ae5114f fix(schedule): add Oddballs to November 2024-06-24 13:27:13 -05:00
Phillip Thelen
28193f86fb Fix serving memoized content 2024-06-21 11:12:18 +02:00
Phillip Thelen
877fe48225 correctly memoize conent api 2024-06-20 12:23:24 +02:00
Phillip Thelen
e0f6f79c5b don’t build multiple times on heroku 2024-06-20 10:11:27 +02:00
Phillip Thelen
f62254d68e fix client command 2024-06-19 19:40:20 +02:00
Phillip Thelen
d054e6fc16 correct build call 2024-06-19 19:36:57 +02:00
Phillip Thelen
7231f699c1 try setting up with heroku buildpack 2024-06-19 19:32:32 +02:00
Phillip Thelen
1dae0793fd call gulp build:prod 2024-06-19 18:50:32 +02:00
Phillip Thelen
f18fbe86b6 build client 2024-06-19 18:40:53 +02:00
Phillip Thelen
61a61724ca testing 2024-06-19 18:33:18 +02:00
Phillip Thelen
93cf30eb18 integration fix 2024-06-19 18:20:43 +02:00
Phillip Thelen
cff08adcd0 specify dev docker file 2024-06-19 18:13:20 +02:00
Phillip Thelen
4da2ed4a1f initialize stub 2024-06-19 18:10:07 +02:00
Phillip Thelen
11c5b26c59 test heroku file 2024-06-19 18:08:45 +02:00
Phillip Thelen
ac85bb2e2d fix stub reference 2024-06-19 18:03:15 +02:00
Phillip Thelen
74dfb2710f test fixes 2024-06-19 18:02:28 +02:00
Phillip Thelen
8dbd3c3db1 fix canOwn test 2024-06-19 17:37:04 +02:00
Phillip Thelen
74b3b348ff fix buy test 2024-06-19 17:32:31 +02:00
Phillip Thelen
3386d61fde fix debug tests 2024-06-19 17:30:52 +02:00
Phillip Thelen
19da14531c add chameleon to featured quests 2024-06-19 17:05:25 +02:00
Phillip Thelen
254dd80f24 fix import 2024-06-19 17:05:14 +02:00
Phillip Thelen
0bc3f16b4b add new content to new release file 2024-06-19 16:33:22 +02:00
Phillip Thelen
5184973bd5 fix release date tests 2024-06-19 16:33:11 +02:00
Phillip Thelen
b6accca5ca fix armoire tests 2024-06-19 16:33:06 +02:00
Phillip Thelen
fec68e6211 fix tests 2024-06-19 16:02:03 +02:00
Phillip Thelen
fc63c906dd Improve test coverage 2024-06-19 15:25:45 +02:00
Phillip Thelen
3333f8f0f5 allow hatching potions to have a release date 2024-06-19 15:25:45 +02:00
Phillip Thelen
89a3ac3dde allow eggs to have a release date
# Conflicts:
#	test/content/armoire.test.js
2024-06-19 15:24:21 +02:00
CuriousMagpie
16551ec83f Merge branch '2024-07-content-prebuild' into subs-private 2024-06-18 15:03:12 -04:00
CuriousMagpie
2645bf6023 update habitica images 2024-06-18 15:02:47 -04:00
CuriousMagpie
f5f4974a73 update habitica-images 2024-06-18 14:58:13 -04:00
CuriousMagpie
162e337d14 Merge branch '2024-07-content-prebuild' into subs-private 2024-06-18 13:46:03 -04:00
CuriousMagpie
21a7d36b7b update sprites 2024-06-18 13:45:09 -04:00
CuriousMagpie
f2506c3231 updated sprites css 2024-06-18 13:24:21 -04:00
CuriousMagpie
d47641e25a typo fix 2024-06-18 12:46:59 -04:00
CuriousMagpie
fb8479ad1e finish July prebuild 2024-06-17 13:44:36 -04:00
CuriousMagpie
3810cf3ef3 add chameleon quest 2024-06-14 10:42:47 -04:00
CuriousMagpie
d05da3722c add June background notes 2024-06-13 17:12:43 -04:00
CuriousMagpie
b8a3440ef2 fix mystery item and background description 2024-06-13 16:40:04 -04:00
CuriousMagpie
44d63032d8 add subscriber gear, enchanted armoire, and background 2024-06-13 15:38:23 -04:00
CuriousMagpie
9d7da91ec6 add sprites 2024-06-13 14:44:59 -04:00
Sabe Jones
8b3267458b fix(groups): fix TypeError when missing 2024-02-05 18:06:18 -06:00
Sabe Jones
bccfaab350 Merge branch 'release' into sabrecat/more-api-sunset 2024-02-05 17:28:11 -06:00
Sabe Jones
753f12979e fix(api): close lingering deprecated APIs 2024-02-05 17:22:47 -06:00
973 changed files with 17821 additions and 33455 deletions

View File

@@ -19,7 +19,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -41,7 +42,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -63,7 +65,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -86,7 +89,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -108,7 +112,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -137,7 +142,8 @@ jobs:
with:
mongodb-version: ${{ matrix.mongodb-version }}
mongodb-replica-set: rs
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -167,7 +173,8 @@ jobs:
with:
mongodb-version: ${{ matrix.mongodb-version }}
mongodb-replica-set: rs
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -197,7 +204,8 @@ jobs:
with:
mongodb-version: ${{ matrix.mongodb-version }}
mongodb-replica-set: rs
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -222,7 +230,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -246,7 +255,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |

2
.gitignore vendored
View File

@@ -47,5 +47,5 @@ webpack.webstorm.config
# mongodb replica set for local dev
mongodb-*.tgz
/mongodb-data
/mongodb-data*
/.nyc_output

25
.heroku/report_deploy.sh Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
DEVELOPER="someone"
if git rev-parse --git-dir > /dev/null 2>&1; then
DEVELOPERS=$(git log -5 --pretty=format:'%an')
IFS=$'\n'
DEVELOPER=""
for dev in $DEVELOPERS
do
if [ "$DEVELOPER" == "someone" ]; then
if [[ ${dev} != *"[bot]"* ]]; then
DEVELOPER=$dev
continue
fi
continue
fi
done
fi
PARTS=$(cut -d"." -f1 <<< $BASE_URL)
SERVER_NAME=$(cut -d"/" -f3 <<< ${PARTS[0]})
SERVER_NAME=":$SERVER_EMOJI: $SERVER_NAME"
wget $SLACK_DEPLOY_URL --post-data="{\"server_name\": \"$SERVER_NAME\", \"developer\": \"$DEVELOPER\", \"base_url\": \"$BASE_URL\"}" -O /dev/null

View File

@@ -1,14 +1,20 @@
Habitica ![Build Status](https://github.com/HabitRPG/habitica/workflows/Test/badge.svg) [![Code Climate](https://codeclimate.com/github/HabitRPG/habitrpg.svg)](https://codeclimate.com/github/HabitRPG/habitrpg) [![Bountysource](https://api.bountysource.com/badge/tracker?tracker_id=68393)](https://www.bountysource.com/trackers/68393-habitrpg?utm_source=68393&utm_medium=shield&utm_campaign=TRACKER_BADGE)
Habitica ![Build Status](https://github.com/HabitRPG/habitica/workflows/Test/badge.svg)
===============
[Habitica](https://habitica.com) is an open-source habit-building program that treats your life like a role-playing game. Level up as you succeed, lose HP as you fail, and earn money to buy weapons and armor.
[Habitica](https://habitica.com) is an open-source habit-building program that treats your life like a role-playing game. Level up as you succeed, lose HP as you fail, and earn Gold to buy weapons and armor!
**We need more programmers!** Your assistance will be greatly appreciated. The wiki pages below and the additional pages they link to will tell you how to get started on contributing code and where you can go to seek further help or ask questions:
**Want to contribute code to Habitica?** We're always looking for assistance on any issues in our repo with the "Help Wanted" label. The wiki pages below and the additional linked pages will tell you how to start contributing code and where you can seek further help or ask questions:
* [Guidance for Blacksmiths](https://habitica.fandom.com/wiki/Guidance_for_Blacksmiths) - an introduction to the technologies used and how the software is organized.
* [Setting up Habitica Locally](https://habitica.fandom.com/wiki/Setting_up_Habitica_Locally) - how to set up a local install of Habitica for development and testing on various platforms.
* [Setting up Habitica Locally](https://github.com/HabitRPG/habitica/wiki/Setting-Up-Habitica-for-Local-Development) - how to set up a local install of Habitica for development and testing.
**Interested in contributing to Habiticas mobile apps?** Visit the links below for our mobile repositories.
* **Android:** https://github.com/HabitRPG/habitica-android
* **iOS:** https://github.com/HabitRPG/habitica-ios
Habitica's code is licensed as described at https://github.com/HabitRPG/habitica/blob/develop/LICENSE
**Found a bug?** Please report it to [admin email](mailto:admin@habitica.com) rather than creating an issue (an admin will advise you if a new issue is necessary; usually it is not).
**Found a bug?** Please report it to [admin email](mailto:admin@habitica.com) rather than create an issue (an admin will advise you if a new issue is necessary; usually it is not).
**Have any questions about Habitica or its community?** See the links in the [habitica.com](https://habitica.com) website's Help menu or drop in to [Guilds > Tavern Chat](https://habitica.com/groups/tavern) to ask questions or chat socially!
**Creating a third-party tool?** Please review our [API Usage Guidelines](https://github.com/HabitRPG/habitica/wiki/API-Usage-Guidelines) to ensure that your tool is compliant and maintains the best experience for Habitica players.
**Have any questions about Habitica or contributing?** See the links in the [Habitica](https://habitica.com) website's Help menu. Theres FAQs, guides, and the option to reach out to us with any further questions!

View File

@@ -37,6 +37,7 @@
"NODE_DB_URI": "mongodb://localhost:27017/habitica-dev?replicaSet=rs",
"TEST_DB_URI": "mongodb://localhost:27017/habitica-test?replicaSet=rs",
"MONGODB_POOL_SIZE": "10",
"MONGODB_SOCKET_TIMEOUT": "20000",
"NODE_ENV": "development",
"PATH": "bin:node_modules/.bin:/usr/local/bin:/usr/bin:/bin",
"PAYPAL_BILLING_PLANS_basic_12mo": "basic_12mo",

View File

@@ -22,7 +22,8 @@ services:
dockerfile: ./Dockerfile-Dev
command: ["npm", "start"]
depends_on:
- mongo
mongo:
condition: service_healthy
environment:
- NODE_DB_URI=mongodb://mongo/habitrpg
networks:
@@ -33,7 +34,16 @@ services:
- .:/usr/src/habitica
- /usr/src/habitica/node_modules
mongo:
image: mongo:3.6
image: mongo:5.0.23
restart: unless-stopped
command: ["--replSet", "rs", "--bind_ip_all", "--port", "27017"]
healthcheck:
test: echo "try { rs.status() } catch (err) { rs.initiate() }" | mongosh --port 27017 --quiet
interval: 10s
timeout: 30s
start_period: 0s
start_interval: 1s
retries: 30
networks:
- habitica
ports:

View File

@@ -42,10 +42,41 @@ function cssVarMap (sprite) {
}
}
function createSpritesStream (name, src) {
function filterFile (file) {
if (file.relative.indexOf('Mount_Icon_') !== -1) {
return false;
}
if (file.path.indexOf('shop/') !== -1) {
return false;
}
if (file.path.indexOf('stable/eggs') !== -1) {
return false;
}
if (file.path.indexOf('stable/food') !== -1) {
return false;
}
if (file.path.indexOf('stable/potions') !== -1) {
return false;
}
if (file.relative.indexOf('shop_') === 0) {
return false;
}
if (file.relative.indexOf('icon_background') === 0) {
return false;
}
return true;
}
async function createSpritesStream (name, src) {
const stream = mergeStream();
// need to import this way bc of weird dependency things
// eslint-disable-next-line global-require
const filter = require('gulp-filter');
const f = filter(filterFile);
const spriteData = gulp.src(src)
.pipe(f)
.pipe(spritesmith({
imgName: `spritesmith-${name}.png`,
cssName: `spritesmith-${name}.css`,
@@ -63,7 +94,7 @@ function createSpritesStream (name, src) {
return stream;
}
gulp.task('sprites:main', () => {
gulp.task('sprites:main', async () => {
const mainSrc = sync('habitica-images/**/*.png');
return createSpritesStream('main', mainSrc);
});

View File

@@ -0,0 +1,47 @@
/* eslint-disable no-console */
import { model as User } from '../../../website/server/models/user';
const MIGRATION_NAME = '2024_purge_invite_accepted';
const progressCount = 1000;
let count = 0;
async function updateUsers (userIds) {
count += userIds.length;
if (count % progressCount === 0) console.warn(`${count} ${userIds[0]}`);
return await User.updateMany(
{ _id: { $in: userIds } },
{ $pull: { notifications: { type: 'GROUP_INVITE_ACCEPTED' } } },
).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'notifications.type': 'GROUP_INVITE_ACCEPTED',
'auth.timestamps.loggedin': { $gt: new Date('2024-06-25') },
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({ _id: 1 })
.select({ _id: 1 })
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
const userIds = users.map(user => user._id);
await updateUsers(userIds); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -0,0 +1,115 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20241119_gem_caps_hourglasses';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count += 1;
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
const { consecutive, customerId, dateTerminated, planId } = user.purchased.plan;
const isRecurring = customerId !== 'Gift' && !dateTerminated;
const updateOp = {
$set: {
migration: MIGRATION_NAME,
'purchased.plan.consecutive.gemCapExtra': Math.max(2 * Math.ceil((consecutive.gemCapExtra + 1) / 2, 26)),
},
$inc: {},
};
let hourglassBonus = 0;
if (isRecurring) {
await user.updateBalance(
5,
'admin_update_balance',
'',
'Subscription Reward Migration',
);
updateOp.$inc.balance = 5;
switch (planId) {
case 'basic':
case 'basic_earned':
case 'group_plan_auto':
hourglassBonus = 2;
break;
case 'basic_3mo':
case 'basic_6mo':
case 'google_6mo':
hourglassBonus = 4;
break;
case 'basic_12mo':
hourglassBonus = 12;
updateOp.$set['purchased.plan.hourglassPromoReceived'] = new Date();
break;
default:
hourglassBonus = 0;
}
if (hourglassBonus) {
updateOp.$inc['purchased.plan.consecutive.trinkets'] = hourglassBonus;
await user.updateHourglasses(
hourglassBonus,
'admin_update_balance',
'',
'Subscription Reward Migration',
);
}
updateOp.$push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_subscriber_reward',
title: 'Thanks for being a subscriber!',
text: 'Enjoy these extra Mystic Hourglasses and Gems to celebrate our new benefits.',
},
seen: false,
},
};
}
return await User.updateOne(
{ _id: user._id },
updateOp,
).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'purchased.plan.customerId': { $exists: true },
$or: [
{ 'purchased.plan.dateTerminated': { $exists: false } },
{ 'purchased.plan.dateTerminated': null },
{ 'purchased.plan.dateTerminated': { $gt: new Date() } },
],
};
const fields = {
_id: 1,
purchased: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -26,7 +26,7 @@ async function updateUser (user) {
[{ name: 'BASE_URL', content: BASE_URL }], // Add variables from template
);
return User.update({ _id: user._id }, { $set: { migration: MIGRATION_NAME } }).exec();
return User.updateOne({ _id: user._id }, { $set: { migration: MIGRATION_NAME } }).exec();
}
export default async function processUsers () {

View File

@@ -27,13 +27,13 @@ async function updateUser (user) {
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return User.update({ _id: user._id }, { $set: set }).exec();
return User.updateOne({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
const query = {
migration: { $ne: MIGRATION_NAME },
'auth.local.lowerCaseUsername': 'olson1',
'auth.local.username': 'ExampleHabitican',
};
const fields = {

View File

@@ -57,7 +57,7 @@ async function updateUser (user) {
export default async function processUsers () {
const query = {
migration: { $ne: MIGRATION_NAME },
'auth.local.username': 'SabreTest',
'auth.local.username': 'ExampleHabitican',
};
const fields = {

View File

@@ -0,0 +1,175 @@
/*
* Award Habitoween ladder items to participants in this month's Habitoween festivities
*/
/* eslint-disable no-console */
import { model as User } from '../../website/server/models/user';
const MIGRATION_NAME = '20241030_habitoween_ladder'; // Update when running in future years
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count += 1;
const set = { migration: MIGRATION_NAME };
const inc = {
'items.food.Candy_Skeleton': 1,
'items.food.Candy_Base': 1,
'items.food.Candy_CottonCandyBlue': 1,
'items.food.Candy_CottonCandyPink': 1,
'items.food.Candy_Shade': 1,
'items.food.Candy_White': 1,
'items.food.Candy_Golden': 1,
'items.food.Candy_Zombie': 1,
'items.food.Candy_Desert': 1,
'items.food.Candy_Red': 1,
};
const push = { notifications: { $each: [] } };
if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-RoyalPurple']) {
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_habitoween_candy',
title: 'Happy Habitoween!',
text: 'For this spooky celebration, you\'ve received an assortment of candy for your Pets!',
destination: '/inventory/stable',
},
seen: false,
});
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-RoyalPurple']) {
set['items.mounts.JackOLantern-RoyalPurple'] = true;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_habitoween_purple_mount',
title: 'Happy Habitoween!',
text: 'For this spooky celebration, you\'ve received a Royal Purple Jack-O-Lantern Mount and an assortment of candy for your Pets!',
destination: '/inventory/stable',
},
seen: false,
});
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Glow']) {
set['items.pets.JackOLantern-RoyalPurple'] = 5;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_habitoween_purple_pet',
title: 'Happy Habitoween!',
text: 'For this spooky celebration, you\'ve received a Royal Purple Jack-O-Lantern Pet and an assortment of candy for your Pets!',
destination: '/inventory/stable',
},
seen: false,
});
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Glow']) {
set['items.mounts.JackOLantern-Glow'] = true;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_habitoween_glow_mount',
title: 'Happy Habitoween!',
text: 'For this spooky celebration, you\'ve received a Glow-in-the-Dark Jack-O-Lantern Mount and an assortment of candy for your Pets!',
destination: '/inventory/stable',
},
seen: false,
});
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Ghost']) {
set['items.pets.JackOLantern-Glow'] = 5;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_habitoween_glow_pet',
title: 'Happy Habitoween!',
text: 'For this spooky celebration, you\'ve received a Glow-in-the-Dark Jack-O-Lantern Pet and an assortment of candy for your Pets!',
destination: '/inventory/stable',
},
seen: false,
});
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) {
set['items.mounts.JackOLantern-Ghost'] = true;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_habitoween_ghost_mount',
title: 'Happy Habitoween!',
text: 'For this spooky celebration, you\'ve received a Ghost Jack-O-Lantern Mount and an assortment of candy for your Pets!',
destination: '/inventory/stable',
},
seen: false,
});
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) {
set['items.pets.JackOLantern-Ghost'] = 5;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_habitoween_ghost_pet',
title: 'Happy Habitoween!',
text: 'For this spooky celebration, you\'ve received a Ghost Jack-O-Lantern Pet and an assortment of candy for your Pets!',
destination: '/inventory/stable',
},
seen: false,
});
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) {
set['items.mounts.JackOLantern-Base'] = true;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_habitoween_base_mount',
title: 'Happy Habitoween!',
text: 'For this spooky celebration, you\'ve received a Jack-O-Lantern Mount and an assortment of candy for your Pets!',
destination: '/inventory/stable',
},
seen: false,
});
} else {
set['items.pets.JackOLantern-Base'] = 5;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_habitoween_base_pet',
title: 'Happy Habitoween!',
text: 'For this spooky celebration, you\'ve received a Jack-O-Lantern Pet and an assortment of candy for your Pets!',
destination: '/inventory/stable',
},
seen: false,
});
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return User.updateOne({ _id: user._id }, { $inc: inc, $push: push, $set: set }).exec();
}
export default async function processUsers () {
const query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2024-10-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({ _id: 1 })
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
}

View File

@@ -0,0 +1,167 @@
/* eslint-disable no-console */
import { model as User } from '../../website/server/models/user';
const MIGRATION_NAME = '20241120_harvest_feast';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count += 1;
const updateOp = {
$set: { migration: MIGRATION_NAME },
};
if (typeof user.items.gear.owned.head_special_turkeyHelmGilded !== 'undefined') {
updateOp.$inc = {
'items.food.Pie_Base': 1,
'items.food.Pie_CottonCandyBlue': 1,
'items.food.Pie_CottonCandyPink': 1,
'items.food.Pie_Desert': 1,
'items.food.Pie_Golden': 1,
'items.food.Pie_Red': 1,
'items.food.Pie_Shade': 1,
'items.food.Pie_Skeleton': 1,
'items.food.Pie_Zombie': 1,
'items.food.Pie_White': 1,
};
updateOp.$push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_harvestfeast_pie',
title: 'Happy Harvest Feast!',
text: 'Gobble gobble, you\'ve received an assortment of pie for your Pets!',
destination: '/inventory/stable',
},
seen: false,
},
};
} else if (typeof user.items.gear.owned.armor_special_turkeyArmorBase !== 'undefined') {
updateOp.$set['items.gear.owned.head_special_turkeyHelmGilded'] = true;
updateOp.$set['items.gear.owned.armor_special_turkeyArmorGilded'] = true;
updateOp.$set['items.gear.owned.back_special_turkeyTailGilded'] = true;
updateOp.$push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_harvestfeast_gilded_set',
title: 'Happy Harvest Feast!',
text: 'Gobble gobble, you\'ve received the Gilded Turkey Armor, Helm, and Tail!',
destination: '/inventory/equipment',
},
seen: false,
},
};
} else if (user.items && user.items.mounts && user.items.mounts['Turkey-Gilded']) {
updateOp.$set['items.gear.owned.head_special_turkeyHelmBase'] = true;
updateOp.$set['items.gear.owned.armor_special_turkeyArmorBase'] = true;
updateOp.$set['items.gear.owned.back_special_turkeyTailBase'] = true;
updateOp.$push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_harvestfeast_base_set',
title: 'Happy Harvest Feast!',
text: 'Gobble gobble, you\'ve received the Turkey Armor, Helm, and Tail!',
destination: '/inventory/equipment',
},
seen: false,
},
};
} else if (user.items && user.items.pets && user.items.pets['Turkey-Gilded']) {
updateOp.$set['items.mounts.Turkey-Gilded'] = true;
updateOp.$push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_harvestfeast_gilded_mount',
title: 'Happy Harvest Feast!',
text: 'Gobble gobble, you\'ve received the Gilded Turkey Mount!',
destination: '/inventory/stable',
},
seen: false,
},
};
} else if (user.items && user.items.mounts && user.items.mounts['Turkey-Base']) {
updateOp.$set['items.pets.Turkey-Gilded'] = 5;
updateOp.$push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_harvestfeast_gilded_pet',
title: 'Happy Harvest Feast!',
text: 'Gobble gobble, you\'ve received the Gilded Turkey Pet!',
destination: '/inventory/stable',
},
seen: false,
},
};
} else if (user.items && user.items.pets && user.items.pets['Turkey-Base']) {
updateOp.$set['items.mounts.Turkey-Base'] = true;
updateOp.$push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_harvestfeast_base_mount',
title: 'Happy Harvest Feast!',
text: 'Gobble gobble, you\'ve received the Turkey Mount!',
destination: '/inventory/stable',
},
seen: false,
},
};
} else {
updateOp.$set['items.pets.Turkey-Base'] = 5;
updateOp.$push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_harvestfeast_base_pet',
title: 'Happy Harvest Feast!',
text: 'Gobble gobble, you\'ve received the Turkey Pet!',
destination: '/inventory/stable',
},
seen: false,
},
};
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return User.updateOne({ _id: user._id }, updateOp).exec();
}
export default async function processUsers () {
const query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2024-10-20') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({ _id: 1 })
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
}

View File

@@ -1,14 +1,12 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20230731_naming_day';
import { v4 as uuid } from 'uuid';
import { model as User } from '../../../website/server/models/user';
import { model as User } from '../../website/server/models/user';
const MIGRATION_NAME = '20240731_naming_day';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
count += 1;
let set;
let push;
@@ -115,16 +113,16 @@ async function updateUser (user) {
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
if (push) {
return await user.updateOne({ $set: set, $inc: inc, $push: push }).exec();
} else {
return await user.updateOne({ $set: set, $inc: inc }).exec();
return user.updateOne({ $set: set, $inc: inc, $push: push }).exec();
}
return user.updateOne({ $set: set, $inc: inc }).exec();
}
export default async function processUsers () {
let query = {
const query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2023-07-01') },
'auth.timestamps.loggedin': { $gt: new Date('2024-07-01') },
};
const fields = {
@@ -136,7 +134,7 @@ export default async function processUsers () {
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.sort({ _id: 1 })
.select(fields)
.exec();
@@ -152,4 +150,4 @@ export default async function processUsers () {
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};
}

125
migrations/users/nye.js Normal file
View File

@@ -0,0 +1,125 @@
/* eslint-disable no-console */
import { model as User } from '../../website/server/models/user';
const MIGRATION_NAME = '20231228_nye';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count += 1;
const updateOp = {
$set: { migration: MIGRATION_NAME },
$push: { },
};
const data = {
title: 'Happy New Year!',
destination: '/inventory/equipment',
};
if (typeof user.items.gear.owned.head_special_nye2023 !== 'undefined') {
updateOp.$inc = {
'items.food.Candy_Skeleton': 1,
'items.food.Candy_Base': 1,
'items.food.Candy_CottonCandyBlue': 1,
'items.food.Candy_CottonCandyPink': 1,
'items.food.Candy_Shade': 1,
'items.food.Candy_White': 1,
'items.food.Candy_Golden': 1,
'items.food.Candy_Zombie': 1,
'items.food.Candy_Desert': 1,
'items.food.Candy_Red': 1,
};
data.icon = 'notif_candy_nye';
data.text = 'Youve received an assortment of candy to celebrate with your Pets!';
data.destination = '/inventory/stable';
} else if (typeof user.items.gear.owned.head_special_nye2022 !== 'undefined') {
updateOp.$set['items.gear.owned.head_special_nye2023'] = true;
data.icon = 'notif_2023hat_nye';
data.text = 'Take on your resolutions with style in this Ludicrous Party Hat!';
} else if (typeof user.items.gear.owned.head_special_nye2021 !== 'undefined') {
updateOp.$set['items.gear.owned.head_special_nye2022'] = true;
data.icon = 'notif_2022hat_nye';
data.text = 'Take on your resolutions with style in this Fabulous Party Hat!';
} else if (typeof user.items.gear.owned.head_special_nye2020 !== 'undefined') {
updateOp.$set['items.gear.owned.head_special_nye2021'] = true;
data.icon = 'notif_2021hat_nye';
data.text = 'Take on your resolutions with style in this Preposterous Party Hat!';
} else if (typeof user.items.gear.owned.head_special_nye2019 !== 'undefined') {
updateOp.$set['items.gear.owned.head_special_nye2020'] = true;
data.icon = 'notif_2020hat_nye';
data.text = 'Take on your resolutions with style in this Extravagant Party Hat!';
} else if (typeof user.items.gear.owned.head_special_nye2018 !== 'undefined') {
updateOp.$set['items.gear.owned.head_special_nye2019'] = true;
data.icon = 'notif_2019hat_nye';
data.text = 'Take on your resolutions with style in this Outrageous Party Hat!';
} else if (typeof user.items.gear.owned.head_special_nye2017 !== 'undefined') {
updateOp.$set['items.gear.owned.head_special_nye2018'] = true;
data.icon = 'notif_2018hat_nye';
data.text = 'Take on your resolutions with style in this Outlandish Party Hat!';
} else if (typeof user.items.gear.owned.head_special_nye2016 !== 'undefined') {
updateOp.$set['items.gear.owned.head_special_nye2017'] = true;
data.icon = 'notif_2017hat_nye';
data.text = 'Take on your resolutions with style in this Fanciful Party Hat!';
} else if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') {
updateOp.$set['items.gear.owned.head_special_nye2016'] = true;
data.icon = 'notif_2016hat_nye';
data.text = 'Take on your resolutions with style in this Whimsical Party Hat!';
} else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') {
updateOp.$set['items.gear.owned.head_special_nye2015'] = true;
data.icon = 'notif_2015hat_nye';
data.text = 'Take on your resolutions with style in this Ridiculous Party Hat!';
} else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') {
updateOp.$set['items.gear.owned.head_special_nye2014'] = true;
data.icon = 'notif_2014hat_nye';
data.text = 'Take on your resolutions with style in this Silly Party Hat!';
} else {
updateOp.$set['items.gear.owned.head_special_nye'] = true;
data.icon = 'notif_2013hat_nye';
data.text = 'Take on your resolutions with style in this Absurd Party Hat!';
}
updateOp.$push.notifications = {
type: 'ITEM_RECEIVED',
data,
seen: false,
};
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return User.updateOne({ _id: user._id }, updateOp).exec();
}
export default async function processUsers () {
const query = {
'auth.timestamps.loggedin': { $gt: new Date('2023-12-01') },
migration: { $ne: MIGRATION_NAME },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({ _id: 1 })
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
}

1131
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "5.26.0",
"version": "5.32.5",
"main": "./website/server/index.js",
"dependencies": {
"@babel/core": "^7.22.10",
@@ -15,8 +15,9 @@
"amplitude": "^6.0.0",
"apidoc": "^0.54.0",
"apple-auth": "^1.0.9",
"babel-preset-env": "^1.7.0",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"body-parser": "^1.20.3",
"bootstrap": "^4.6.2",
"compression": "^1.7.4",
"cookie-session": "^2.0.0",
@@ -27,7 +28,7 @@
"eslint": "^8.55.0",
"eslint-config-habitrpg": "^6.2.3",
"eslint-plugin-mocha": "^5.0.0",
"express": "^4.19.2",
"express": "^4.21.1",
"express-basic-auth": "^1.2.1",
"express-validator": "^5.2.0",
"firebase-admin": "^12.1.1",
@@ -35,6 +36,7 @@
"got": "^11.8.6",
"gulp": "^4.0.0",
"gulp-babel": "^8.0.0",
"gulp-filter": "^7.0.0",
"gulp-imagemin": "^7.1.0",
"gulp-nodemon": "^2.5.0",
"gulp.spritesmith": "^6.13.0",
@@ -49,7 +51,7 @@
"method-override": "^3.0.0",
"moment": "^2.29.4",
"moment-recur": "^1.0.7",
"mongoose": "^7.6.3",
"mongoose": "^7.8.3",
"morgan": "^1.10.0",
"nconf": "^0.12.1",
"node-gcm": "^1.0.5",
@@ -74,6 +76,7 @@
"useragent": "^2.1.9",
"uuid": "^9.0.0",
"validator": "^13.11.0",
"webpack-bundle-analyzer": "^4.10.2",
"winston": "^3.10.0",
"winston-loggly-bulk": "^3.3.0",
"xml2js": "^0.6.2"
@@ -104,19 +107,21 @@
"client:build": "cd website/client && npm run build",
"client:unit": "cd website/client && npm run test:unit",
"start": "gulp nodemon",
"start:simple": "node ./website/server/index.js",
"debug": "gulp nodemon --inspect",
"mongo:dev": "run-rs -v 5.0.23 -l ubuntu1804 --keep --dbpath mongodb-data --number 1 --quiet",
"mongo:test": "run-rs -v 5.0.23 -l ubuntu1804 --keep --dbpath mongodb-data-testing --number 1 --quiet",
"postinstall": "git config --global url.\"https://\".insteadOf git:// && gulp build && cd website/client && npm install",
"apidoc": "gulp apidoc",
"heroku-postbuild": "npm run client:build"
"heroku-postbuild": ".heroku/report_deploy.sh"
},
"devDependencies": {
"axios": "^1.4.0",
"axios": "^1.7.4",
"chai": "^4.3.7",
"chai-as-promised": "^7.1.1",
"chai-moment": "^0.1.0",
"chalk": "^5.3.0",
"cross-spawn": "^7.0.3",
"cross-spawn": "^7.0.5",
"mocha": "^5.1.1",
"monk": "^7.3.4",
"nyc": "^15.1.0",

View File

@@ -44,7 +44,6 @@ describe('bug-report', () => {
USER_HOURGLASSES: 0,
USER_ID: userId,
USER_LEVEL: 1,
USER_OFFSET_MONTHS: 0,
USER_PAYMENT_PLATFORM: undefined,
USER_SUBSCRIPTION: undefined,
USER_TIMEZONE_OFFSET: 0,

View File

@@ -154,6 +154,14 @@ describe('cron', async () => {
expect(user.purchased.plan.consecutive.count).to.equal(1);
});
it('increments plan.cumulativeCount', async () => {
user.purchased.plan.cumulativeCount = 0;
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.purchased.plan.cumulativeCount).to.equal(1);
});
it('increments plan.consecutive.count by more than 1 if user skipped months between logins', async () => {
user.purchased.plan.dateUpdated = moment().subtract(2, 'months').toDate();
user.purchased.plan.consecutive.count = 0;
@@ -163,12 +171,13 @@ describe('cron', async () => {
expect(user.purchased.plan.consecutive.count).to.equal(2);
});
it('decrements plan.consecutive.offset when offset is greater than 0', async () => {
user.purchased.plan.consecutive.offset = 2;
it('increments plan.cumulativeCount by more than 1 if user skipped months between logins', async () => {
user.purchased.plan.dateUpdated = moment().subtract(3, 'months').toDate();
user.purchased.plan.cumulativeCount = 0;
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.purchased.plan.consecutive.offset).to.equal(1);
expect(user.purchased.plan.cumulativeCount).to.equal(3);
});
it('does not award unearned plan.consecutive.trinkets if subscription ended during an absence', async () => {
@@ -185,12 +194,12 @@ describe('cron', async () => {
});
it('does not increment plan.consecutive.gemCapExtra when user has reached the gemCap limit', async () => {
user.purchased.plan.consecutive.gemCapExtra = 25;
user.purchased.plan.consecutive.gemCapExtra = 26;
user.purchased.plan.consecutive.count = 5;
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(25);
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(26);
});
it('does not reset plan stats if we are before the last day of the cancelled month', async () => {
@@ -205,16 +214,14 @@ describe('cron', async () => {
user.purchased.plan.dateTerminated = moment(new Date()).subtract({ days: 1 });
user.purchased.plan.consecutive.gemCapExtra = 20;
user.purchased.plan.consecutive.count = 5;
user.purchased.plan.consecutive.offset = 1;
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.purchased.plan.customerId).to.not.exist;
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(0);
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(20);
expect(user.purchased.plan.consecutive.count).to.equal(0);
expect(user.purchased.plan.consecutive.offset).to.equal(0);
});
describe('for a 1-month recurring subscription', async () => {
@@ -236,13 +243,11 @@ describe('cron', async () => {
user1.purchased.plan.dateUpdated = moment().toDate();
user1.purchased.plan.planId = 'basic';
user1.purchased.plan.consecutive.count = 0;
user1.purchased.plan.perkMonthCount = 0;
user1.purchased.plan.consecutive.offset = 0;
user1.purchased.plan.consecutive.trinkets = 0;
user1.purchased.plan.consecutive.trinkets = 1;
user1.purchased.plan.consecutive.gemCapExtra = 0;
});
it('does not increment consecutive benefits after the first month', async () => {
it('increments consecutive benefits', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months')
.add(2, 'days')
.toDate());
@@ -253,75 +258,8 @@ describe('cron', async () => {
user: user1, tasksByType, daysMissed, analytics,
});
expect(user1.purchased.plan.consecutive.count).to.equal(1);
expect(user1.purchased.plan.consecutive.offset).to.equal(0);
expect(user1.purchased.plan.consecutive.trinkets).to.equal(0);
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(0);
});
it('does not increment consecutive benefits after the second month', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months')
.add(2, 'days')
.toDate());
// Add 1 month to simulate what happens a month after the subscription was created.
// Add 2 days so that we're sure we're not affected by any start-of-month effects
// e.g., from time zone oddness.
await cron({
user: user1, tasksByType, daysMissed, analytics,
});
expect(user1.purchased.plan.consecutive.count).to.equal(2);
expect(user1.purchased.plan.consecutive.offset).to.equal(0);
expect(user1.purchased.plan.consecutive.trinkets).to.equal(0);
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(0);
});
it('increments consecutive benefits after the second month if they also received a 1 month gift subscription', async () => {
user1.purchased.plan.perkMonthCount = 1;
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months')
.add(2, 'days')
.toDate());
// Add 1 month to simulate what happens a month after the subscription was created.
// Add 2 days so that we're sure we're not affected by any start-of-month effects
// e.g., from time zone oddness.
await cron({
user: user1, tasksByType, daysMissed, analytics,
});
expect(user1.purchased.plan.perkMonthCount).to.equal(0);
expect(user1.purchased.plan.consecutive.count).to.equal(2);
expect(user1.purchased.plan.consecutive.offset).to.equal(0);
expect(user1.purchased.plan.consecutive.trinkets).to.equal(1);
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(5);
});
it('increments consecutive benefits after the third month', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(3, 'months')
.add(2, 'days')
.toDate());
// Add 1 month to simulate what happens a month after the subscription was created.
// Add 2 days so that we're sure we're not affected by any start-of-month effects
// e.g., from time zone oddness.
await cron({
user: user1, tasksByType, daysMissed, analytics,
});
expect(user1.purchased.plan.consecutive.count).to.equal(3);
expect(user1.purchased.plan.consecutive.offset).to.equal(0);
expect(user1.purchased.plan.consecutive.trinkets).to.equal(1);
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(5);
});
it('does not increment consecutive benefits after the fourth month', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(4, 'months')
.add(2, 'days')
.toDate());
// Add 1 month to simulate what happens a month after the subscription was created.
// Add 2 days so that we're sure we're not affected by any start-of-month effects
// e.g., from time zone oddness.
await cron({
user: user1, tasksByType, daysMissed, analytics,
});
expect(user1.purchased.plan.consecutive.count).to.equal(4);
expect(user1.purchased.plan.consecutive.offset).to.equal(0);
expect(user1.purchased.plan.consecutive.trinkets).to.equal(1);
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(5);
expect(user1.purchased.plan.consecutive.trinkets).to.equal(2);
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(2);
});
it('increments consecutive benefits correctly if user has been absent with continuous subscription', async () => {
@@ -332,33 +270,8 @@ describe('cron', async () => {
user: user1, tasksByType, daysMissed, analytics,
});
expect(user1.purchased.plan.consecutive.count).to.equal(10);
expect(user1.purchased.plan.consecutive.offset).to.equal(0);
expect(user1.purchased.plan.consecutive.trinkets).to.equal(3);
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(15);
});
it('initializes plan.perkMonthCount if necessary', async () => {
user.purchased.plan.perkMonthCount = undefined;
clock = sinon.useFakeTimers(moment(user.purchased.plan.dateUpdated)
.utcOffset(0)
.startOf('month')
.add(1, 'months')
.add(2, 'days')
.toDate());
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.purchased.plan.perkMonthCount).to.equal(1);
user.purchased.plan.perkMonthCount = undefined;
user.purchased.plan.consecutive.count = 8;
clock.restore();
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months')
.add(2, 'days')
.toDate());
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.purchased.plan.perkMonthCount).to.equal(2);
expect(user1.purchased.plan.consecutive.trinkets).to.equal(11);
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(20);
});
});
@@ -379,14 +292,12 @@ describe('cron', async () => {
user3.purchased.plan.customerId = 'subscribedId';
user3.purchased.plan.dateUpdated = moment().toDate();
user3.purchased.plan.planId = 'basic_3mo';
user3.purchased.plan.perkMonthCount = 0;
user3.purchased.plan.consecutive.count = 0;
user3.purchased.plan.consecutive.offset = 3;
user3.purchased.plan.consecutive.trinkets = 1;
user3.purchased.plan.consecutive.gemCapExtra = 5;
user3.purchased.plan.consecutive.gemCapExtra = 0;
});
it('does not increment consecutive benefits in the first month of the first paid period that they already have benefits for', async () => {
it('increments consecutive benefits', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months')
.add(2, 'days')
.toDate());
@@ -394,102 +305,8 @@ describe('cron', async () => {
user: user3, tasksByType, daysMissed, analytics,
});
expect(user3.purchased.plan.consecutive.count).to.equal(1);
expect(user3.purchased.plan.consecutive.offset).to.equal(2);
expect(user3.purchased.plan.consecutive.trinkets).to.equal(1);
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(5);
});
it('does not increment consecutive benefits in the middle of the period that they already have benefits for', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user3, tasksByType, daysMissed, analytics,
});
expect(user3.purchased.plan.consecutive.count).to.equal(2);
expect(user3.purchased.plan.consecutive.offset).to.equal(1);
expect(user3.purchased.plan.consecutive.trinkets).to.equal(1);
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(5);
});
it('does not increment consecutive benefits in the final month of the period that they already have benefits for', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(3, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user3, tasksByType, daysMissed, analytics,
});
expect(user3.purchased.plan.consecutive.count).to.equal(3);
expect(user3.purchased.plan.consecutive.offset).to.equal(0);
expect(user3.purchased.plan.consecutive.trinkets).to.equal(1);
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(5);
});
it('increments consecutive benefits the month after the second paid period has started', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(4, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user3, tasksByType, daysMissed, analytics,
});
expect(user3.purchased.plan.consecutive.count).to.equal(4);
expect(user3.purchased.plan.consecutive.offset).to.equal(2);
expect(user3.purchased.plan.consecutive.trinkets).to.equal(2);
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10);
});
it('keeps existing plan.perkMonthCount intact when incrementing consecutive benefits', async () => {
user3.purchased.plan.perkMonthCount = 2;
user3.purchased.plan.consecutive.trinkets = 1;
user3.purchased.plan.consecutive.gemCapExtra = 5;
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(4, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user3, tasksByType, daysMissed, analytics,
});
expect(user3.purchased.plan.perkMonthCount).to.equal(2);
expect(user3.purchased.plan.consecutive.trinkets).to.equal(2);
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10);
});
it('does not increment consecutive benefits in the second month of the second period that they already have benefits for', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(5, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user3, tasksByType, daysMissed, analytics,
});
expect(user3.purchased.plan.consecutive.count).to.equal(5);
expect(user3.purchased.plan.consecutive.offset).to.equal(1);
expect(user3.purchased.plan.consecutive.trinkets).to.equal(2);
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10);
});
it('does not increment consecutive benefits in the final month of the second period that they already have benefits for', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(6, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user3, tasksByType, daysMissed, analytics,
});
expect(user3.purchased.plan.consecutive.count).to.equal(6);
expect(user3.purchased.plan.consecutive.offset).to.equal(0);
expect(user3.purchased.plan.consecutive.trinkets).to.equal(2);
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10);
});
it('increments consecutive benefits the month after the third paid period has started', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(7, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user3, tasksByType, daysMissed, analytics,
});
expect(user3.purchased.plan.consecutive.count).to.equal(7);
expect(user3.purchased.plan.consecutive.offset).to.equal(2);
expect(user3.purchased.plan.consecutive.trinkets).to.equal(3);
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(15);
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(2);
});
it('increments consecutive benefits correctly if user has been absent with continuous subscription', async () => {
@@ -500,8 +317,7 @@ describe('cron', async () => {
user: user3, tasksByType, daysMissed, analytics,
});
expect(user3.purchased.plan.consecutive.count).to.equal(10);
expect(user3.purchased.plan.consecutive.offset).to.equal(2);
expect(user3.purchased.plan.consecutive.trinkets).to.equal(4);
expect(user3.purchased.plan.consecutive.trinkets).to.equal(11);
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(20);
});
});
@@ -523,14 +339,12 @@ describe('cron', async () => {
user6.purchased.plan.customerId = 'subscribedId';
user6.purchased.plan.dateUpdated = moment().toDate();
user6.purchased.plan.planId = 'google_6mo';
user6.purchased.plan.perkMonthCount = 0;
user6.purchased.plan.consecutive.count = 0;
user6.purchased.plan.consecutive.offset = 6;
user6.purchased.plan.consecutive.trinkets = 2;
user6.purchased.plan.consecutive.gemCapExtra = 10;
user6.purchased.plan.consecutive.trinkets = 1;
user6.purchased.plan.consecutive.gemCapExtra = 0;
});
it('does not increment consecutive benefits in the first month of the first paid period that they already have benefits for', async () => {
it('increments benefits', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months')
.add(2, 'days')
.toDate());
@@ -538,74 +352,8 @@ describe('cron', async () => {
user: user6, tasksByType, daysMissed, analytics,
});
expect(user6.purchased.plan.consecutive.count).to.equal(1);
expect(user6.purchased.plan.consecutive.offset).to.equal(5);
expect(user6.purchased.plan.consecutive.trinkets).to.equal(2);
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(10);
});
it('does not increment consecutive benefits in the final month of the period that they already have benefits for', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(6, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user6, tasksByType, daysMissed, analytics,
});
expect(user6.purchased.plan.consecutive.count).to.equal(6);
expect(user6.purchased.plan.consecutive.offset).to.equal(0);
expect(user6.purchased.plan.consecutive.trinkets).to.equal(2);
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(10);
});
it('increments consecutive benefits the month after the second paid period has started', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(7, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user6, tasksByType, daysMissed, analytics,
});
expect(user6.purchased.plan.consecutive.count).to.equal(7);
expect(user6.purchased.plan.consecutive.offset).to.equal(5);
expect(user6.purchased.plan.consecutive.trinkets).to.equal(4);
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(20);
});
it('keeps existing plan.perkMonthCount intact when incrementing consecutive benefits', async () => {
user6.purchased.plan.perkMonthCount = 2;
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(7, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user6, tasksByType, daysMissed, analytics,
});
expect(user6.purchased.plan.perkMonthCount).to.equal(2);
expect(user6.purchased.plan.consecutive.trinkets).to.equal(4);
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(20);
});
it('increments consecutive benefits the month after the third paid period has started', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(13, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user6, tasksByType, daysMissed, analytics,
});
expect(user6.purchased.plan.consecutive.count).to.equal(13);
expect(user6.purchased.plan.consecutive.offset).to.equal(5);
expect(user6.purchased.plan.consecutive.trinkets).to.equal(6);
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(25);
});
it('increments consecutive benefits correctly if user has been absent with continuous subscription', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(19, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user6, tasksByType, daysMissed, analytics,
});
expect(user6.purchased.plan.consecutive.count).to.equal(19);
expect(user6.purchased.plan.consecutive.offset).to.equal(5);
expect(user6.purchased.plan.consecutive.trinkets).to.equal(8);
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(25);
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(2);
});
});
@@ -626,11 +374,10 @@ describe('cron', async () => {
user12.purchased.plan.dateUpdated = moment().toDate();
user12.purchased.plan.planId = 'basic_12mo';
user12.purchased.plan.consecutive.count = 0;
user12.purchased.plan.consecutive.offset = 12;
user12.purchased.plan.consecutive.trinkets = 4;
user12.purchased.plan.consecutive.gemCapExtra = 20;
user12.purchased.plan.consecutive.trinkets = 1;
user12.purchased.plan.consecutive.gemCapExtra = 26;
it('does not increment consecutive benefits in the first month of the first paid period that they already have benefits for', async () => {
it('increments consecutive benefits the month after the second paid period has started', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months')
.add(2, 'days')
.toDate());
@@ -638,61 +385,20 @@ describe('cron', async () => {
user: user12, tasksByType, daysMissed, analytics,
});
expect(user12.purchased.plan.consecutive.count).to.equal(1);
expect(user12.purchased.plan.consecutive.offset).to.equal(11);
expect(user12.purchased.plan.consecutive.trinkets).to.equal(4);
expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(20);
});
it('does not increment consecutive benefits in the final month of the period that they already have benefits for', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(12, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user12, tasksByType, daysMissed, analytics,
});
expect(user12.purchased.plan.consecutive.count).to.equal(12);
expect(user12.purchased.plan.consecutive.offset).to.equal(0);
expect(user12.purchased.plan.consecutive.trinkets).to.equal(4);
expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(20);
});
it('increments consecutive benefits the month after the second paid period has started', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(13, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user12, tasksByType, daysMissed, analytics,
});
expect(user12.purchased.plan.consecutive.count).to.equal(13);
expect(user12.purchased.plan.consecutive.offset).to.equal(11);
expect(user12.purchased.plan.consecutive.trinkets).to.equal(8);
expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(25);
});
it('increments consecutive benefits the month after the third paid period has started', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(25, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user12, tasksByType, daysMissed, analytics,
});
expect(user12.purchased.plan.consecutive.count).to.equal(25);
expect(user12.purchased.plan.consecutive.offset).to.equal(11);
expect(user12.purchased.plan.consecutive.trinkets).to.equal(12);
expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(25);
expect(user12.purchased.plan.consecutive.trinkets).to.equal(2);
expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(26);
});
it('increments consecutive benefits correctly if user has been absent with continuous subscription', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(37, 'months')
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(10, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user12, tasksByType, daysMissed, analytics,
});
expect(user12.purchased.plan.consecutive.count).to.equal(37);
expect(user12.purchased.plan.consecutive.offset).to.equal(11);
expect(user12.purchased.plan.consecutive.trinkets).to.equal(16);
expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(25);
expect(user12.purchased.plan.consecutive.count).to.equal(10);
expect(user12.purchased.plan.consecutive.trinkets).to.equal(11);
expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(26);
});
});
@@ -715,11 +421,11 @@ describe('cron', async () => {
.toDate();
user3g.purchased.plan.planId = null;
user3g.purchased.plan.consecutive.count = 0;
user3g.purchased.plan.consecutive.offset = 3;
user3g.purchased.plan.cumulativeCount = 0;
user3g.purchased.plan.consecutive.trinkets = 1;
user3g.purchased.plan.consecutive.gemCapExtra = 5;
user3g.purchased.plan.consecutive.gemCapExtra = 0;
it('does not increment consecutive benefits in the first month of the gift subscription', async () => {
it('increments benefits', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months')
.add(2, 'days')
.toDate());
@@ -727,35 +433,9 @@ describe('cron', async () => {
user: user3g, tasksByType, daysMissed, analytics,
});
expect(user3g.purchased.plan.consecutive.count).to.equal(1);
expect(user3g.purchased.plan.consecutive.offset).to.equal(2);
expect(user3g.purchased.plan.consecutive.trinkets).to.equal(1);
expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(5);
});
it('does not increment consecutive benefits in the second month of the gift subscription', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user3g, tasksByType, daysMissed, analytics,
});
expect(user3g.purchased.plan.consecutive.count).to.equal(2);
expect(user3g.purchased.plan.consecutive.offset).to.equal(1);
expect(user3g.purchased.plan.consecutive.trinkets).to.equal(1);
expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(5);
});
it('does not increment consecutive benefits in the third month of the gift subscription', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(3, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user3g, tasksByType, daysMissed, analytics,
});
expect(user3g.purchased.plan.consecutive.count).to.equal(3);
expect(user3g.purchased.plan.consecutive.offset).to.equal(0);
expect(user3g.purchased.plan.consecutive.trinkets).to.equal(1);
expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(5);
expect(user3g.purchased.plan.cumulativeCount).to.equal(1);
expect(user3g.purchased.plan.consecutive.trinkets).to.equal(2);
expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(2);
});
it('does not increment consecutive benefits in the month after the gift subscription has ended', async () => {
@@ -767,84 +447,9 @@ describe('cron', async () => {
});
// subscription has been erased by now
expect(user3g.purchased.plan.consecutive.count).to.equal(0);
expect(user3g.purchased.plan.consecutive.offset).to.equal(0);
expect(user3g.purchased.plan.consecutive.trinkets).to.equal(1);
expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(0); // erased
});
});
describe('for a 6-month recurring subscription where the user has incorrect consecutive month data from prior bugs', async () => {
const user6x = new User({
auth: {
local: {
username: 'username6x',
lowerCaseUsername: 'username6x',
email: 'email6x@example.com',
salt: 'salt',
hashed_password: 'hashed_password', // eslint-disable-line camelcase
},
},
});
// user6x has a 6-month recurring subscription starting 8 months in the past
// before issue #4819 was fixed
user6x.purchased.plan.customerId = 'subscribedId';
user6x.purchased.plan.dateUpdated = moment().toDate();
user6x.purchased.plan.planId = 'basic_6mo';
user6x.purchased.plan.consecutive.count = 8;
user6x.purchased.plan.consecutive.offset = 0;
user6x.purchased.plan.consecutive.trinkets = 3;
user6x.purchased.plan.consecutive.gemCapExtra = 15;
it('increments consecutive benefits in the first month since the fix for #4819 goes live', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user6x, tasksByType, daysMissed, analytics,
});
expect(user6x.purchased.plan.consecutive.count).to.equal(9);
expect(user6x.purchased.plan.consecutive.offset).to.equal(5);
expect(user6x.purchased.plan.consecutive.trinkets).to.equal(5);
expect(user6x.purchased.plan.consecutive.gemCapExtra).to.equal(25);
});
it('does not increment consecutive benefits in the second month after the fix goes live', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user6x, tasksByType, daysMissed, analytics,
});
expect(user6x.purchased.plan.consecutive.count).to.equal(10);
expect(user6x.purchased.plan.consecutive.offset).to.equal(4);
expect(user6x.purchased.plan.consecutive.trinkets).to.equal(5);
expect(user6x.purchased.plan.consecutive.gemCapExtra).to.equal(25);
});
it('does not increment consecutive benefits in the third month after the fix goes live', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(3, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user6x, tasksByType, daysMissed, analytics,
});
expect(user6x.purchased.plan.consecutive.count).to.equal(11);
expect(user6x.purchased.plan.consecutive.offset).to.equal(3);
expect(user6x.purchased.plan.consecutive.trinkets).to.equal(5);
expect(user6x.purchased.plan.consecutive.gemCapExtra).to.equal(25);
});
it('increments consecutive benefits in the seventh month after the fix goes live', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(7, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user6x, tasksByType, daysMissed, analytics,
});
expect(user6x.purchased.plan.consecutive.count).to.equal(15);
expect(user6x.purchased.plan.consecutive.offset).to.equal(5);
expect(user6x.purchased.plan.consecutive.trinkets).to.equal(7);
expect(user6x.purchased.plan.consecutive.gemCapExtra).to.equal(25);
expect(user3g.purchased.plan.consecutive.trinkets).to.equal(2);
expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(2);
expect(user3g.purchased.plan.cumulativeCount).to.equal(1);
});
});
});
@@ -888,12 +493,12 @@ describe('cron', async () => {
expect(user.purchased.plan.consecutive.count).to.equal(0);
});
it('does not decrement plan.consecutive.offset when offset is greater than 0', async () => {
user.purchased.plan.consecutive.offset = 1;
it('does not increment plan.cumulativeCount', async () => {
user.purchased.plan.cumulativeCount = 0;
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.purchased.plan.consecutive.offset).to.equal(1);
expect(user.purchased.plan.cumulativeCount).to.equal(0);
});
it('does not increment plan.consecutive.trinkets when user has reached a month that is a multiple of 3', async () => {
@@ -913,12 +518,12 @@ describe('cron', async () => {
});
it('does not increment plan.consecutive.gemCapExtra when user has reached the gemCap limit', async () => {
user.purchased.plan.consecutive.gemCapExtra = 25;
user.purchased.plan.consecutive.gemCapExtra = 26;
user.purchased.plan.consecutive.count = 5;
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(25);
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(26);
});
it('does nothing to plan stats if we are before the last day of the cancelled month', async () => {
@@ -928,22 +533,6 @@ describe('cron', async () => {
});
expect(user.purchased.plan.customerId).to.not.exist;
});
xit('does nothing to plan stats when we are after the last day of the cancelled month', async () => {
user.purchased.plan.dateTerminated = moment(new Date()).subtract({ days: 1 });
user.purchased.plan.consecutive.gemCapExtra = 20;
user.purchased.plan.consecutive.count = 5;
user.purchased.plan.consecutive.offset = 1;
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.purchased.plan.customerId).to.exist;
expect(user.purchased.plan.consecutive.gemCapExtra).to.exist;
expect(user.purchased.plan.consecutive.count).to.exist;
expect(user.purchased.plan.consecutive.offset).to.exist;
});
});
describe('todos', async () => {

View File

@@ -715,7 +715,7 @@ describe('Purchasing a group plan for group', () => {
const mysteryItem = { title: 'item' };
const mysteryItems = [mysteryItem];
const consecutive = {
trinkets: 3,
trinkets: 4,
gemCapExtra: 20,
offset: 1,
count: 13,

View File

@@ -12,6 +12,7 @@ import {
} from '../../../../helpers/api-unit.helper';
import * as worldState from '../../../../../website/server/libs/worldState';
import { TransactionModel } from '../../../../../website/server/models/transaction';
import { REPEATING_EVENTS } from '../../../../../website/common/script/content/constants/events';
describe('payments/index', () => {
let user;
@@ -65,7 +66,6 @@ describe('payments/index', () => {
mysteryItems: [],
consecutive: {
trinkets: 0,
offset: 0,
gemCapExtra: 0,
},
};
@@ -108,14 +108,8 @@ describe('payments/index', () => {
});
it('add a transaction entry to the recipient', async () => {
recipient.purchased.plan = plan;
expect(recipient.purchased.plan.extraMonths).to.eql(0);
await api.createSubscription(data);
expect(recipient.purchased.plan.extraMonths).to.eql(3);
const transactions = await TransactionModel
.find({ userId: recipient._id })
.sort({ createdAt: -1 })
@@ -177,6 +171,45 @@ describe('payments/index', () => {
expect(recipient.purchased.plan.dateUpdated).to.exist;
});
it('does not reset gemCapExtra if they already had one', async () => {
recipient.purchased.plan.consecutive.gemCapExtra = 10;
await api.createSubscription(data);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(10);
});
it('sets gemCapExtra to 0 if they receive a 3 month sub', async () => {
data.gift.subscription.key = 'basic_3mo';
data.gift.subscription.months = 3;
await api.createSubscription(data);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
});
it('sets gemCapExtra to max if they receive a 12 month sub', async () => {
recipient.purchased.plan.consecutive.gemCapExtra = 10;
data.gift.subscription.key = 'basic_12mo';
data.gift.subscription.months = 12;
await api.createSubscription(data);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(26);
});
it('gives user 1 hourglass if they have no active subscription', async () => {
await api.createSubscription(data);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
});
it('does not give any hourglasses if they have an active subscription', async () => {
recipient.purchased.plan = plan;
await api.createSubscription(data);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(plan.consecutive.trinkets);
});
it('sets plan.dateUpdated if it did exist but the user has cancelled', async () => {
recipient.purchased.plan.dateUpdated = moment().subtract(1, 'days').toDate();
recipient.purchased.plan.dateTerminated = moment().subtract(1, 'days').toDate();
@@ -235,116 +268,6 @@ describe('payments/index', () => {
expect(recipient.purchased.plan.customerId).to.eql('customer-id');
});
it('sets plan.perkMonthCount to 1 if user is not subscribed', async () => {
recipient.purchased.plan = plan;
recipient.purchased.plan.perkMonthCount = 1;
recipient.purchased.plan.customerId = undefined;
data.sub.key = 'basic_earned';
data.gift.subscription.key = 'basic_earned';
data.gift.subscription.months = 1;
expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
});
it('sets plan.perkMonthCount to 1 if field is not initialized', async () => {
recipient.purchased.plan = plan;
recipient.purchased.plan.perkMonthCount = -1;
recipient.purchased.plan.customerId = undefined;
data.sub.key = 'basic_earned';
data.gift.subscription.key = 'basic_earned';
data.gift.subscription.months = 1;
expect(recipient.purchased.plan.perkMonthCount).to.eql(-1);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
});
it('sets plan.perkMonthCount to 1 if user had previous count but lapsed subscription', async () => {
recipient.purchased.plan = plan;
recipient.purchased.plan.perkMonthCount = 2;
recipient.purchased.plan.customerId = undefined;
data.sub.key = 'basic_earned';
data.gift.subscription.key = 'basic_earned';
data.gift.subscription.months = 1;
expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
});
it('adds to plan.perkMonthCount if user is already subscribed', async () => {
recipient.purchased.plan = plan;
recipient.purchased.plan.perkMonthCount = 1;
data.sub.key = 'basic_earned';
data.gift.subscription.key = 'basic_earned';
data.gift.subscription.months = 1;
expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
});
it('awards perks if plan.perkMonthCount reaches 3 with existing subscription', async () => {
recipient.purchased.plan = plan;
recipient.purchased.plan.perkMonthCount = 2;
data.sub.key = 'basic_earned';
data.gift.subscription.key = 'basic_earned';
data.gift.subscription.months = 1;
expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(0);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5);
});
it('awards perks if plan.perkMonthCount reaches 3 without existing subscription', async () => {
recipient.purchased.plan.perkMonthCount = 0;
expect(recipient.purchased.plan.perkMonthCount).to.eql(0);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(0);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5);
});
it('awards perks if plan.perkMonthCount reaches 3 without initialized field', async () => {
expect(recipient.purchased.plan.perkMonthCount).to.eql(-1);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(0);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5);
});
it('awards perks if plan.perkMonthCount goes over 3', async () => {
recipient.purchased.plan = plan;
recipient.purchased.plan.perkMonthCount = 2;
data.sub.key = 'basic_earned';
expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5);
});
it('sets plan.customerId to "Gift" if it does not already exist', async () => {
expect(recipient.purchased.plan.customerId).to.not.exist;
@@ -421,8 +344,8 @@ describe('payments/index', () => {
context('Active Promotion', () => {
beforeEach(() => {
sinon.stub(worldState, 'getCurrentEventList').returns([{
...common.content.events.winter2021Promo,
event: 'winter2021',
...REPEATING_EVENTS.giftOneGetOne,
event: 'g1g1',
}]);
});
@@ -438,22 +361,30 @@ describe('payments/index', () => {
expect(user.purchased.plan.dateTerminated).to.exist;
expect(user.purchased.plan.dateUpdated).to.exist;
expect(user.purchased.plan.dateCreated).to.exist;
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
expect(recipient.items.pets['Jackalope-RoyalPurple']).to.eql(5);
expect(recipient.purchased.plan.customerId).to.eql('Gift');
expect(recipient.purchased.plan.dateTerminated).to.exist;
expect(recipient.purchased.plan.dateUpdated).to.exist;
expect(recipient.purchased.plan.dateCreated).to.exist;
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
});
it('adds extraMonths to existing subscription for purchaser and creates a gift subscription for recipient without sub', async () => {
user.purchased.plan = plan;
expect(user.purchased.plan.extraMonths).to.eql(0);
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
await api.createSubscription(data);
expect(user.purchased.plan.extraMonths).to.eql(3);
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
expect(recipient.items.pets['Jackalope-RoyalPurple']).to.eql(5);
expect(recipient.purchased.plan.customerId).to.eql('Gift');
@@ -466,10 +397,12 @@ describe('payments/index', () => {
recipient.purchased.plan = plan;
expect(recipient.purchased.plan.extraMonths).to.eql(0);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
await api.createSubscription(data);
expect(recipient.purchased.plan.extraMonths).to.eql(3);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
expect(user.items.pets['Jackalope-RoyalPurple']).to.eql(5);
expect(user.purchased.plan.customerId).to.eql('Gift');
@@ -484,11 +417,15 @@ describe('payments/index', () => {
expect(user.purchased.plan.extraMonths).to.eql(0);
expect(recipient.purchased.plan.extraMonths).to.eql(0);
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
await api.createSubscription(data);
expect(user.purchased.plan.extraMonths).to.eql(3);
expect(recipient.purchased.plan.extraMonths).to.eql(3);
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
});
it('sends a private message about the promotion', async () => {
@@ -511,7 +448,6 @@ describe('payments/index', () => {
expect(user.purchased.plan.customerId).to.eql('customer-id');
expect(user.purchased.plan.dateUpdated).to.exist;
expect(user.purchased.plan.gemsBought).to.eql(0);
expect(user.purchased.plan.perkMonthCount).to.eql(0);
expect(user.purchased.plan.paymentMethod).to.eql('Payment Method');
expect(user.purchased.plan.extraMonths).to.eql(0);
expect(user.purchased.plan.dateTerminated).to.eql(null);
@@ -549,33 +485,6 @@ describe('payments/index', () => {
expect(user.purchased.plan.dateCurrentTypeCreated).to.not.eql(initialDate);
});
it('keeps plan.perkMonthCount when changing subscription type', async () => {
await api.createSubscription(data);
user.purchased.plan.perkMonthCount = 2;
await api.createSubscription(data);
expect(user.purchased.plan.perkMonthCount).to.eql(2);
});
it('sets plan.perkMonthCount to zero when creating new monthly subscription', async () => {
user.purchased.plan.perkMonthCount = 2;
await api.createSubscription(data);
expect(user.purchased.plan.perkMonthCount).to.eql(0);
});
it('sets plan.perkMonthCount to zero when creating new 3 month subscription', async () => {
user.purchased.plan.perkMonthCount = 2;
await api.createSubscription(data);
expect(user.purchased.plan.perkMonthCount).to.eql(0);
});
it('updates plan.consecutive.offset when changing subscription type', async () => {
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.offset).to.eql(3);
data.sub.key = 'basic_6mo';
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.offset).to.eql(6);
});
it('awards the Royal Purple Jackalope pet', async () => {
await api.createSubscription(data);
@@ -694,6 +603,7 @@ describe('payments/index', () => {
expect(user.purchased.plan.dateCreated).to.eql(created);
expect(user.purchased.plan.dateUpdated).to.not.eql(updated);
expect(user.purchased.plan.customerId).to.eql('customer-id');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26);
});
});
@@ -741,55 +651,20 @@ describe('payments/index', () => {
});
context('Block subscription perks', () => {
it('adds block months to plan.consecutive.offset', async () => {
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.offset).to.eql(3);
});
it('does not add to plans.consecutive.offset if 1 month subscription', async () => {
data.sub.key = 'basic_earned';
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.offset).to.eql(0);
});
it('resets plans.consecutive.offset if 1 month subscription', async () => {
user.purchased.plan.consecutive.offset = 1;
await user.save();
data.sub.key = 'basic_earned';
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.offset).to.eql(0);
});
it('adds 5 to plan.consecutive.gemCapExtra for 3 month block', async () => {
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
});
it('adds 10 to plan.consecutive.gemCapExtra for 6 month block', async () => {
data.sub.key = 'basic_6mo';
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
});
it('adds 20 to plan.consecutive.gemCapExtra for 12 month block', async () => {
it('adds 26 to plan.consecutive.gemCapExtra for 12 month block', async () => {
data.sub.key = 'basic_12mo';
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26);
});
it('does not raise plan.consecutive.gemCapExtra higher than 25', async () => {
it('does not raise plan.consecutive.gemCapExtra higher than 26', async () => {
data.sub.key = 'basic_12mo';
await api.createSubscription(data);
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(25);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26);
});
it('adds a plan.consecutive.trinkets for 3 month block', async () => {
@@ -798,20 +673,29 @@ describe('payments/index', () => {
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
});
it('adds 2 plan.consecutive.trinkets for 6 month block', async () => {
it('adds 1 plan.consecutive.trinkets for 6 month block', async () => {
data.sub.key = 'basic_6mo';
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
});
it('adds 4 plan.consecutive.trinkets for 12 month block', async () => {
it('adds 1 plan.consecutive.trinkets for 12 month block if they had promo', async () => {
user.purchased.plan.hourglassPromoReceived = new Date();
data.sub.key = 'basic_12mo';
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
});
it('adds 12 plan.consecutive.trinkets for 12 month block', async () => {
data.sub.key = 'basic_12mo';
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
context('Upgrades subscription', () => {
@@ -819,70 +703,38 @@ describe('payments/index', () => {
beforeEach(async () => {
data.updatedFrom = { logic: 'payDifference' };
});
it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
});
it('Adds 15 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
it('Adds 26 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26);
});
it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
});
it('Adds 2 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_6mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
@@ -894,7 +746,7 @@ describe('payments/index', () => {
data.updatedFrom.key = 'basic_3mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
});
@@ -902,70 +754,39 @@ describe('payments/index', () => {
beforeEach(async () => {
data.updatedFrom = { logic: 'payFull' };
});
it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
});
it('Adds 20 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
it('Adds 26 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(25);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26);
});
it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
});
it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_6mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(6);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
@@ -977,7 +798,7 @@ describe('payments/index', () => {
data.updatedFrom.key = 'basic_3mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(5);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
});
@@ -988,30 +809,13 @@ describe('payments/index', () => {
data.updatedFrom = { logic: 'refundAndRepay' };
});
context('Upgrades within first half of subscription', () => {
it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-01-10'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
});
it('Adds 15 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
it('Adds 26 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
@@ -1019,28 +823,10 @@ describe('payments/index', () => {
clock = sinon.useFakeTimers(new Date('2022-02-05'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26);
});
it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-01-08'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
});
it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
@@ -1054,17 +840,17 @@ describe('payments/index', () => {
clock = sinon.useFakeTimers(new Date('2022-01-31'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
it('Adds 2 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_6mo';
@@ -1072,35 +858,17 @@ describe('payments/index', () => {
clock = sinon.useFakeTimers(new Date('2022-01-28'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo after initial cycle', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
clock.restore();
clock = sinon.useFakeTimers(new Date('2024-01-08'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
});
it('Adds 2 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo after initial cycle', async () => {
it('2 plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo after initial cycle', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_6mo';
@@ -1108,10 +876,10 @@ describe('payments/index', () => {
clock = sinon.useFakeTimers(new Date('2022-08-28'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo after initial cycle', async () => {
it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo after initial cycle', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
@@ -1125,11 +893,11 @@ describe('payments/index', () => {
clock = sinon.useFakeTimers(new Date('2022-07-31'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
});
context('Upgrades within second half of subscription', () => {
it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
it('Adds 0 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
@@ -1144,16 +912,16 @@ describe('payments/index', () => {
clock = sinon.useFakeTimers(new Date('2022-01-20'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
});
it('Adds 20 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
it('Adds 26 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
@@ -1161,17 +929,17 @@ describe('payments/index', () => {
clock = sinon.useFakeTimers(new Date('2022-02-24'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(25);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26);
});
it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
it('Adds 0 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
@@ -1179,17 +947,17 @@ describe('payments/index', () => {
clock = sinon.useFakeTimers(new Date('2022-01-28'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
});
it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_6mo';
@@ -1197,10 +965,10 @@ describe('payments/index', () => {
clock = sinon.useFakeTimers(new Date('2022-05-28'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(6);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
@@ -1214,17 +982,17 @@ describe('payments/index', () => {
clock = sinon.useFakeTimers(new Date('2022-03-03'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(5);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo after initial cycle', async () => {
it('Adds 0 to plan.consecutive.trinkets from basic_earned to basic_6mo after initial cycle', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
@@ -1232,17 +1000,17 @@ describe('payments/index', () => {
clock = sinon.useFakeTimers(new Date('2022-05-28'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
});
it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo after initial cycle', async () => {
it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo after initial cycle', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_6mo';
@@ -1250,10 +1018,10 @@ describe('payments/index', () => {
clock = sinon.useFakeTimers(new Date('2023-05-28'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(6);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo after initial cycle', async () => {
it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo after initial cycle', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
@@ -1267,7 +1035,7 @@ describe('payments/index', () => {
clock = sinon.useFakeTimers(new Date('2023-09-03'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(5);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
});
afterEach(async () => {
@@ -1277,22 +1045,6 @@ describe('payments/index', () => {
});
context('Downgrades subscription', () => {
it('does not remove from plan.consecutive.gemCapExtra from basic_6mo to basic_earned', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
data.sub.key = 'basic_earned';
data.updatedFrom = { key: 'basic_6mo' };
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
});
it('does not remove from plan.consecutive.gemCapExtra from basic_12mo to basic_3mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
@@ -1300,28 +1052,12 @@ describe('payments/index', () => {
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26);
data.sub.key = 'basic_3mo';
data.updatedFrom = { key: 'basic_12mo' };
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
});
it('does not remove from plan.consecutive.trinkets from basic_6mo to basic_earned', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
data.sub.key = 'basic_earned';
data.updatedFrom = { key: 'basic_6mo' };
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26);
});
it('does not remove from plan.consecutive.trinkets from basic_12mo to basic_3mo', async () => {
@@ -1331,12 +1067,12 @@ describe('payments/index', () => {
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
data.sub.key = 'basic_3mo';
data.updatedFrom = { key: 'basic_12mo' };
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
expect(user.purchased.plan.consecutive.trinkets).to.eql(13);
});
});
});
@@ -1453,6 +1189,32 @@ describe('payments/index', () => {
expect(user.purchased.plan.extraMonths).to.eql(0);
});
it('does not reset gemCapExtra', async () => {
user.purchased.plan.consecutive.gemCapExtra = 12;
await api.cancelSubscription(data);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(12);
});
it('initializes gemCapExtra', async () => {
await api.cancelSubscription(data);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
});
it('initializes hourglasses', async () => {
await api.cancelSubscription(data);
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
});
it('does not reset owned hourglasses', async () => {
user.purchased.plan.consecutive.trinkets = 12;
await api.cancelSubscription(data);
expect(user.purchased.plan.consecutive.trinkets).to.eql(12);
});
it('sends an email', async () => {
await api.cancelSubscription(data);

View File

@@ -51,6 +51,7 @@ describe('Stripe - Checkout', () => {
gift: undefined,
sub: undefined,
gemsBlock: gemsBlockKey,
server_url: BASE_URL,
};
expect(gems.validateGiftMessage).to.not.be.called;
@@ -101,6 +102,7 @@ describe('Stripe - Checkout', () => {
gift: JSON.stringify(gift),
sub: undefined,
gemsBlock: undefined,
server_url: BASE_URL,
};
expect(gems.validateGiftMessage).to.be.calledOnce;
@@ -155,6 +157,7 @@ describe('Stripe - Checkout', () => {
gift: JSON.stringify(gift),
sub: undefined,
gemsBlock: undefined,
server_url: BASE_URL,
};
expect(oneTimePayments.getOneTimePaymentInfo).to.be.calledOnce;
@@ -192,6 +195,7 @@ describe('Stripe - Checkout', () => {
userId: user._id,
gift: undefined,
sub: JSON.stringify(sub),
server_url: BASE_URL,
};
expect(subscriptions.checkSubData).to.be.calledOnce;
@@ -258,6 +262,7 @@ describe('Stripe - Checkout', () => {
userId: user._id,
gift: undefined,
sub: JSON.stringify(sub),
server_url: BASE_URL,
groupId,
};
@@ -328,8 +333,9 @@ describe('Stripe - Checkout', () => {
user.purchased.plan.customerId = customerId;
const metadata = {
userId: user._id,
type: 'edit-card-user',
userId: user._id,
server_url: BASE_URL,
};
const res = await createEditCardCheckoutSession({ user }, stripe);
@@ -418,6 +424,7 @@ describe('Stripe - Checkout', () => {
const metadata = {
userId: user._id,
type: 'edit-card-group',
server_url: BASE_URL,
groupId,
};
@@ -455,6 +462,7 @@ describe('Stripe - Checkout', () => {
userId: anotherUser._id,
type: 'edit-card-group',
groupId,
server_url: BASE_URL,
};
const res = await createEditCardCheckoutSession({ user: anotherUser, groupId }, stripe);

View File

@@ -308,6 +308,7 @@ describe('Stripe - One Time Payments', () => {
customerId,
paymentMethod: 'Gift',
gift,
autoRenews: false,
gemsBlock: undefined,
});
});

View File

@@ -173,6 +173,7 @@ describe('Stripe - Subscriptions', () => {
paymentMethod: 'Stripe',
sub: sinon.match({ ...sub }),
groupId: null,
autoRenews: true,
});
});
@@ -197,6 +198,7 @@ describe('Stripe - Subscriptions', () => {
paymentMethod: 'Stripe',
sub: sinon.match({ ...sub }),
groupId,
autoRenews: true,
});
});
@@ -231,6 +233,7 @@ describe('Stripe - Subscriptions', () => {
paymentMethod: 'Stripe',
sub: sinon.match({ ...sub }),
groupId,
autoRenews: true,
});
});
});

View File

@@ -16,6 +16,7 @@ import * as subscriptions from '../../../../../../website/server/libs/payments/s
const { i18n } = common;
describe('Stripe - Webhooks', () => {
const BASE_URL = nconf.get('BASE_URL');
const stripe = stripeModule('test');
const endpointSecret = nconf.get('STRIPE_WEBHOOKS_ENDPOINT_SECRET');
const headers = {};
@@ -284,7 +285,9 @@ describe('Stripe - Webhooks', () => {
const session = {};
beforeEach(() => {
session.metadata = {};
session.metadata = {
server_url: BASE_URL,
};
event = { type: eventType, data: { object: session } };
constructEventStub = sandbox.stub(stripe.webhooks, 'constructEvent');
constructEventStub.returns(event);

View File

@@ -54,6 +54,7 @@ describe('rateLimiter middleware', () => {
it('does not throw when there are available points', async () => {
nconfGetStub.withArgs('RATE_LIMITER_ENABLED').returns('true');
nconfGetStub.withArgs('RATE_LIMITER_IP_COST').returns(1);
const attachRateLimiter = requireAgain(pathToRateLimiter).default;
await attachRateLimiter(req, res, next);
@@ -71,6 +72,7 @@ describe('rateLimiter middleware', () => {
it('does not throw when an unknown error is thrown by the rate limiter', async () => {
nconfGetStub.withArgs('RATE_LIMITER_ENABLED').returns('true');
nconfGetStub.withArgs('RATE_LIMITER_IP_COST').returns(1);
sandbox.stub(logger, 'error');
sandbox.stub(RateLimiterMemory.prototype, 'consume')
.returns(Promise.reject(new Error('Unknown error.')));
@@ -104,6 +106,7 @@ describe('rateLimiter middleware', () => {
it('limits when LIVELINESS_PROBE_KEY is incorrect', async () => {
nconfGetStub.withArgs('RATE_LIMITER_ENABLED').returns('true');
nconfGetStub.withArgs('LIVELINESS_PROBE_KEY').returns('abc');
nconfGetStub.withArgs('RATE_LIMITER_IP_COST').returns(1);
const attachRateLimiter = requireAgain(pathToRateLimiter).default;
req.query.liveliness = 'das';
@@ -120,6 +123,7 @@ describe('rateLimiter middleware', () => {
it('limits when LIVELINESS_PROBE_KEY is not set', async () => {
nconfGetStub.withArgs('RATE_LIMITER_ENABLED').returns('true');
nconfGetStub.withArgs('LIVELINESS_PROBE_KEY').returns(undefined);
nconfGetStub.withArgs('RATE_LIMITER_IP_COST').returns(1);
const attachRateLimiter = requireAgain(pathToRateLimiter).default;
await attachRateLimiter(req, res, next);
@@ -135,6 +139,7 @@ describe('rateLimiter middleware', () => {
it('throws when LIVELINESS_PROBE_KEY is blank', async () => {
nconfGetStub.withArgs('RATE_LIMITER_ENABLED').returns('true');
nconfGetStub.withArgs('LIVELINESS_PROBE_KEY').returns('');
nconfGetStub.withArgs('RATE_LIMITER_IP_COST').returns(1);
const attachRateLimiter = requireAgain(pathToRateLimiter).default;
req.query.liveliness = '';
@@ -150,6 +155,7 @@ describe('rateLimiter middleware', () => {
it('throws when there are no available points remaining', async () => {
nconfGetStub.withArgs('RATE_LIMITER_ENABLED').returns('true');
nconfGetStub.withArgs('RATE_LIMITER_IP_COST').returns(1);
const attachRateLimiter = requireAgain(pathToRateLimiter).default;
// call for 31 times
@@ -173,6 +179,7 @@ describe('rateLimiter middleware', () => {
it('uses the user id if supplied or the ip address', async () => {
nconfGetStub.withArgs('RATE_LIMITER_ENABLED').returns('true');
nconfGetStub.withArgs('RATE_LIMITER_IP_COST').returns(1);
const attachRateLimiter = requireAgain(pathToRateLimiter).default;
req.ip = 1;
@@ -199,4 +206,51 @@ describe('rateLimiter middleware', () => {
'X-RateLimit-Reset': sinon.match(Date),
});
});
it('applies increased cost for registration calls with and without user id', async () => {
nconfGetStub.withArgs('RATE_LIMITER_ENABLED').returns('true');
nconfGetStub.withArgs('RATE_LIMITER_REGISTRATION_COST').returns(3);
const attachRateLimiter = requireAgain(pathToRateLimiter).default;
req.path = '/api/v4/user/auth/local/register';
req.ip = 1;
await attachRateLimiter(req, res, next);
req.headers['x-api-user'] = 'user-1';
await attachRateLimiter(req, res, next);
await attachRateLimiter(req, res, next);
// user id an ip are counted as separate sources
expect(res.set).to.have.been.calledWithMatch({
'X-RateLimit-Limit': 30,
'X-RateLimit-Remaining': 27, // 2 calls with user id
'X-RateLimit-Reset': sinon.match(Date),
});
req.headers['x-api-user'] = undefined;
await attachRateLimiter(req, res, next);
await attachRateLimiter(req, res, next);
expect(res.set).to.have.been.calledWithMatch({
'X-RateLimit-Limit': 30,
'X-RateLimit-Remaining': 24, // 3 calls with only ip
'X-RateLimit-Reset': sinon.match(Date),
});
});
it('applies increased cost for unauthenticated API calls', async () => {
nconfGetStub.withArgs('RATE_LIMITER_ENABLED').returns('true');
nconfGetStub.withArgs('RATE_LIMITER_IP_COST').returns(10);
const attachRateLimiter = requireAgain(pathToRateLimiter).default;
req.ip = 1;
await attachRateLimiter(req, res, next);
await attachRateLimiter(req, res, next);
expect(res.set).to.have.been.calledWithMatch({
'X-RateLimit-Limit': 30,
'X-RateLimit-Remaining': 10,
'X-RateLimit-Reset': sinon.match(Date),
});
});
});

View File

@@ -3,6 +3,7 @@ import {
createAndPopulateGroup,
translate as t,
} from '../../../../helpers/api-integration/v3';
import { model as Group } from '../../../../../website/server/models/group';
describe('GET /groups/:groupId/chat', () => {
let user;
@@ -37,4 +38,34 @@ describe('GET /groups/:groupId/chat', () => {
});
});
});
context('public Guild', () => {
let group;
before(async () => {
({ group } = await createAndPopulateGroup({
groupDetails: {
name: 'test group',
type: 'guild',
privacy: 'private',
},
members: 1,
upgradeToGroupPlan: true,
chat: [
'Hello',
'Welcome to the Guild',
],
}));
// 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 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'),
});
});
});
});

View File

@@ -4,6 +4,7 @@ import {
createAndPopulateGroup,
translate as t,
} from '../../../../helpers/api-integration/v3';
import { model as Group } from '../../../../../website/server/models/group';
describe('POST /chat/:chatId/like', () => {
let user;
@@ -111,4 +112,18 @@ describe('POST /chat/:chatId/like', () => {
message: t('groupNotFound'),
});
});
it('does not like a message that belongs to a sunset public group', async () => {
const message = await anotherUser.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
// Creation API is shut down, we need to simulate an extant public group
await Group.updateOne({ _id: groupWithChat._id }, { $set: { privacy: 'public' }, $unset: { 'purchased.plan': 1 } }).exec();
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'),
});
});
});

View File

@@ -0,0 +1,73 @@
import nconf from 'nconf';
import {
generateUser,
createAndPopulateGroup,
} from '../../../../helpers/api-integration/v3';
describe('POST /debug/boss-rage', () => {
let user;
let nconfStub;
beforeEach(async () => {
user = await generateUser();
});
beforeEach(() => {
nconfStub = sandbox.stub(nconf, 'get');
nconfStub.withArgs('DEBUG_ENABLED').returns(true);
nconfStub.withArgs('BASE_URL').returns('https://example.com');
});
afterEach(() => {
nconfStub.restore();
});
it('errors if user is not in a party', async () => {
await expect(user.post('/debug/boss-rage'))
.to.eventually.be.rejected.and.deep.equal({
code: 400,
error: 'BadRequest',
message: 'User not in a party.',
});
});
it('returns error when not in production mode', async () => {
nconfStub.withArgs('DEBUG_ENABLED').returns(false);
await expect(user.post('/debug/boss-rage'))
.to.eventually.be.rejected.and.deep.equal({
code: 404,
error: 'NotFound',
message: 'Not found.',
});
});
context('user is in a party', async () => {
let party;
beforeEach(async () => {
const { group, groupLeader } = await createAndPopulateGroup({
groupDetails: {
name: 'Test Party',
type: 'party',
},
members: 2,
});
party = group;
user = groupLeader;
});
it('increases boss rage to 50', async () => {
await user.post('/debug/boss-rage');
await party.sync();
expect(party.quest.progress.rage).to.eql(50);
});
it('increases boss rage to 100', async () => {
await user.post('/debug/boss-rage');
await user.post('/debug/boss-rage');
await party.sync();
expect(party.quest.progress.rage).to.eql(100);
});
});
});

View File

@@ -34,9 +34,11 @@ describe('POST /debug/jump-time', () => {
expect(resultDate.getMonth()).to.eql(today.getMonth());
expect(resultDate.getFullYear()).to.eql(today.getFullYear());
const newResultDate = new Date((await user.post('/debug/jump-time', { offsetDays: 1 })).time);
expect(newResultDate.getDate()).to.eql(today.getDate() + 1);
expect(newResultDate.getMonth()).to.eql(today.getMonth());
expect(newResultDate.getFullYear()).to.eql(today.getFullYear());
const tomorrow = new Date(today.valueOf());
tomorrow.setDate(today.getDate() + 1);
expect(newResultDate.getDate()).to.eql(tomorrow.getDate());
expect(newResultDate.getMonth()).to.eql(tomorrow.getMonth());
expect(newResultDate.getFullYear()).to.eql(tomorrow.getFullYear());
});
it('jumps back', async () => {
@@ -45,9 +47,11 @@ describe('POST /debug/jump-time', () => {
expect(resultDate.getMonth()).to.eql(today.getMonth());
expect(resultDate.getFullYear()).to.eql(today.getFullYear());
const newResultDate = new Date((await user.post('/debug/jump-time', { offsetDays: -1 })).time);
expect(newResultDate.getDate()).to.eql(today.getDate() - 1);
expect(newResultDate.getMonth()).to.eql(today.getMonth());
expect(newResultDate.getFullYear()).to.eql(today.getFullYear());
const yesterday = new Date(today.valueOf());
yesterday.setDate(today.getDate() - 1);
expect(newResultDate.getDate()).to.eql(yesterday.getDate());
expect(newResultDate.getMonth()).to.eql(yesterday.getMonth());
expect(newResultDate.getFullYear()).to.eql(yesterday.getFullYear());
});
it('can jump a lot', async () => {
@@ -55,7 +59,7 @@ describe('POST /debug/jump-time', () => {
expect(resultDate.getDate()).to.eql(today.getDate());
expect(resultDate.getMonth()).to.eql(today.getMonth());
expect(resultDate.getFullYear()).to.eql(today.getFullYear());
const newResultDate = new Date((await user.post('/debug/jump-time', { offsetDays: 355 })).time);
const newResultDate = new Date((await user.post('/debug/jump-time', { offsetDays: 365 })).time);
expect(newResultDate.getFullYear()).to.eql(today.getFullYear() + 1);
});

View File

@@ -85,22 +85,6 @@ describe('POST /group/:groupId/join', () => {
await expect(user.get('/user')).to.eventually.have.nested.property('items.quests.basilist', 1);
});
it('notifies inviting user that their invitation was accepted', async () => {
await invitedUser.post(`/groups/${guild._id}/join`);
const inviter = await user.get('/user');
const expectedData = {
headerText: t('invitationAcceptedHeader'),
bodyText: t('invitationAcceptedBody', {
username: invitedUser.auth.local.username,
groupName: guild.name,
}),
};
expect(inviter.notifications[1].type).to.eql('GROUP_INVITE_ACCEPTED');
expect(inviter.notifications[1].data).to.eql(expectedData);
});
it('awards Joined Guild achievement', async () => {
await invitedUser.post(`/groups/${guild._id}/join`);
@@ -155,23 +139,6 @@ describe('POST /group/:groupId/join', () => {
await expect(invitedUser.get('/user')).to.eventually.have.nested.property('party._id', party._id);
});
it('notifies inviting user that their invitation was accepted', async () => {
await invitedUser.post(`/groups/${party._id}/join`);
const inviter = await user.get('/user');
const expectedData = {
headerText: t('invitationAcceptedHeader'),
bodyText: t('invitationAcceptedBody', {
username: invitedUser.auth.local.username,
groupName: party.name,
}),
};
expect(inviter.notifications[0].type).to.eql('GROUP_INVITE_ACCEPTED');
expect(inviter.notifications[0].data).to.eql(expectedData);
});
it('clears invitation from user when joining party', async () => {
await invitedUser.post(`/groups/${party._id}/join`);

View File

@@ -60,12 +60,12 @@ describe('PUT /heroes/:heroId', () => {
expect(heroRes.profile).to.have.all.keys(['name']);
// test response values
expect(heroRes.balance).to.equal(3 + 0.75); // 3+0.75 for first contrib level
expect(heroRes.balance).to.equal(3 + 2.5); // 3+2.5 for first contrib level
expect(heroRes.contributor.level).to.equal(1);
expect(heroRes.purchased.ads).to.equal(true);
// test hero values
await hero.sync();
expect(hero.balance).to.equal(3 + 0.75); // 3+0.75 for first contrib level
expect(hero.balance).to.equal(3 + 2.5); // 3+2.5 for first contrib level
expect(hero.contributor.level).to.equal(1);
expect(hero.purchased.ads).to.equal(true);
expect(hero.auth.blocked).to.equal(prevBlockState);
@@ -136,12 +136,12 @@ describe('PUT /heroes/:heroId', () => {
expect(heroRes.profile).to.have.all.keys(['name']);
// test response values
expect(heroRes.balance).to.equal(1); // 0+1 for sixth contrib level
expect(heroRes.balance).to.equal(15); // 0+15 for sixth contrib level
expect(heroRes.contributor.level).to.equal(6);
expect(heroRes.items.pets['Dragon-Hydra']).to.equal(5);
// test hero values
await hero.sync();
expect(hero.balance).to.equal(1); // 0+1 for sixth contrib level
expect(hero.balance).to.equal(15); // 0+15 for sixth contrib level
expect(hero.contributor.level).to.equal(6);
expect(hero.items.pets['Dragon-Hydra']).to.equal(5);
});

View File

@@ -0,0 +1,56 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
import common from '../../../../../website/common';
describe('GET /members/username/:username', () => {
let user;
before(async () => {
user = await generateUser();
});
it('validates req.params.username', async () => {
await expect(user.get('/members/username/')).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('invalidReqParams'),
});
});
it('returns a member public data only', async () => {
// make sure user has all the fields that can be returned by the getMember call
const member = await generateUser({
contributor: { level: 1 },
backer: { tier: 3 },
preferences: {
costume: false,
background: 'volcano',
},
secret: {
text: 'Clark Kent',
},
});
const memberRes = await user.get(`/members/username/${member.auth.local.username}`);
expect(memberRes).to.have.all.keys([ // works as: object has all and only these keys
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
]);
expect(Object.keys(memberRes.auth)).to.eql(['local', 'timestamps']);
expect(Object.keys(memberRes.preferences).sort()).to.eql([
'size', 'hair', 'skin', 'shirt',
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
].sort());
expect(memberRes.stats.maxMP).to.exist;
expect(memberRes.stats.maxHealth).to.equal(common.maxHealth);
expect(memberRes.stats.toNextLevel).to.equal(common.tnl(memberRes.stats.lvl));
expect(memberRes.inbox.optOut).to.exist;
expect(memberRes.inbox.canReceive).to.exist;
expect(memberRes.inbox.messages).to.not.exist;
expect(memberRes.secret).to.not.exist;
expect(memberRes.blocks).to.not.exist;
});
});

View File

@@ -125,6 +125,90 @@ describe('POST /tasks/:id/score/:direction', () => {
expect(body.finalLvl).to.eql(user.stats.lvl);
});
});
context('handles drops', async () => {
let randomStub;
afterEach(() => {
randomStub.restore();
});
it('gives user a drop', async () => {
user = await generateUser({
'stats.gp': 100,
'achievements.completedTask': true,
'items.eggs': {
Wolf: 1,
},
});
randomStub = sandbox.stub(Math, 'random').returns(0.1);
const task = await user.post('/tasks/user', {
text: 'test habit',
type: 'habit',
});
const res = await user.post(`/tasks/${task.id}/score/up`);
expect(res._tmp.drop).to.be.ok;
});
it('does not give a drop when non-sub drop cap is reached', async () => {
user = await generateUser({
'stats.gp': 100,
'achievements.completedTask': true,
'items.eggs': {
Wolf: 1,
},
'items.lastDrop.count': 5,
});
randomStub = sandbox.stub(Math, 'random').returns(0.1);
const task = await user.post('/tasks/user', {
text: 'test habit',
type: 'habit',
});
const res = await user.post(`/tasks/${task.id}/score/up`);
expect(res._tmp.drop).to.be.undefined;
});
it('gives a drop when subscriber is over regular cap but under subscriber cap', async () => {
user = await generateUser({
'stats.gp': 100,
'achievements.completedTask': true,
'items.eggs': {
Wolf: 1,
},
'items.lastDrop.count': 6,
'purchased.plan.customerId': '123',
});
randomStub = sandbox.stub(Math, 'random').returns(0.1);
const task = await user.post('/tasks/user', {
text: 'test habit',
type: 'habit',
});
const res = await user.post(`/tasks/${task.id}/score/up`);
expect(res._tmp.drop).to.be.ok;
});
it('does not give a drop when subscriber is at subscriber drop cap', async () => {
user = await generateUser({
'stats.gp': 100,
'achievements.completedTask': true,
'items.eggs': {
Wolf: 1,
},
'items.lastDrop.count': 10,
'purchased.plan.customerId': '123',
});
randomStub = sandbox.stub(Math, 'random').returns(0.1);
const task = await user.post('/tasks/user', {
text: 'test habit',
type: 'habit',
});
const res = await user.post(`/tasks/${task.id}/score/up`);
expect(res._tmp.drop).to.be.undefined;
});
});
});
context('todos', () => {

View File

@@ -105,9 +105,9 @@ describe('POST /tasks/:taskId/assign/:memberId', () => {
const groupTask = await user.get(`/tasks/group/${guild._id}`);
expect(member.notifications.length).to.equal(2);
expect(member.notifications[1].type).to.equal('GROUP_TASK_ASSIGNED');
expect(member.notifications[1].taskId).to.equal(groupTask._id);
const lastNotification = member.notifications[member.notifications.length - 1];
expect(lastNotification.type).to.equal('GROUP_TASK_ASSIGNED');
expect(lastNotification.taskId).to.equal(groupTask._id);
});
it('assigns a task to multiple users', async () => {

View File

@@ -89,10 +89,12 @@ describe('POST /tasks/:taskId/unassign/:memberId', () => {
});
it('removes task assignment notification from unassigned user', async () => {
await member.sync();
const oldNotificationCount = member.notifications.length;
await user.post(`/tasks/${task._id}/unassign/${member._id}`);
await member.sync();
expect(member.notifications.length).to.equal(1); // mystery items
expect(member.notifications.length).to.equal(oldNotificationCount - 1);
});
it('unassigns a user and only that user from a task', async () => {

View File

@@ -31,7 +31,7 @@ describe('POST /user/buy-mystery-set/:key', () => {
expect(res.data).to.eql({
items: JSON.parse(JSON.stringify(user.items)), // otherwise dates can't be compared
purchasedPlanConsecutive: user.purchased.plan.consecutive,
purchasedPlanConsecutive: JSON.parse(JSON.stringify(user.purchased.plan.consecutive)),
});
expect(res.message).to.equal(t('hourglassPurchaseSet'));
});

View File

@@ -123,7 +123,7 @@ describe('GET /world-state', () => {
const res = await requester().get('/world-state');
expect(res.npcImageSuffix).to.equal('winter');
expect(res.npcImageSuffix).to.equal('fall');
});
});
});

View File

@@ -0,0 +1,104 @@
import find from 'lodash/find';
import {
generateUser,
translate as t,
} from '../../../helpers/api-integration/v4';
/**
* Checks the messages array if the uniqueMessageId has the like flag
* @param {InboxMessage[]} messages
* @param {String} uniqueMessageId
* @param {String} userId
* @param {Boolean} likeStatus
*/
function expectMessagesLikeStatus (messages, uniqueMessageId, userId, likeStatus) {
const messageToCheck = find(messages, { uniqueMessageId });
expect(messageToCheck.likes[userId]).to.equal(likeStatus);
}
// eslint-disable-next-line mocha/no-exclusive-tests
describe('POST /inbox/like-private-message/:messageId', () => {
let userToSendMessage;
const getLikeUrl = messageId => `/inbox/like-private-message/${messageId}`;
before(async () => {
userToSendMessage = await generateUser();
});
it('Returns an error when private message is not found', async () => {
await expect(userToSendMessage.post(getLikeUrl('some-unknown-id')))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('messageGroupChatNotFound'),
});
});
it('Likes a message', async () => {
const receiver = await generateUser();
const sentMessageResult = await userToSendMessage.post('/members/send-private-message', {
message: 'some message :)',
toUserId: receiver._id,
});
const { uniqueMessageId } = sentMessageResult.message;
const likeResult = await receiver.post(getLikeUrl(uniqueMessageId));
expect(likeResult.likes[receiver._id]).to.equal(true);
const senderMessages = await userToSendMessage.get('/inbox/messages');
expectMessagesLikeStatus(senderMessages, uniqueMessageId, receiver._id, true);
const receiversMessages = await receiver.get('/inbox/messages');
expectMessagesLikeStatus(receiversMessages, uniqueMessageId, receiver._id, true);
});
it('Allows to likes their own private message', async () => {
const receiver = await generateUser();
const sentMessageResult = await userToSendMessage.post('/members/send-private-message', {
message: 'some message :)',
toUserId: receiver._id,
});
const { uniqueMessageId } = sentMessageResult.message;
const likeResult = await userToSendMessage.post(getLikeUrl(uniqueMessageId));
expect(likeResult.likes[userToSendMessage._id]).to.equal(true);
const messages = await userToSendMessage.get('/inbox/messages');
expectMessagesLikeStatus(messages, uniqueMessageId, userToSendMessage._id, true);
const receiversMessages = await receiver.get('/inbox/messages');
expectMessagesLikeStatus(receiversMessages, uniqueMessageId, userToSendMessage._id, true);
});
it('Unlikes a message', async () => {
const receiver = await generateUser();
const sentMessageResult = await userToSendMessage.post('/members/send-private-message', {
message: 'some message :)',
toUserId: receiver._id,
});
const { uniqueMessageId } = sentMessageResult.message;
const likeResult = await receiver.post(getLikeUrl(uniqueMessageId));
expect(likeResult.likes[receiver._id]).to.equal(true);
const unlikeResult = await receiver.post(getLikeUrl(uniqueMessageId));
expect(unlikeResult.likes[receiver._id]).to.equal(false);
const messages = await userToSendMessage.get('/inbox/messages');
const messageToCheck = find(messages, { id: sentMessageResult.message.id });
expect(messageToCheck.likes[receiver._id]).to.equal(false);
});
});

View File

@@ -40,6 +40,24 @@ describe('GET /user', () => {
expect(returnedUser.stats).to.not.exist;
});
it('returns when ALWAYS_LOADED paths are requested', async () => {
const returnedUser = await user.get('/user?userFields=_id,notifications,preferences,auth,flags,permissions');
expect(returnedUser._id).to.equal(user._id);
expect(returnedUser.notifications).to.exist;
expect(returnedUser.preferences).to.exist;
expect(returnedUser.auth).to.exist;
expect(returnedUser.flags).to.exist;
expect(returnedUser.permissions).to.exist;
});
it('returns when subpaths paths are requested', async () => {
const returnedUser = await user.get('/user?userFields=auth.local.username');
expect(returnedUser._id).to.equal(user._id);
expect(returnedUser.auth.local.username).to.exist;
});
it('does not return requested private properties', async () => {
const returnedUser = await user.get('/user?userFields=apiToken,secret.text');

View File

@@ -183,8 +183,6 @@ describe('cron utility functions', () => {
});
describe('getPlanContext', () => {
const now = new Date(2022, 5, 1);
function baseUserData (count, offset, planId) {
return {
purchased: {
@@ -192,7 +190,7 @@ describe('cron utility functions', () => {
consecutive: {
count,
offset,
gemCapExtra: 25,
gemCapExtra: 26,
trinkets: 19,
},
quantity: 1,
@@ -213,52 +211,19 @@ describe('cron utility functions', () => {
};
}
it('monthly plan, next date in 3 months', () => {
it('elapsedMonths is 0 if its the same month', () => {
const user = baseUserData(60, 0, 'group_plan_auto');
user.purchased.plan.perkMonthCount = 0;
const planContext = getPlanContext(user, now);
expect(planContext.nextHourglassDate)
.to.be.sameMoment('2022-08-10T02:00:00.144Z');
const planContext = getPlanContext(user, new Date(2022, 4, 20));
expect(planContext.elapsedMonths).to.equal(0);
});
it('monthly plan, next date in 1 month', () => {
const user = baseUserData(62, 0, 'group_plan_auto');
user.purchased.plan.perkMonthCount = 2;
it('elapsedMonths is 1 after one month', () => {
const user = baseUserData(60, 0, 'group_plan_auto');
const planContext = getPlanContext(user, now);
const planContext = getPlanContext(user, new Date(2022, 5, 11));
expect(planContext.nextHourglassDate)
.to.be.sameMoment('2022-06-10T02:00:00.144Z');
});
it('multi-month plan, no offset', () => {
const user = baseUserData(60, 0, 'basic_3mo');
const planContext = getPlanContext(user, now);
expect(planContext.nextHourglassDate)
.to.be.sameMoment('2022-06-10T02:00:00.144Z');
});
it('multi-month plan with offset', () => {
const user = baseUserData(60, 1, 'basic_3mo');
const planContext = getPlanContext(user, now);
expect(planContext.nextHourglassDate)
.to.be.sameMoment('2022-07-10T02:00:00.144Z');
});
it('multi-month plan with perk count', () => {
const user = baseUserData(60, 1, 'basic_3mo');
user.purchased.plan.perkMonthCount = 2;
const planContext = getPlanContext(user, now);
expect(planContext.nextHourglassDate)
.to.be.sameMoment('2022-07-10T02:00:00.144Z');
expect(planContext.elapsedMonths).to.equal(1);
});
});
});

View File

@@ -47,15 +47,17 @@ describe('shops', () => {
describe('premium hatching potions', () => {
it('contains current scheduled premium hatching potions', async () => {
clock = sinon.useFakeTimers(new Date('2024-04-01'));
clock = sinon.useFakeTimers(new Date('2024-04-01T09:00:00.000Z'));
const potions = shared.shops.getMarketCategories(user).find(x => x.identifier === 'premiumHatchingPotions');
expect(potions.items.length).to.eql(2);
expect(potions.items.length).to.eql(3);
});
it('does not contain past scheduled premium hatching potions', async () => {
clock = sinon.useFakeTimers(new Date('2024-04-01T09:00:00.000Z'));
const potions = shared.shops.getMarketCategories(user).find(x => x.identifier === 'premiumHatchingPotions');
expect(potions.items.filter(x => x.key === 'Aquatic' || x.key === 'Celestial').length).to.eql(0);
expect(potions.items.filter(x => x.key === 'Aquatic' || x.key === 'Celestial').length, 'Aquatic or Celestial found').to.eql(0);
});
it('returns end date for scheduled premium potions', async () => {
const potions = shared.shops.getMarketCategories(user).find(x => x.identifier === 'premiumHatchingPotions');
potions.items.forEach(potion => {
@@ -73,9 +75,9 @@ describe('shops', () => {
});
it('does not contain locked quest premium hatching potions', async () => {
clock = sinon.useFakeTimers(new Date('2024-04-01'));
clock = sinon.useFakeTimers(new Date('2024-04-01T09:00:00.000Z'));
const potions = shared.shops.getMarketCategories(user).find(x => x.identifier === 'premiumHatchingPotions');
expect(potions.items.length).to.eql(2);
expect(potions.items.length).to.eql(3);
expect(potions.items.filter(x => x.key === 'Bronze' || x.key === 'BlackPearl').length).to.eql(0);
});
@@ -341,6 +343,16 @@ describe('shops', () => {
const backgrounds = shopCategories.find(cat => cat.identifier === 'backgrounds').items;
expect(backgrounds.length).to.be.greaterThan(0);
});
it('does not add an end date to steampunk gear', () => {
const categories = shopCategories.filter(cat => cat.identifier.startsWith('30'));
categories.forEach(category => {
expect(category.end).to.not.exist;
category.items.forEach(item => {
expect(item.end).to.not.exist;
});
});
});
});
describe('customizationShop', () => {

View File

@@ -233,6 +233,17 @@ describe('shared.ops.purchase', () => {
expect(user.items.hatchingPotions[key]).to.eql(1);
});
it('purchases event hatching potion', async () => {
clock.restore();
clock = sandbox.useFakeTimers(moment('2022-04-10').valueOf());
const type = 'hatchingPotions';
const key = 'Veggie';
await purchase(user, { params: { type, key } });
expect(user.items.hatchingPotions[key]).to.eql(1);
});
it('purchases hatching potion if user completed quest', async () => {
const type = 'hatchingPotions';
const key = 'Bronze';

View File

@@ -10,7 +10,7 @@ describe('events', () => {
});
it('returns empty array when no events are active', () => {
clock = sinon.useFakeTimers(new Date('2024-01-06'));
clock = sinon.useFakeTimers(new Date('2024-01-08'));
const events = getRepeatingEvents();
expect(events).to.be.empty;
});
@@ -27,14 +27,14 @@ describe('events', () => {
it('returns nye event at beginning of the year', () => {
clock = sinon.useFakeTimers(new Date('2025-01-01'));
const events = getRepeatingEvents();
expect(events).to.have.length(1);
expect(events).to.have.length(2);
expect(events[0].key).to.equal('nye');
});
it('returns nye event at end of the year', () => {
clock = sinon.useFakeTimers(new Date('2024-12-30'));
const events = getRepeatingEvents();
expect(events).to.have.length(1);
expect(events).to.have.length(2);
expect(events[0].key).to.equal('nye');
});
});

View File

@@ -72,7 +72,7 @@ describe('food', () => {
});
it('sets canDrop for pie if it is pie season', () => {
clock = sinon.useFakeTimers(new Date(2024, 2, 14));
clock = sinon.useFakeTimers(new Date(2024, 2, 15));
const datedContent = require('../../website/common/script/content').default;
each(datedContent.food, foodItem => {
if (foodItem.key.indexOf('Pie_') !== -1) {

View File

@@ -5,7 +5,7 @@ import {
expectValidTranslationString,
} from '../helpers/content.helper';
import { all } from '../../website/common/script/content/hatching-potions';
import hatchingPotions from '../../website/common/script/content/hatching-potions';
describe('hatchingPotions', () => {
let clock;
@@ -25,7 +25,7 @@ describe('hatchingPotions', () => {
potionTypes.forEach(potionType => {
describe(potionType, () => {
it('contains basic information about each potion', () => {
each(all, (potion, key) => {
each(hatchingPotions.all, (potion, key) => {
expectValidTranslationString(potion.text);
expectValidTranslationString(potion.notes);
expect(potion.canBuy).to.be.a('function');
@@ -35,4 +35,20 @@ describe('hatchingPotions', () => {
});
});
});
it('does not contain unreleased potions', () => {
clock = sinon.useFakeTimers(new Date('2024-05-20'));
const premiumPotions = hatchingPotions.premium;
expect(premiumPotions.Koi).to.not.exist;
});
it('Releases potions when appropriate without needing restarting', () => {
clock = sinon.useFakeTimers(new Date('2024-05-20'));
const mayPotions = hatchingPotions.premium;
clock.restore();
clock = sinon.useFakeTimers(new Date('2024-06-20'));
const junePotions = hatchingPotions.premium;
expect(junePotions.Koi).to.exist;
expect(Object.keys(mayPotions).length).to.equal(Object.keys(junePotions).length - 1);
});
});

View File

@@ -42,23 +42,23 @@ describe('content index', () => {
expect(Object.keys(juneGear).length, '').to.equal(Object.keys(julyGear).length - 3);
});
it('Releases pets gear when appropriate without needing restarting', () => {
it('Releases pets when appropriate without needing restarting', () => {
clock = sinon.useFakeTimers(new Date('2024-06-20'));
const junePets = content.petInfo;
expect(junePets['Chameleon-Base']).to.not.exist;
clock.restore();
clock = sinon.useFakeTimers(new Date('2024-07-10'));
clock = sinon.useFakeTimers(new Date('2024-07-18'));
const julyPets = content.petInfo;
expect(julyPets['Chameleon-Base']).to.exist;
expect(Object.keys(junePets).length, '').to.equal(Object.keys(julyPets).length - 10);
});
it('Releases mounts gear when appropriate without needing restarting', () => {
it('Releases mounts when appropriate without needing restarting', () => {
clock = sinon.useFakeTimers(new Date('2024-06-20'));
const juneMounts = content.mountInfo;
expect(juneMounts['Chameleon-Base']).to.not.exist;
clock.restore();
clock = sinon.useFakeTimers(new Date('2024-07-10'));
clock = sinon.useFakeTimers(new Date('2024-07-18'));
const julyMounts = content.mountInfo;
expect(julyMounts['Chameleon-Base']).to.exist;
expect(Object.keys(juneMounts).length, '').to.equal(Object.keys(julyMounts).length - 10);
@@ -131,7 +131,7 @@ describe('content index', () => {
});
it('marks pie as buyable and droppable during pi day', () => {
clock = sinon.useFakeTimers(new Date('2024-03-14'));
clock = sinon.useFakeTimers(new Date('2024-03-15'));
const { food } = content;
Object.keys(food).forEach(key => {
if (key === 'Saddle') {

View File

@@ -0,0 +1,42 @@
import {
each,
} from 'lodash';
import {
expectValidTranslationString,
} from '../helpers/content.helper';
import { quests } from '../../website/common/script/content/quests';
describe('quests', () => {
let clock;
afterEach(() => {
if (clock) {
clock.restore();
}
});
it('contains basic information about each quest', () => {
each(quests, (quest, key) => {
expectValidTranslationString(quest.text);
expectValidTranslationString(quest.notes);
expectValidTranslationString(quest.completion);
expect(quest.key, key).to.equal(key);
expect(quest.category, key).to.be.a('string');
if (quest.boss) {
expectValidTranslationString(quest.boss.name);
expect(quest.boss.hp, key).to.be.a('number');
expect(quest.boss.str, key).to.be.a('number');
}
expect(quest.drop).to.be.an('object');
expect(quest.drop.gp, key).to.be.a('number');
expect(quest.drop.exp, key).to.be.a('number');
if (quest.drop.items) {
quest.drop.items.forEach(drop => {
expectValidTranslationString(drop.text);
expect(drop.type, key).to.exist;
});
}
});
});
});

View File

@@ -19,8 +19,8 @@ describe('releaseDates', () => {
});
describe('armoire', () => {
it('should only contain valid armoire names', () => {
const lastReleaseDate = maxBy(Object.values(ARMOIRE_RELEASE_DATES), value => new Date(`${value.year}-${value.month + 1}-20`));
clock = sinon.useFakeTimers(new Date(`${lastReleaseDate.year}-${lastReleaseDate.month + 1}-20`));
const lastReleaseDate = maxBy(Object.values(ARMOIRE_RELEASE_DATES), value => new Date(`${value.year}-${value.month}-22`));
clock = sinon.useFakeTimers(new Date(`${lastReleaseDate.year}-${lastReleaseDate.month}-22`));
Object.keys(ARMOIRE_RELEASE_DATES).forEach(key => {
expect(find(armoire.all, { set: key }), `${key} is not a valid armoire set`).to.exist;
});
@@ -40,8 +40,8 @@ describe('releaseDates', () => {
describe('eggs', () => {
it('should only contain valid egg names', () => {
const lastReleaseDate = maxBy(Object.values(EGGS_RELEASE_DATES), value => new Date(`${value.year}-${value.month + 1}-${value.day}`));
clock = sinon.useFakeTimers(new Date(`${lastReleaseDate.year}-${lastReleaseDate.month + 1}-${lastReleaseDate.day}`));
const lastReleaseDate = maxBy(Object.values(EGGS_RELEASE_DATES), value => new Date(`${value.year}-${value.month}-${value.day}`));
clock = sinon.useFakeTimers(new Date(`${lastReleaseDate.year}-${lastReleaseDate.month}-${lastReleaseDate.day + 1}`));
Object.keys(EGGS_RELEASE_DATES).forEach(key => {
expect(eggs.all[key], `${key} is not a valid egg name`).to.exist;
});
@@ -61,8 +61,8 @@ describe('releaseDates', () => {
describe('hatchingPotions', () => {
it('should only contain valid potion names', () => {
const lastReleaseDate = maxBy(Object.values(HATCHING_POTIONS_RELEASE_DATES), value => new Date(`${value.year}-${value.month + 1}-${value.day}`));
clock = sinon.useFakeTimers(new Date(`${lastReleaseDate.year}-${lastReleaseDate.month + 1}-${lastReleaseDate.day}`));
const lastReleaseDate = maxBy(Object.values(HATCHING_POTIONS_RELEASE_DATES), value => new Date(`${value.year}-${value.month}-${value.day}`));
clock = sinon.useFakeTimers(new Date(`${lastReleaseDate.year}-${lastReleaseDate.month}-${lastReleaseDate.day + 1}`));
Object.keys(HATCHING_POTIONS_RELEASE_DATES).forEach(key => {
expect(hatchingPotions.all[key], `${key} is not a valid potion name`).to.exist;
});

View File

@@ -1,4 +1,5 @@
// eslint-disable-next-line max-len
import maxBy from 'lodash/maxBy';
import moment from 'moment';
import nconf from 'nconf';
import {
@@ -7,9 +8,10 @@ import {
import QUEST_PETS from '../../website/common/script/content/quests/pets';
import QUEST_HATCHINGPOTIONS from '../../website/common/script/content/quests/potions';
import QUEST_BUNDLES from '../../website/common/script/content/bundles';
import { premium } from '../../website/common/script/content/hatching-potions';
import potions from '../../website/common/script/content/hatching-potions';
import SPELLS from '../../website/common/script/content/spells';
import QUEST_SEASONAL from '../../website/common/script/content/quests/seasonal';
import { HATCHING_POTIONS_RELEASE_DATES } from '../../website/common/script/content/constants/releaseDates';
function validateMatcher (matcher, checkedDate) {
expect(matcher.end).to.be.a('date');
@@ -18,12 +20,19 @@ function validateMatcher (matcher, checkedDate) {
describe('Content Schedule', () => {
let switchoverTime;
let clock;
beforeEach(() => {
switchoverTime = nconf.get('CONTENT_SWITCHOVER_TIME_OFFSET') || 0;
clearCachedMatchers();
});
afterEach(() => {
if (clock) {
clock.restore();
}
});
it('assembles scheduled items on january 15th', () => {
const date = new Date('2024-01-15');
const matchers = getAllScheduleMatchingGroups(date);
@@ -105,8 +114,14 @@ describe('Content Schedule', () => {
expect(matchers.backgrounds.end).to.eql(moment.utc(`2024-05-07T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
it('sets the end date if its on the release day', () => {
const date = new Date('2024-05-07T07:00:00.000Z');
it('sets the end date if its on the release day before switchover', () => {
const date = new Date('2024-05-07T07:00:00.000+00:00');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.backgrounds.end).to.eql(moment.utc(`2024-05-07T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
it('sets the end date if its on the release day after switchover', () => {
const date = new Date('2024-05-07T09:00:00.000+00:00');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.backgrounds.end).to.eql(moment.utc(`2024-06-07T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
@@ -123,12 +138,60 @@ describe('Content Schedule', () => {
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2024-06-21T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
it('sets the end date for a winter gala', () => {
const date = new Date('2024-12-22');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2025-03-21T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
it('sets the end date in new year for a winter gala', () => {
const date = new Date('2025-01-04');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2025-03-21T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
it('uses correct date for first hours of the month', () => {
// if the date is checked before CONTENT_SWITCHOVER_TIME_OFFSET,
// it should be considered the previous month
const date = new Date('2024-05-01T02:00:00.000Z');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.petQuests.items).to.contain('snake');
expect(matchers.petQuests.items).to.not.contain('horse');
expect(matchers.timeTravelers.match('202304'), '202304').to.be.true;
expect(matchers.timeTravelers.match('202404'), '202404').to.be.false;
expect(matchers.timeTravelers.match('202305'), '202305').to.be.false;
});
it('uses correct date after switchover time', () => {
// if the date is checked after CONTENT_SWITCHOVER_TIME_OFFSET,
// it should be considered the current
const date = new Date('2024-05-01T09:00:00.000Z');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.petQuests.items).to.contain('snake');
expect(matchers.petQuests.items).to.not.contain('horse');
expect(matchers.timeTravelers.match('202304'), '202304').to.be.false;
expect(matchers.timeTravelers.match('202305'), '202305').to.be.true;
expect(matchers.timeTravelers.match('202405'), '202405').to.be.false;
});
it('uses UTC timezone', () => {
// if the date is checked after CONTENT_SWITCHOVER_TIME_OFFSET,
// it should be considered the current
clock = sinon.useFakeTimers(new Date('2024-05-01T05:00:00.000-04:00'));
const matchers = getAllScheduleMatchingGroups();
expect(matchers.petQuests.items).to.contain('snake');
expect(matchers.petQuests.items).to.not.contain('horse');
expect(matchers.timeTravelers.match('202304'), '202304').to.be.false;
expect(matchers.timeTravelers.match('202305'), '202305').to.be.true;
expect(matchers.timeTravelers.match('202405'), '202405').to.be.false;
});
it('contains content for repeating events', () => {
const date = new Date('2024-04-15');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.premiumHatchingPotions).to.exist;
expect(matchers.premiumHatchingPotions.items.length).to.equal(4);
expect(matchers.premiumHatchingPotions.items.indexOf('Garden')).to.not.equal(-1);
expect(matchers.premiumHatchingPotions.items.length).to.equal(5);
expect(matchers.premiumHatchingPotions.items.indexOf('Veggie')).to.not.equal(-1);
expect(matchers.premiumHatchingPotions.items.indexOf('Porcelain')).to.not.equal(-1);
});
@@ -167,7 +230,9 @@ describe('Content Schedule', () => {
});
it('premium hatching potions', () => {
const potionKeys = Object.keys(premium);
const lastReleaseDate = maxBy(Object.values(HATCHING_POTIONS_RELEASE_DATES), value => new Date(`${value.year}-${value.month}-${value.day}`));
clock = sinon.useFakeTimers(new Date(`${lastReleaseDate.year}-${lastReleaseDate.month}-${lastReleaseDate.day + 1}`));
const potionKeys = Object.keys(potions.premium);
Object.keys(MONTHLY_SCHEDULE).forEach(key => {
const monthlyPotions = MONTHLY_SCHEDULE[key][21].find(item => item.type === 'premiumHatchingPotions');
for (const potion of monthlyPotions.items) {
@@ -207,6 +272,21 @@ describe('Content Schedule', () => {
expect(matcher.match('backgroundkey072024')).to.be.true;
});
it('allows background matching the month for new backgrounds from multiple years', () => {
const date = new Date('2026-07-08');
const matcher = getAllScheduleMatchingGroups(date).backgrounds;
expect(matcher.match('backgroundkey072024')).to.be.true;
expect(matcher.match('backgroundkey072025')).to.be.true;
expect(matcher.match('backgroundkey072026')).to.be.true;
});
it('allows background matching the previous month in the first week for new backgrounds', () => {
const date = new Date('2024-09-02');
const matcher = getAllScheduleMatchingGroups(date).backgrounds;
expect(matcher.match('backgroundkey082024')).to.be.true;
expect(matcher.match('backgroundkey092024')).to.be.false;
});
it('disallows background in the future', () => {
const date = new Date('2024-07-08');
const matcher = getAllScheduleMatchingGroups(date).backgrounds;
@@ -226,46 +306,59 @@ describe('Content Schedule', () => {
expect(matcher.match('backgroundkey022021')).to.be.true;
});
it('allows background even yeared backgrounds in first half of year', () => {
it('allows even yeared backgrounds in first half of year', () => {
const date = new Date('2025-02-08');
const matcher = getAllScheduleMatchingGroups(date).backgrounds;
expect(matcher.match('backgroundkey022024')).to.be.true;
expect(matcher.match('backgroundkey082022')).to.be.true;
});
it('allows background odd yeared backgrounds in second half of year', () => {
it('allows odd yeared backgrounds in second half of year', () => {
const date = new Date('2024-08-08');
const matcher = getAllScheduleMatchingGroups(date).backgrounds;
expect(matcher.match('backgroundkey022023')).to.be.true;
expect(matcher.match('backgroundkey082021')).to.be.true;
});
it('allows odd yeared backgrounds in beginning of january', () => {
const date = new Date('2025-01-06');
const matcher = getAllScheduleMatchingGroups(date).backgrounds;
expect(matcher.match('backgroundkey122024'), 'backgroundkey122024').to.be.true;
expect(matcher.match('backgroundkey062023'), 'backgroundkey062022').to.be.true;
});
});
describe('timeTravelers matcher', () => {
it('allows sets matching the month', () => {
const date = new Date('2024-07-08');
const matcher = getAllScheduleMatchingGroups(date).timeTravelers;
expect(matcher.match('202307')).to.be.true;
expect(matcher.match('202207')).to.be.true;
expect(matcher.match('202307'), '202307').to.be.true;
expect(matcher.match('202207'), '202207').to.be.true;
});
it('disallows sets not matching the month', () => {
const date = new Date('2024-07-08');
const matcher = getAllScheduleMatchingGroups(date).timeTravelers;
expect(matcher.match('202306')).to.be.false;
expect(matcher.match('202402')).to.be.false;
expect(matcher.match('202306'), '202306').to.be.false;
expect(matcher.match('202402'), '202402').to.be.false;
});
it('disallows sets from current month', () => {
const date = new Date('2024-07-08');
const matcher = getAllScheduleMatchingGroups(date).timeTravelers;
expect(matcher.match('202407')).to.be.false;
expect(matcher.match('202407'), '202407').to.be.false;
});
it('disallows sets from the future', () => {
const date = new Date('2024-07-08');
const matcher = getAllScheduleMatchingGroups(date).backgrounds;
expect(matcher.match('202507')).to.be.false;
const matcher = getAllScheduleMatchingGroups(date).timeTravelers;
expect(matcher.match('202507'), '202507').to.be.false;
});
it('matches sets released in the earlier half of the year', () => {
const date = new Date('2024-07-08');
const matcher = getAllScheduleMatchingGroups(date).timeTravelers;
expect(matcher.match('202401'), '202401').to.be.true;
});
});
});

View File

@@ -45,7 +45,7 @@ describe('time-travelers store', () => {
describe('on may 1st', () => {
beforeEach(() => {
date = new Date('2024-05-01');
date = new Date('2024-05-01T09:00:00.000Z');
});
it('returns the correct gear', () => {
const items = timeTravelers.timeTravelerStore(user, date);

View File

@@ -7,7 +7,7 @@ module.exports = {
extends: [
'habitrpg/lib/vue',
],
ignorePatterns: ['dist/', 'node_modules/'],
ignorePatterns: ['dist/', 'node_modules/', '*.d.ts'],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',

View File

@@ -23,7 +23,6 @@
"bootstrap": "^4.6.0",
"bootstrap-vue": "^2.23.1",
"core-js": "^3.33.1",
"dompurify": "^3.0.3",
"eslint": "7.32.0",
"eslint-config-habitrpg": "6.2.0",
"eslint-plugin-mocha": "5.3.0",
@@ -39,7 +38,6 @@
"sass": "^1.63.4",
"sass-loader": "^14.1.1",
"sinon": "^17.0.1",
"smartbanner.js": "^1.19.3",
"stopword": "^2.0.8",
"timers-browserify": "^2.0.12",
"uuid": "^9.0.1",
@@ -59,7 +57,7 @@
"chai": "^5.1.0",
"inspectpack": "^4.7.1",
"terser-webpack-plugin": "^5.3.10",
"webpack": "^5.89.0"
"webpack": "^5.94.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -2307,15 +2305,6 @@
"@types/json-schema": "*"
}
},
"node_modules/@types/eslint-scope": {
"version": "3.7.7",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
"integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
"dependencies": {
"@types/eslint": "*",
"@types/estree": "*"
}
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
@@ -3129,9 +3118,9 @@
"integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA=="
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
"integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz",
"integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==",
"dependencies": {
"@webassemblyjs/helper-numbers": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6"
@@ -3148,9 +3137,9 @@
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q=="
},
"node_modules/@webassemblyjs/helper-buffer": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
"integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA=="
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz",
"integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw=="
},
"node_modules/@webassemblyjs/helper-numbers": {
"version": "1.11.6",
@@ -3168,14 +3157,14 @@
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA=="
},
"node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
"integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz",
"integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6"
"@webassemblyjs/wasm-gen": "1.12.1"
}
},
"node_modules/@webassemblyjs/ieee754": {
@@ -3200,26 +3189,26 @@
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA=="
},
"node_modules/@webassemblyjs/wasm-edit": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
"integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz",
"integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/helper-wasm-section": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6",
"@webassemblyjs/wasm-opt": "1.11.6",
"@webassemblyjs/wasm-parser": "1.11.6",
"@webassemblyjs/wast-printer": "1.11.6"
"@webassemblyjs/helper-wasm-section": "1.12.1",
"@webassemblyjs/wasm-gen": "1.12.1",
"@webassemblyjs/wasm-opt": "1.12.1",
"@webassemblyjs/wasm-parser": "1.12.1",
"@webassemblyjs/wast-printer": "1.12.1"
}
},
"node_modules/@webassemblyjs/wasm-gen": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
"integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz",
"integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
"@webassemblyjs/leb128": "1.11.6",
@@ -3227,22 +3216,22 @@
}
},
"node_modules/@webassemblyjs/wasm-opt": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
"integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz",
"integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6",
"@webassemblyjs/wasm-parser": "1.11.6"
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/wasm-gen": "1.12.1",
"@webassemblyjs/wasm-parser": "1.12.1"
}
},
"node_modules/@webassemblyjs/wasm-parser": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
"integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz",
"integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-api-error": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
@@ -3251,11 +3240,11 @@
}
},
"node_modules/@webassemblyjs/wast-printer": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
"integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz",
"integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@xtuc/long": "4.2.2"
}
},
@@ -3326,10 +3315,10 @@
"node": ">=0.4.0"
}
},
"node_modules/acorn-import-assertions": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
"integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
"node_modules/acorn-import-attributes": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
"integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
"peerDependencies": {
"acorn": "^8"
}
@@ -3945,9 +3934,9 @@
"integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w=="
},
"node_modules/body-parser": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
@@ -3957,7 +3946,7 @@
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"qs": "6.13.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
@@ -4046,11 +4035,11 @@
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dependencies": {
"fill-range": "^7.0.1"
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@@ -4151,6 +4140,33 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
"integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/call-bound": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
"integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"get-intrinsic": "^1.2.6"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -4627,9 +4643,9 @@
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
},
"node_modules/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
"engines": {
"node": ">= 0.6"
}
@@ -5398,11 +5414,6 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/dompurify": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz",
"integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w=="
},
"node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
@@ -5438,6 +5449,19 @@
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA=="
},
"node_modules/dunder-proto": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz",
"integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==",
"dependencies": {
"call-bind-apply-helpers": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/duplexer": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
@@ -5480,9 +5504,9 @@
}
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"engines": {
"node": ">= 0.8"
}
@@ -5496,9 +5520,9 @@
}
},
"node_modules/enhanced-resolve": {
"version": "5.15.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
"integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
"version": "5.17.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
"integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -5604,12 +5628,9 @@
}
},
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"engines": {
"node": ">= 0.4"
}
@@ -5627,6 +5648,17 @@
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz",
"integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w=="
},
"node_modules/es-object-atoms": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
"integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz",
@@ -6711,36 +6743,36 @@
}
},
"node_modules/express": {
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.2",
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.6.0",
"cookie": "0.7.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"qs": "6.13.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"send": "0.19.0",
"serve-static": "1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
@@ -6749,6 +6781,10 @@
},
"engines": {
"node": ">= 0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/express/node_modules/array-flatten": {
@@ -6871,9 +6907,9 @@
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -6893,12 +6929,12 @@
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
@@ -7141,15 +7177,20 @@
}
},
"node_modules/get-intrinsic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz",
"integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"dunder-proto": "^1.0.0",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.0.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
@@ -7269,11 +7310,11 @@
}
},
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dependencies": {
"get-intrinsic": "^1.1.3"
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -7369,9 +7410,9 @@
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"engines": {
"node": ">= 0.4"
},
@@ -7399,9 +7440,9 @@
"integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg=="
},
"node_modules/hasown": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": {
"function-bind": "^1.1.2"
},
@@ -8935,6 +8976,14 @@
"markdown-it": "bin/markdown-it.js"
}
},
"node_modules/math-intrinsics": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.0.0.tgz",
"integrity": "sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA==",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mdn-data": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
@@ -8974,9 +9023,12 @@
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/merge-source-map": {
"version": "1.1.0",
@@ -9008,11 +9060,11 @@
}
},
"node_modules/micromatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dependencies": {
"braces": "^3.0.2",
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
@@ -9878,9 +9930,12 @@
}
},
"node_modules/object-inspect": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
"integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -10326,9 +10381,9 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
},
"node_modules/path-type": {
"version": "4.0.0",
@@ -11145,11 +11200,11 @@
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"dependencies": {
"side-channel": "^1.0.4"
"side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
@@ -11752,9 +11807,9 @@
"dev": true
},
"node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
@@ -11787,6 +11842,14 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/send/node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -11871,14 +11934,14 @@
}
},
"node_modules/serve-static": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
"dependencies": {
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.18.0"
"send": "0.19.0"
},
"engines": {
"node": ">= 0.8.0"
@@ -11967,13 +12030,68 @@
}
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3",
"side-channel-list": "^1.0.0",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-list": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-map": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-weakmap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3",
"side-channel-map": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -12095,17 +12213,6 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/smartbanner.js": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/smartbanner.js/-/smartbanner.js-1.22.0.tgz",
"integrity": "sha512-JhERLgwEPuzVdwAHds1J6txWBVq9BwmlAn+5VicrAfIOMO3ehNA7VHu8IIJNnW1LsElSCaLWxjdLjlEwLDqAvA==",
"engines": {
"node": ">=10.24.1 <22.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ain"
}
},
"node_modules/sockjs": {
"version": "0.3.24",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
@@ -13342,9 +13449,9 @@
}
},
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
"dependencies": {
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.1.2"
@@ -13378,33 +13485,32 @@
}
},
"node_modules/webpack": {
"version": "5.89.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
"integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
"version": "5.94.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
"integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
"dependencies": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^1.0.0",
"@webassemblyjs/ast": "^1.11.5",
"@webassemblyjs/wasm-edit": "^1.11.5",
"@webassemblyjs/wasm-parser": "^1.11.5",
"@types/estree": "^1.0.5",
"@webassemblyjs/ast": "^1.12.1",
"@webassemblyjs/wasm-edit": "^1.12.1",
"@webassemblyjs/wasm-parser": "^1.12.1",
"acorn": "^8.7.1",
"acorn-import-assertions": "^1.9.0",
"browserslist": "^4.14.5",
"acorn-import-attributes": "^1.9.5",
"browserslist": "^4.21.10",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.15.0",
"enhanced-resolve": "^5.17.1",
"es-module-lexer": "^1.2.1",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.2.9",
"graceful-fs": "^4.2.11",
"json-parse-even-better-errors": "^2.3.1",
"loader-runner": "^4.2.0",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
"schema-utils": "^3.2.0",
"tapable": "^2.1.1",
"terser-webpack-plugin": "^5.3.7",
"watchpack": "^2.4.0",
"terser-webpack-plugin": "^5.3.10",
"watchpack": "^2.4.1",
"webpack-sources": "^3.2.3"
},
"bin": {

View File

@@ -25,7 +25,6 @@
"bootstrap": "^4.6.0",
"bootstrap-vue": "^2.23.1",
"core-js": "^3.33.1",
"dompurify": "^3.0.3",
"eslint": "7.32.0",
"eslint-config-habitrpg": "6.2.0",
"eslint-plugin-mocha": "5.3.0",
@@ -41,7 +40,6 @@
"sass": "^1.63.4",
"sass-loader": "^14.1.1",
"sinon": "^17.0.1",
"smartbanner.js": "^1.19.3",
"stopword": "^2.0.8",
"timers-browserify": "^2.0.12",
"uuid": "^9.0.1",
@@ -61,6 +59,6 @@
"chai": "^5.1.0",
"inspectpack": "^4.7.1",
"terser-webpack-plugin": "^5.3.10",
"webpack": "^5.89.0"
"webpack": "^5.94.0"
}
}

View File

@@ -7,18 +7,6 @@
<title>Habitica - Gamify Your Life</title>
<meta name="description" content="Habitica is a free habit and productivity app that treats your real life like a game. Habitica can help you achieve your goals to become healthy and happy.">
<meta name="keywords" content="Habits,Goals,Todo,Gamification,Health,Fitness,School,Work">
<meta name="smartbanner:title" content="Habitica">
<meta name="smartbanner:author" content="HabitRPG, Inc.">
<meta name="smartbanner:price" content="FREE">
<meta name="smartbanner:price-suffix-apple" content=" - On the App Store">
<meta name="smartbanner:price-suffix-google" content=" - In Google Play">
<meta name="smartbanner:icon-apple" content="/static/presskit/Logo/iOS.png">
<meta name="smartbanner:icon-google" content="/static/presskit/Logo/Android.png">
<meta name="smartbanner:button" content="VIEW">
<meta name="smartbanner:button-url-apple" content="https://itunes.apple.com/us/app/habitica-gamified-taskmanager/id994882113">
<meta name="smartbanner:button-url-google" content="https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica">
<meta name="smartbanner:enabled-platforms" content="android,ios">
<meta name="smartbanner:hide-ttl" content="2592000000">
<link href="https://fonts.googleapis.com/css?family=Roboto+Condensed:400,400i,700,700i|Roboto:400,400i,700,700i" rel="stylesheet">
<link rel="shortcut icon" sizes="48x48" href="/static/icons/favicon.ico">
<link rel="shortcut icon" sizes="192x192" href="/static/icons/favicon_192x192.png">

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -27,73 +27,15 @@
</div>
</div>
</div>
<div
id="app"
:class="{
'casting-spell': castingSpell,
}"
>
<!-- <banned-account-modal /> -->
<amazon-payments-modal v-if="!isStaticPage" />
<payments-success-modal />
<sub-cancel-modal-confirm v-if="isUserLoaded" />
<sub-canceled-modal v-if="isUserLoaded" />
<bug-report-modal v-if="isUserLoaded" />
<bug-report-success-modal v-if="isUserLoaded" />
<external-link-modal />
<birthday-modal />
<snackbars />
<router-view v-if="!isUserLoggedIn || isStaticPage" />
<template v-else>
<template v-if="isUserLoaded">
<chat-banner />
<damage-paused-banner />
<gems-promo-banner />
<gift-promo-banner />
<birthday-banner />
<notifications-display />
<app-menu />
<div
class="container-fluid"
:class="{'no-margin': noMargin}"
>
<app-header />
<buyModal
:item="selectedItemToBuy || {}"
:with-pin="true"
:generic-purchase="genericPurchase(selectedItemToBuy)"
@buyPressed="customPurchase($event)"
/>
<selectMembersModal
:item="selectedSpellToBuy || {}"
:group="user.party"
@memberSelected="memberSelected($event)"
/>
<div :class="{sticky: user.preferences.stickyHeader}">
<router-view />
</div>
</div>
<app-footer v-if="!hideFooter" />
<audio
id="sound"
ref="sound"
autoplay="autoplay"
></audio>
</template>
</template>
</div>
<snackbars />
<router-view v-if="!isUserLoggedIn || isStaticPage" />
<user-main v-else />
</div>
</template>
<style lang='scss' scoped>
@import '~@/assets/scss/colors.scss';
#app {
display: flex;
flex-direction: column;
overflow-x: hidden;
}
#loading-screen-inapp {
#melior {
color: $white;
@@ -163,68 +105,20 @@
<script>
import axios from 'axios';
import { loadProgressBar } from 'axios-progress-bar';
import birthdayModal from '@/components/news/birthdayModal';
import AppMenu from './components/header/menu';
import AppHeader from './components/header/index';
import ChatBanner from './components/header/banners/chatBanner';
import DamagePausedBanner from './components/header/banners/damagePaused';
import GemsPromoBanner from './components/header/banners/gemsPromo';
import GiftPromoBanner from './components/header/banners/giftPromo';
import BirthdayBanner from './components/header/banners/birthdayBanner';
import AppFooter from './components/appFooter';
import notificationsDisplay from './components/notifications';
import snackbars from './components/snackbars/notifications';
import { mapState } from '@/libs/store';
import * as Analytics from '@/libs/analytics';
import BuyModal from './components/shops/buyModal.vue';
import SelectMembersModal from '@/components/selectMembersModal.vue';
import notifications from '@/mixins/notifications';
import { setup as setupPayments } from '@/libs/payments';
import amazonPaymentsModal from '@/components/payments/amazonModal';
import paymentsSuccessModal from '@/components/payments/successModal';
import subCancelModalConfirm from '@/components/payments/cancelModalConfirm';
import subCanceledModal from '@/components/payments/canceledModal';
import externalLinkModal from '@/components/externalLinkModal.vue';
import spellsMixin from '@/mixins/spells';
import {
CONSTANTS,
getLocalSetting,
removeLocalSetting,
} from '@/libs/userlocalManager';
const bugReportModal = () => import(/* webpackChunkName: "bug-report-modal" */'@/components/bugReportModal');
const bugReportSuccessModal = () => import(/* webpackChunkName: "bug-report-success-modal" */'@/components/bugReportSuccessModal');
import { mapState } from '@/libs/store';
import userMain from '@/pages/user-main';
import snackbars from '@/components/snackbars/notifications';
const COMMUNITY_MANAGER_EMAIL = process.env.EMAILS_COMMUNITY_MANAGER_EMAIL; // eslint-disable-line
export default {
name: 'App',
components: {
AppMenu,
AppHeader,
AppFooter,
birthdayModal,
ChatBanner,
DamagePausedBanner,
GemsPromoBanner,
GiftPromoBanner,
BirthdayBanner,
notificationsDisplay,
snackbars,
BuyModal,
SelectMembersModal,
amazonPaymentsModal,
paymentsSuccessModal,
subCancelModalConfirm,
subCanceledModal,
bugReportModal,
bugReportSuccessModal,
externalLinkModal,
userMain,
},
mixins: [notifications, spellsMixin],
data () {
return {
selectedItemToBuy: null,
@@ -238,71 +132,25 @@ export default {
};
},
computed: {
...mapState(['isUserLoggedIn', 'browserTimezoneUtcOffset', 'isUserLoaded', 'notificationsRemoved']),
...mapState(['isUserLoggedIn', 'isUserLoaded', 'notificationsRemoved']),
...mapState({ user: 'user.data' }),
isStaticPage () {
return this.$route.meta.requiresLogin === false;
},
castingSpell () {
return this.$store.state.spellOptions.castingSpell;
},
noMargin () {
return ['privateMessages'].includes(this.$route.name);
},
hideFooter () {
return ['privateMessages'].includes(this.$route.name);
},
},
created () {
this.$root.$on('playSound', sound => {
const theme = this.user.preferences.sound;
if (!theme || theme === 'off') {
return;
}
const file = `/static/audio/${theme}/${sound}`;
if (this.audioSuffix === null) {
this.audioSource = document.createElement('source');
if (this.$refs.sound.canPlayType('audio/ogg')) {
this.audioSuffix = '.ogg';
this.audioSource.type = 'audio/ogg';
} else {
this.audioSuffix = '.mp3';
this.audioSource.type = 'audio/mp3';
}
this.audioSource.src = file + this.audioSuffix;
this.$refs.sound.appendChild(this.audioSource);
} else {
this.audioSource.src = file + this.audioSuffix;
}
this.$refs.sound.load();
// Setup listener for title
this.$store.watch(state => state.title, title => {
document.title = title;
});
// @TODO: I'm not sure these should be at the app level.
// Can we move these back into shop/inventory or maybe they need a lateral move?
this.$root.$on('buyModal::showItem', item => {
this.selectedItemToBuy = item;
this.$root.$emit('bv::show::modal', 'buy-modal');
});
this.$root.$on('bv::modal::hidden', event => {
if (event.componentId === 'buy-modal') {
this.$root.$emit('buyModal::hidden', this.selectedItemToBuy.key);
this.$store.watch(state => state.isUserLoaded, () => {
if (this.isUserLoaded) {
this.hideLoadingScreen();
}
});
this.$root.$on('selectMembersModal::showItem', item => {
this.selectedSpellToBuy = item;
this.$root.$emit('bv::show::modal', 'select-member-modal');
});
// @TODO split up this file, it's too big
loadProgressBar({
showSpinner: false,
this.$nextTick(() => {
// Load external scripts after the app has been rendered
Analytics.load();
});
axios.interceptors.response.use(response => { // Set up Response interceptors
@@ -414,79 +262,20 @@ export default {
return Promise.reject(error);
});
// Setup listener for title
this.$store.watch(state => state.title, title => {
document.title = title;
});
this.$nextTick(() => {
// Load external scripts after the app has been rendered
Analytics.load();
});
if (this.isUserLoggedIn && !this.isStaticPage) {
// Load the user and the user tasks
Promise.all([
this.$store.dispatch('user:fetch'),
this.$store.dispatch('tasks:fetchUserTasks'),
]).then(() => {
this.$store.state.isUserLoaded = true;
Analytics.setUser();
Analytics.updateUser();
return axios.get(
'/api/v4/i18n/browser-script',
{
language: this.user.preferences.language,
headers: {
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
Expires: '0',
},
},
);
}).then(() => {
const i18nData = window && window['habitica-i18n'];
this.$loadLocale(i18nData);
this.hideLoadingScreen();
// Adjust the timezone offset
const browserTimezoneOffset = -this.browserTimezoneUtcOffset;
if (this.user.preferences.timezoneOffset !== browserTimezoneOffset) {
this.$store.dispatch('user:set', {
'preferences.timezoneOffset': browserTimezoneOffset,
});
}
let appState = getLocalSetting(CONSTANTS.savedAppStateValues.SAVED_APP_STATE);
if (appState) {
appState = JSON.parse(appState);
if (appState.paymentCompleted) {
removeLocalSetting(CONSTANTS.savedAppStateValues.SAVED_APP_STATE);
this.$root.$emit('habitica:payment-success', appState);
}
}
this.$nextTick(() => {
// Load external scripts after the app has been rendered
setupPayments();
});
}).catch(err => {
console.error('Impossible to fetch user. Clean up localStorage and refresh.', err); // eslint-disable-line no-console
});
} else {
this.hideLoadingScreen();
}
},
beforeDestroy () {
this.$root.$off('playSound');
this.$root.$off('buyModal::showItem');
this.$root.$off('selectMembersModal::showItem');
},
mounted () {
// Remove the index.html loading screen and now show the inapp loading
const loadingScreen = document.getElementById('loading-screen');
if (loadingScreen) document.body.removeChild(loadingScreen);
if (this.isStaticPage || !this.isUserLoggedIn) {
this.hideLoadingScreen();
}
},
methods: {
hideLoadingScreen () {
this.loading = false;
},
checkForBannedUser (error) {
const AUTH_SETTINGS = localStorage.getItem('habit-mobile-settings');
const parseSettings = JSON.parse(AUTH_SETTINGS);
@@ -507,57 +296,9 @@ export default {
this.$store.dispatch('auth:logout', { redirectToLogin: true });
return true;
},
itemSelected (item) {
this.selectedItemToBuy = item;
},
genericPurchase (item) {
if (!item) return false;
if (['card', 'debuffPotion'].includes(item.purchaseType)) return false;
return true;
},
customPurchase (item) {
if (item.purchaseType === 'card') {
this.selectedSpellToBuy = item;
// hide the dialog
this.$root.$emit('bv::hide::modal', 'buy-modal');
// remove the dialog from our modal-stack,
// the default hidden event is delayed
this.$root.$emit('bv::modal::hidden', {
target: {
id: 'buy-modal',
},
});
this.$root.$emit('bv::show::modal', 'select-member-modal');
}
if (item.purchaseType === 'debuffPotion') {
this.castStart(item, this.user);
}
},
async memberSelected (member) {
await this.castStart(this.selectedSpellToBuy, member);
this.selectedSpellToBuy = null;
if (this.user.party._id) {
this.$store.dispatch('party:getMembers', { forceLoad: true });
}
this.$root.$emit('bv::hide::modal', 'select-member-modal');
},
hideLoadingScreen () {
this.loading = false;
},
},
};
</script>
<style src="intro.js/minified/introjs.min.css"></style>
<style src="axios-progress-bar/dist/nprogress.css"></style>
<style src="@/assets/scss/index.scss" lang="scss"></style>
<style src="@/assets/scss/sprites.scss" lang="scss"></style>
<style src="smartbanner.js/dist/smartbanner.min.css"></style>

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -3,9 +3,9 @@
font-family: 'Roboto', sans-serif;
font-size: 14px;
font-weight: bold;
line-height: 1.71;
border: 1px solid transparent;
padding: 4px 12px;
line-height: 1.714;
border: 2px solid transparent;
padding: 2px 12px;
border-radius: 4px;
box-shadow: 0 1px 3px 0 rgba($black, 0.12), 0 1px 2px 0 rgba($black, 0.24);
color: $white;
@@ -19,7 +19,7 @@
}
&:focus {
border-color: $purple-400;
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
}
&:active, &.active:not(.btn-flat) {
@@ -30,9 +30,9 @@
cursor: default;
color: $gray-50;
opacity: 0.75;
box-shadow: 0 1px 3px 0 rgba(26, 24, 29, 0.12), 0 1px 2px 0 rgba(26, 24, 29, 0.24);
box-shadow: none;
background-color: $gray-700;
border: 1px solid transparent;
border: 2px solid transparent;
.svg {
color: $gray-300;
@@ -40,7 +40,7 @@
}
&.with-icon {
height: 2rem; // otherwise would something set the height to 33px
height: 32px; // otherwise would something set the height to 33px
display: flex;
flex-direction: row;
align-items: center;
@@ -48,40 +48,47 @@
}
.btn-front {
border: none !important;
font-size: 16px;
line-height: 1.5;
padding: 7.5px 15.5px;
padding: 2px 17px;
&:hover {
border: none !important;
}
}
.btn-primary {
background: $purple-200;
border: 1px solid transparent;
border: 2px solid transparent;
line-height: 1.714;
--icon-color: #{$purple-500};
&:focus {
background: $purple-200;
border-color: $purple-400;
border: 2px solid $purple-400;
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
--icon-color: #{$white};
}
&:not(:disabled):not(.disabled) {
&:hover {
background: #5d3b9c;
border: 1px solid transparent;
background: $purple-200;
border: 2px solid transparent;
--icon-color: #{$white};
}
&:active, &.active {
background: $purple-200;
border: 1px solid transparent;
border: 2px solid transparent;
box-shadow: none;
--icon-color: #{$white};
}
&:active:focus, &.active:focus {
box-shadow: none;
border-color: $purple-400;
border: 2px solid $purple-400;
}
}
@@ -94,42 +101,45 @@
.btn-secondary,
.dropdown > .btn-secondary.dropdown-toggle:not(.btn-success),
.show > .btn-secondary.dropdown-toggle:not(.btn-success)
{
.show > .btn-secondary.dropdown-toggle:not(.btn-success) {
background: $white;
border: 1px solid transparent;
border: 2px solid transparent;
color: $gray-50;
--icon-color: #{$gray-200};
&:focus, &:active {
color: $gray-50;
background: $white;
border-color: $purple-400;
border: 2px solid $purple-400;
color: $gray-50;
--icon-color: #{$purple-300};
}
&:not(:disabled):not(.disabled) {
&:active, &.active {
background: $white;
border: 2px solid $purple-400;
color: $purple-300;
--icon-color: #{$purple-300};
&:focus {
color: $purple-300;
box-shadow: none;
border-color: $purple-400;
border: 2px solid $purple-400;
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
}
&:active {
box-shadow: none;
}
background: $white;
border: 1px solid transparent;
}
&:hover {
color: $purple-300;
background: $white !important;
border: 1px solid transparent;
border: 2px solid transparent;
--icon-color: #{$purple-300};
.svg {
@@ -151,67 +161,116 @@
.btn-danger {
background: $maroon-100;
border: 1px solid transparent;
border: 2px solid transparent;
box-shadow: 0 1px 3px 0 rgba($black, 0.16), 0 1px 3px 0 rgba($black, 0.24);
&:hover:not(:disabled):not(.disabled) {
background: #e14e4e;
border: 1px solid transparent;
background: $maroon-100;
border: 2px solid transparent;
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
}
&:focus {
background: $maroon-100;
border-color: $purple-400;
border: 2px solid $purple-400;
}
&:not(:disabled):not(.disabled):active:focus, &:not(:disabled):not(.disabled).active:focus {
box-shadow: none;
border-color: $purple-400;
border: 2px solid $purple-400;
}
&:not(:disabled):not(.disabled):active, &:not(:disabled):not(.disabled).active {
background: $maroon-100;
border: 1px solid transparent;
border: 2px solid $purple-400;
}
}
.btn-warning {
background: $orange-10;
box-shadow: 0 1px 3px 0 rgba($black, 0.16), 0 1px 3px 0 rgba($black, 0.24);
color: $white !important;
&:hover:not(:disabled):not(.disabled) {
background: $orange-10;
color: $white;
}
&:focus {
background: $orange-10;
border: 2px solid $purple-400;
}
&:not(:disabled):not(.disabled):active:focus, &:not(:disabled):not(.disabled).active:focus {
border: 2px solid $purple-400;
box-shadow: none;
}
&:not(:disabled):not(.disabled):active, &:not(:disabled):not(.disabled).active {
background: $orange-10;
box-shadow: none;
}
}
.btn-success {
background: $green-50;
border: 1px solid transparent;
border: 2px solid transparent;
&:hover:not(:disabled):not(.disabled) {
background: #32bd8a;
border: 1px solid transparent;
background: $green-50;
border: 2px solid transparent;
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
}
&:focus {
background: $green-50;
border-color: $purple-400;
border: 2px solid $purple-400;
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
}
&:not(:disabled):not(.disabled):active:focus, &:not(:disabled):not(.disabled).active:focus {
border: 2px solid $purple-400;
box-shadow: none;
border-color: $purple-400;
}
&:not(:disabled):not(.disabled):active, &:not(:disabled):not(.disabled).active {
background: $green-50;
border: 1px solid transparent;
border: 2px solid $purple-400;
box-shadow: none;
}
}
.btn-info {
background: $blue-50;
border: 2px solid transparent;
box-shadow: 0 1px 3px 0 rgba($black, 0.16), 0 1px 3px 0 rgba($black, 0.24);
&:disabled {
background: $blue-50;
box-shadow: none;
}
&:hover {
border: 2px solid transparent;
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
}
&:focus {
background: $blue-100;
border: 2px solid $purple-400;
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
}
&:hover:not(:disabled):not(.disabled) {
background-color: $blue-100;
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
}
&:active:not(:disabled):not(.disabled), &.active:not(:disabled):not(.disabled) {
background: $blue-50;
background: $blue-100;
border: 2px solid $purple-400;
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
}
}
@@ -220,7 +279,7 @@
width: 100%;
padding: 8px;
font-size: 14px;
line-height: 1.43;
line-height: 1.714;
font-weight: bold;
text-align: center;
background: $gray-500;
@@ -238,12 +297,22 @@
box-shadow: none;
}
.btn-flat,
.dropdown > .btn-flat.dropdown-toggle:not(.btn-success),
.show > .btn-flat.dropdown-toggle:not(.btn-success) {
&.with-icon {
.svg-icon.color {
color: var(--icon-color);
}
}
}
.btn-cancel {
color: $blue-10;
}
.btn-small {
font-size: 12px;
line-height: 1.33;
padding: 4px 8px;
line-height: 2;
padding: 2px 2px;
}

View File

@@ -1,8 +1,9 @@
.dropdown > .btn {
padding: 0.219rem 0.75rem;
font-family: 'Roboto', sans-serif;
font-size: 14px;
font-weight: normal;
line-height: 1.714;
padding: 2px 12px;
}
.dropdown-toggle:hover {
@@ -33,11 +34,16 @@
}
.dropdown-menu {
padding: 0px;
border: none;
border: transparent;
border-radius: 2px;
box-shadow: 0 3px 6px 0 rgba(26, 24, 29, 0.16), 0 3px 6px 0 rgba(26, 24, 29, 0.24);
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
padding: 0;
}
.no-min-width {
.dropdown-menu {
min-width: 0 !important;
}
}
// shared dropdown-item styles
@@ -53,6 +59,8 @@
color: $gray-50 !important;
cursor: pointer;
--dropdown-item-hover-icon-color: #{$gray-200};
&:focus {
outline: none;
background-color: inherit;
@@ -87,7 +95,7 @@
&:not(:hover) {
.with-icon .svg-icon {
color: $gray-200;
color: var(dropdown-item-hover-icon-color);
}
}
}
@@ -113,6 +121,10 @@
}
.dropdown-icon-item {
line-height: 1;
padding-top: 2px !important;
padding-bottom: 2px !important;
.svg-icon {
margin: 0px 16px 0px 0px;
vertical-align: middle;
@@ -128,7 +140,6 @@
.dropdown-toggle {
width: 100% !important;
height: 32px;
text-align: left;
}
@@ -147,7 +158,7 @@
// selectList.vue items sizing
.selectListItem .dropdown-item {
padding: 0.25rem 0.75rem;
padding: 0.25rem 1rem 0.25rem 0.75rem;
height: 32px;
&:active, &:hover, &:focus, &.active {

View File

@@ -44,6 +44,10 @@ ul {
color: $purple-200;
}
h4 {
color: $gray-50;
}
.body-text {
font-size: 1em;
color: $gray-10;

View File

@@ -86,3 +86,91 @@ h4 {
.opacity-75 {
opacity: 0.75;
}
.bg-gray-100 {
background-color: $gray-100 !important;
}
.bg-gray-300 {
background-color: $gray-300 !important;
}
.bg-gray-600 {
background-color: $gray-600 !important;
}
.bg-gray-700 {
background-color: $gray-700 !important;
}
.bg-green-10 {
background-color: $green-10 !important;
}
.bg-green-100 {
background-color: $green-100 !important;
}
.bg-purple-100 {
background-color: $purple-100 !important;
}
.bg-purple-300 {
background-color: $purple-300 !important;
}
.bg-white {
background-color: $white !important;
}
.gray-10 {
color: $gray-10 !important;
}
.gray-50 {
color: $gray-50 !important;
}
.gray-200 {
color: $gray-200 !important;
}
.gray-300 {
color: $gray-300 !important;
}
.green-10 {
color: $green-10 !important;
}
.maroon-50 {
color: $maroon-50 !important;
}
.purple-200 {
color: $purple-200 !important;
}
.purple-300 {
color: $purple-300 !important;
}
.purple-600 {
color: $purple-600 !important;
}
.teal-1 {
color: $teal-1 !important;
}
.teal-10 {
color: $teal-10 !important;
}
.yellow-10 {
color: $yellow-10 !important;
}
.white {
color: $white !important;
}

Some files were not shown because too many files have changed in this diff Show More