Compare commits

...

457 Commits

Author SHA1 Message Date
Phillip Thelen
a018588021 Banning a user now automatically hides all their posts 2022-12-09 13:02:49 +01:00
dependabot[bot]
580139ff69 build(deps): bump express from 4.17.1 to 4.18.2 in /website/client (#14396)
Bumps [express](https://github.com/expressjs/express) from 4.17.1 to 4.18.2.
- [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.17.1...4.18.2)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-08 10:10:07 -05:00
dependabot[bot]
e0860e604e build(deps): bump qs from 6.5.2 to 6.5.3 (#14395)
Bumps [qs](https://github.com/ljharb/qs) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/ljharb/qs/releases)
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.5.2...v6.5.3)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-08 10:09:34 -05:00
dependabot[bot]
9fc69456bb build(deps): bump qs from 6.5.2 to 6.5.3 in /website/client (#14394)
Bumps [qs](https://github.com/ljharb/qs) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/ljharb/qs/releases)
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.5.2...v6.5.3)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-08 10:08:58 -05:00
Weblate
5bf14e05cc Merge branch 'origin/develop' into Weblate. 2022-12-06 20:49:01 +01:00
SabreCat
7d081056ba Merge branch 'release' into develop 2022-12-06 13:37:29 -06:00
Weblate
2ff7bef2a6 Merge branch 'origin/develop' into Weblate. 2022-12-06 20:36:09 +01:00
SabreCat
51b3b0c4c7 4.251.0 2022-12-06 13:34:53 -06:00
SabreCat
174a4e69f9 fix(backgrounds): we're in December now 2022-12-06 13:34:25 -06:00
Natalie L
1ce060eac6 chore(content): add December 2022 Backgrounds and Enchanted Armoire Items (#14382)
* chore(content): css and images

* chore(content): add December 2022 Backgrounds and Enchanted Armoire Items

* fix(typos): dots no dip

* fix(typo): capitalize game terms

* fix(typos): GitHub regex find, why

* fix(typo): last one maybe?

Co-authored-by: SabreCat <sabe@habitica.com>
Co-authored-by: Sabe Jones <sabrecat@gmail.com>
2022-12-06 13:21:24 -06:00
Weblate
55f07f8ab2 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (German)

Currently translated at 99.7% (737 of 739 strings)

Translated using Weblate (Korean)

Currently translated at 96.4% (54 of 56 strings)

Translated using Weblate (Spanish)

Currently translated at 95.4% (2581 of 2705 strings)

Translated using Weblate (German)

Currently translated at 99.4% (735 of 739 strings)

Translated using Weblate (Ukrainian)

Currently translated at 59.4% (449 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (732 of 732 strings)

Translated using Weblate (Ukrainian)

Currently translated at 24.5% (663 of 2705 strings)

Translated using Weblate (Ukrainian)

Currently translated at 56.6% (428 of 755 strings)

Translated using Weblate (German)

Currently translated at 99.1% (733 of 739 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (221 of 221 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (221 of 221 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Spanish)

Currently translated at 97.7% (216 of 221 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 94.5% (714 of 755 strings)

Translated using Weblate (German)

Currently translated at 100.0% (221 of 221 strings)

Translated using Weblate (Russian)

Currently translated at 93.2% (206 of 221 strings)

Translated using Weblate (German)

Currently translated at 99.0% (219 of 221 strings)

Translated using Weblate (German)

Currently translated at 98.6% (218 of 221 strings)

Translated using Weblate (Spanish)

Currently translated at 95.4% (2581 of 2705 strings)

Translated using Weblate (German)

Currently translated at 93.6% (207 of 221 strings)

Translated using Weblate (French)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (French)

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (French)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (French)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (French)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (German)

Currently translated at 93.2% (206 of 221 strings)

Translated using Weblate (German)

Currently translated at 93.2% (206 of 221 strings)

Translated using Weblate (Ukrainian)

Currently translated at 24.4% (661 of 2705 strings)

Translated using Weblate (Ukrainian)

Currently translated at 24.3% (659 of 2705 strings)

Translated using Weblate (Ukrainian)

Currently translated at 55.8% (422 of 755 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Ukrainian)

Currently translated at 55.4% (419 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 54.0% (408 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 53.7% (406 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (221 of 221 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (221 of 221 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.1% (208 of 221 strings)

Translated using Weblate (Italian)

Currently translated at 96.3% (213 of 221 strings)

Translated using Weblate (German)

Currently translated at 92.7% (205 of 221 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (221 of 221 strings)

Translated using Weblate (Ukrainian)

Currently translated at 24.1% (654 of 2705 strings)

Translated using Weblate (Ukrainian)

Currently translated at 53.5% (404 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (221 of 221 strings)

Translated using Weblate (German)

Currently translated at 92.3% (204 of 221 strings)

Translated using Weblate (Korean)

Currently translated at 75.0% (6 of 8 strings)

Translated using Weblate (Korean)

Currently translated at 77.7% (168 of 216 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (German)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Ukrainian)

Currently translated at 52.8% (399 of 755 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (739 of 739 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (German)

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (2705 of 2705 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Japanese)

Currently translated at 99.9% (2703 of 2705 strings)

Translated using Weblate (German)

Currently translated at 100.0% (2705 of 2705 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 93.7% (708 of 755 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (Korean)

Currently translated at 96.4% (54 of 56 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 93.1% (703 of 755 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (739 of 739 strings)

Translated using Weblate (Russian)

Currently translated at 99.8% (738 of 739 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (739 of 739 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (739 of 739 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (739 of 739 strings)

Translated using Weblate (German)

Currently translated at 99.9% (2703 of 2705 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (739 of 739 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Ukrainian)

Currently translated at 51.9% (392 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (732 of 732 strings)

Translated using Weblate (Polish)

Currently translated at 64.8% (1754 of 2705 strings)

Translated using Weblate (Polish)

Currently translated at 64.8% (1753 of 2705 strings)

Translated using Weblate (Polish)

Currently translated at 64.7% (1752 of 2705 strings)

Translated using Weblate (Polish)

Currently translated at 64.6% (1749 of 2705 strings)

Translated using Weblate (Polish)

Currently translated at 64.6% (1748 of 2705 strings)

Translated using Weblate (Polish)

Currently translated at 64.5% (1747 of 2705 strings)

Translated using Weblate (Polish)

Currently translated at 64.5% (1746 of 2705 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Polish)

Currently translated at 98.2% (57 of 58 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (German)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2705 of 2705 strings)

Translated using Weblate (Ukrainian)

Currently translated at 24.1% (652 of 2705 strings)

Translated using Weblate (Russian)

Currently translated at 99.8% (2702 of 2705 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2705 of 2705 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (2705 of 2705 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 51.5% (389 of 755 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (German)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Chinese (Hong Kong))

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (German)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Ukrainian)

Currently translated at 24.1% (652 of 2701 strings)

Translated using Weblate (Spanish)

Currently translated at 95.5% (2581 of 2701 strings)

Translated using Weblate (Ukrainian)

Currently translated at 50.5% (382 of 755 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 98.6% (141 of 143 strings)

Translated using Weblate (Bulgarian)

Currently translated at 65.0% (93 of 143 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 87.5% (2365 of 2701 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Polish)

Currently translated at 96.5% (56 of 58 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (732 of 732 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 89.3% (193 of 216 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 96.9% (127 of 131 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Ukrainian)

Currently translated at 24.1% (651 of 2701 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2701 of 2701 strings)

Translated using Weblate (Spanish)

Currently translated at 95.5% (2581 of 2701 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 92.8% (701 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 49.8% (376 of 755 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 74.8% (95 of 127 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 93.7% (134 of 143 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 88.8% (192 of 216 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 95.5% (386 of 404 strings)

Translated using Weblate (Spanish)

Currently translated at 95.5% (2581 of 2701 strings)

Translated using Weblate (German)

Currently translated at 99.5% (402 of 404 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (2701 of 2701 strings)

Translated using Weblate (Polish)

Currently translated at 95.3% (204 of 214 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (732 of 732 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (732 of 732 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (German)

Currently translated at 99.0% (400 of 404 strings)

Translated using Weblate (German)

Currently translated at 99.0% (400 of 404 strings)

Translated using Weblate (German)

Currently translated at 99.0% (400 of 404 strings)

Translated using Weblate (German)

Currently translated at 99.0% (400 of 404 strings)

Translated using Weblate (German)

Currently translated at 99.0% (400 of 404 strings)

Translated using Weblate (German)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (German)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (German)

Currently translated at 100.0% (216 of 216 strings)

Translated using Weblate (German)

Currently translated at 100.0% (216 of 216 strings)

Translated using Weblate (German)

Currently translated at 97.7% (395 of 404 strings)

Translated using Weblate (German)

Currently translated at 97.7% (395 of 404 strings)

Translated using Weblate (German)

Currently translated at 97.7% (395 of 404 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (German)

Currently translated at 97.7% (395 of 404 strings)

Translated using Weblate (Ukrainian)

Currently translated at 47.6% (360 of 755 strings)

Translated using Weblate (German)

Currently translated at 97.7% (395 of 404 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 95.0% (384 of 404 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Ukrainian)

Currently translated at 47.2% (357 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (732 of 732 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2 of 2 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (German)

Currently translated at 100.0% (732 of 732 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (German)

Currently translated at 97.5% (394 of 404 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 99.8% (731 of 732 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 99.3% (142 of 143 strings)

Translated using Weblate (German)

Currently translated at 97.2% (393 of 404 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 85.1% (184 of 216 strings)

Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Annika Frederike Schomber <nick.namen@gmx.de>
Co-authored-by: Benoit Hetru <me+hbtc@gahanka.net>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Danylo <nylo2005@gmail.com>
Co-authored-by: Dessie Z <desize1996@gmail.com>
Co-authored-by: Hexe des Windes (she/her) <krausanna1@gmail.com>
Co-authored-by: Ike Osenberg <ike.osenberg@gmail.com>
Co-authored-by: IvorTheBoneless <bohdanfiloenko657@gmail.com>
Co-authored-by: KC <stuffr123456@gmail.com>
Co-authored-by: KanI <twinklingnerd@gmail.com>
Co-authored-by: Kedr <sergeysamori.ua@gmail.com>
Co-authored-by: Khsmty <me@taigasaito.org>
Co-authored-by: Lena Kubisa <lenorek.05.poczta@gmail.com>
Co-authored-by: Lio Zam <zerofux@web.de>
Co-authored-by: LiziKnight <liziknight0316@outlook.com>
Co-authored-by: Mara S. (Dolichotis) <marascherzer@gmail.com>
Co-authored-by: Nakonana <nanaki1989@web.de>
Co-authored-by: Nazar Paruna <nazarparuna@gmail.com>
Co-authored-by: Raithe <RaitheOfDureya@gmail.com>
Co-authored-by: Sandra Marcial <sandramarcial80@gmail.com>
Co-authored-by: Sara López <sarayupy@gmail.com>
Co-authored-by: Sergey Shevelev <vlkgamer45@gmail.com>
Co-authored-by: Tobias Welti <tobias.welti@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Wolf Forst <wiesenkatz@proton.me>
Co-authored-by: mattya 226 <worldworld1114@gmail.com>
Co-authored-by: Естай <akseleu@yahoo.com>
Co-authored-by: 박동훈 <creator98@naver.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/de/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/uk/
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/it/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ru/
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/backgrounds/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/character/zh_Hant_HK/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/content/it/
Translate-URL: https://translate.habitica.com/projects/habitica/content/uk/
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/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/front/de/
Translate-URL: https://translate.habitica.com/projects/habitica/front/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/front/it/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/front/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hant/
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/it/
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/gear/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/zh_Hans/
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/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/noscript/pt_BR/
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/npc/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/ko/
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/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hant/
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/it/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
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/Character
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/Noscript
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Spells
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2022-12-06 19:42:51 +01:00
SabreCat
4fe8b63748 4.250.1 2022-12-05 14:47:18 -06:00
Natalie L
b5c64185f0 chore(tavern): update to remove moderators from tavern (#14393)
* chore(tavern): update to remove moderators from tavern

* fix(tavern): additional cleanup, change string in Vue instead of Weblate

* fix(git): correct target branch

Co-authored-by: SabreCat <sabe@habitica.com>
2022-12-05 14:46:25 -06:00
dependabot[bot]
debeee7569 build(deps): bump superagent from 8.0.4 to 8.0.5 (#14385)
Bumps [superagent](https://github.com/ladjs/superagent) from 8.0.4 to 8.0.5.
- [Release notes](https://github.com/ladjs/superagent/releases)
- [Changelog](https://github.com/ladjs/superagent/blob/master/HISTORY.md)
- [Commits](https://github.com/ladjs/superagent/compare/v8.0.4...v8.0.5)

---
updated-dependencies:
- dependency-name: superagent
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-05 12:47:52 -05:00
dependabot[bot]
64b8a28363 build(deps): bump decode-uri-component from 0.2.0 to 0.2.2 (#14383)
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-05 12:46:16 -05:00
SabreCat
894558f2df chore(images): update submodule 2022-12-02 13:21:32 -06:00
dependabot[bot]
57be0fbe45 build(deps): bump regenerator-runtime from 0.13.9 to 0.13.11 (#14370)
Bumps [regenerator-runtime](https://github.com/facebook/regenerator) from 0.13.9 to 0.13.11.
- [Release notes](https://github.com/facebook/regenerator/releases)
- [Commits](https://github.com/facebook/regenerator/compare/regenerator-runtime@0.13.9...regenerator-runtime@0.13.11)

---
updated-dependencies:
- dependency-name: regenerator-runtime
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-28 16:44:39 -05:00
dependabot[bot]
cf9fbd43bb build(deps): bump passport from 0.5.0 to 0.6.0 (#14357)
Bumps [passport](https://github.com/jaredhanson/passport) from 0.5.0 to 0.6.0.
- [Release notes](https://github.com/jaredhanson/passport/releases)
- [Changelog](https://github.com/jaredhanson/passport/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jaredhanson/passport/compare/v0.5.0...v0.6.0)

---
updated-dependencies:
- dependency-name: passport
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-28 16:36:36 -05:00
SabreCat
ea817eecf7 Merge branch 'release' into develop 2022-11-28 15:34:57 -06:00
SabreCat
f1381878e7 4.250.0 2022-11-28 15:34:44 -06:00
Natalie L
9bd039b17b chore(content): add December 2022 Mystery Items (#14379)
* chore(submodule): images

* chore(content): December Mystery Items

* chore(content): sprites

* fix(typo): whitespace

Co-authored-by: Sabe Jones <sabrecat@gmail.com>
2022-11-28 15:34:18 -06:00
dependabot[bot]
8804892135 build(deps): bump superagent from 8.0.3 to 8.0.4 (#14375)
Bumps [superagent](https://github.com/visionmedia/superagent) from 8.0.3 to 8.0.4.
- [Release notes](https://github.com/visionmedia/superagent/releases)
- [Changelog](https://github.com/visionmedia/superagent/blob/master/HISTORY.md)
- [Commits](https://github.com/visionmedia/superagent/compare/v8.0.3...v8.0.4)

---
updated-dependencies:
- dependency-name: superagent
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-28 16:32:23 -05:00
SabreCat
90b34c4dac fix(shops): correct imports 2022-11-23 13:23:43 -06:00
SabreCat
96a919ed4b fix(shops): quest countdowns too 2022-11-23 13:13:06 -06:00
SabreCat
e56b672226 4.249.7 2022-11-23 13:08:35 -06:00
SabreCat
91cbf7a2a9 fix(shops): show correct countdown outside of Gala 2022-11-23 13:08:23 -06:00
SabreCat
04e2a39a9f fix(test): rearrange for legacy event logic 2022-11-21 20:08:15 -06:00
SabreCat
bdd926e110 4.249.6 2022-11-21 18:55:03 -06:00
SabreCat
a8e9c9bc70 chore(event): set up Harvest Feast 2022 2022-11-21 18:54:57 -06:00
SabreCat
497073a714 4.249.5 2022-11-21 16:42:36 -06:00
SabreCat
f1fa6a8456 Revert "Allow sub upgrades/downgrades on iOS (#14303)"
This reverts commit 9e98e56e9b.
2022-11-21 16:40:56 -06:00
prexio
3f690c24da Change Wikia to Fandom (#14291) 2022-11-18 16:54:12 -06:00
Natalie L
f24d81d895 fix(content): quality of life change for staff and moderators (#14245)
* fix(content): quality of life change for staff and moderators

* fix(content): quality of life change for staff and moderators, I really mean it this time

Co-authored-by: Sabe Jones <sabrecat@gmail.com>
2022-11-18 16:52:09 -06:00
Sabe Jones
82c5e40b92 Enforce maximum password length (#14290)
* fix(auth): enforce maximum password length

* fix(auth): line length and better error message

* fix(auth): correctly import/export constant

Co-authored-by: SabreCat <sabe@habitica.com>
2022-11-18 16:49:10 -06:00
Adam Fitzgibbon
6b27e18699 add prop to task-wrapper so that the cursor remains consistant (#14302) 2022-11-18 16:48:34 -06:00
Adam Fitzgibbon
4f70a6fbf4 now grouping the stable A-Z sortBy option by first letter so that Show More functionality works like the other sort types (#14304) 2022-11-18 16:44:58 -06:00
Adam Fitzgibbon
300c2bb0a8 change currency to gold if the gear is owned (#14326)
* change currency to gold if the gear is owned

* fix linting error
2022-11-18 16:43:14 -06:00
Sky Chrastina
4b4f073089 Fix az sort (#14347)
* add stopword package

* sort pet and potion quests by stopword-ized text

* chore(package): revert package lock
Will update after merge

* fix(package): Friday brain

Co-authored-by: SabreCat <sabe@habitica.com>
2022-11-18 16:38:28 -06:00
Sky Chrastina
1d8e3d45a1 new check asset + css in shop, task, and buy gems (#14340) 2022-11-18 16:29:43 -06:00
Natalie L
116068effa fix(apiDescription): remove incorrect information (#14367) 2022-11-18 16:21:03 -06:00
SabreCat
f2aaee15f3 Merge branch 'release' into develop 2022-11-18 15:29:06 -06:00
SabreCat
06a8d2bbd7 4.249.4 2022-11-18 15:12:32 -06:00
SabreCat
15353eba8a fix(subs): roll all the way back, didn't work :( 2022-11-18 15:12:14 -06:00
SabreCat
febffb3f07 Revert "Fix double subscriptions, second attempt (#14345)"
This reverts commit 1a5cba57b7.
2022-11-18 14:26:49 -06:00
dependabot[bot]
25c7d52d6a build(deps-dev): bump chai from 4.3.6 to 4.3.7 (#14354)
Bumps [chai](https://github.com/chaijs/chai) from 4.3.6 to 4.3.7.
- [Release notes](https://github.com/chaijs/chai/releases)
- [Changelog](https://github.com/chaijs/chai/blob/4.x.x/History.md)
- [Commits](https://github.com/chaijs/chai/compare/v4.3.6...v4.3.7)

---
updated-dependencies:
- dependency-name: chai
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-18 12:01:23 -05:00
dependabot[bot]
837c1c20a3 build(deps-dev): bump sinon from 14.0.1 to 14.0.2 (#14353)
Bumps [sinon](https://github.com/sinonjs/sinon) from 14.0.1 to 14.0.2.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/main/docs/changelog.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v14.0.1...v14.0.2)

---
updated-dependencies:
- dependency-name: sinon
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-18 12:01:06 -05:00
dependabot[bot]
02b11a61bc build(deps): bump dompurify from 2.4.0 to 2.4.1 in /website/client (#14352)
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/2.4.0...2.4.1)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-18 12:00:51 -05:00
dependabot[bot]
a0e28f7db4 build(deps): bump superagent from 8.0.2 to 8.0.3 (#14351)
Bumps [superagent](https://github.com/visionmedia/superagent) from 8.0.2 to 8.0.3.
- [Release notes](https://github.com/visionmedia/superagent/releases)
- [Changelog](https://github.com/visionmedia/superagent/blob/master/HISTORY.md)
- [Commits](https://github.com/visionmedia/superagent/compare/v8.0.2...v8.0.3)

---
updated-dependencies:
- dependency-name: superagent
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-18 12:00:34 -05:00
SabreCat
fdfa2d6df4 Merge branch 'release' into develop 2022-11-15 19:29:37 -06:00
SabreCat
4fd2011be5 4.249.3 2022-11-15 19:29:18 -06:00
SabreCat
259131ee3f feat(transactions): UI updates
by @negue
2022-11-15 19:28:36 -06:00
Sabe Jones
1a5cba57b7 Fix double subscriptions, second attempt (#14345)
* fix(subscriptions): reject subs that come in too fast

* fix(lint): remove unused import

* fix(groups): individual subs may come rapidly

* fix(subscriptions): bad paren, handle rapid testing

* fix(test): reset dateUpdated between subs

* fix(test): one more block for dateUpdated

Co-authored-by: SabreCat <sabe@habitica.com>
2022-11-15 19:19:37 -06:00
SabreCat
5e05190f22 fix(event): start at 8AM not 8PM 2022-11-15 16:05:06 -06:00
SabreCat
81540ef399 fix(events): EST now not EDT 2022-11-14 14:38:58 -06:00
SabreCat
2bbff36cc8 4.249.2 2022-11-14 14:13:13 -06:00
SabreCat
9f52e47011 feat(content): November Quests and Hatching Potions
by @CuriousMagpie
2022-11-14 14:13:08 -06:00
Alys
4dca69f14b change the bannedWordUsed text as discussed with beffymaroo 2022-11-12 18:43:28 +10:00
dependabot[bot]
1378b1e1ad build(deps): bump @babel/preset-env from 7.19.1 to 7.20.2 (#14330)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.19.1 to 7.20.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.20.2/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-10 10:25:23 -05:00
dependabot[bot]
734a611a5c build(deps-dev): bump chalk from 5.1.0 to 5.1.2 (#14292)
Bumps [chalk](https://github.com/chalk/chalk) from 5.1.0 to 5.1.2.
- [Release notes](https://github.com/chalk/chalk/releases)
- [Commits](https://github.com/chalk/chalk/compare/v5.1.0...v5.1.2)

---
updated-dependencies:
- dependency-name: chalk
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-10 10:24:27 -05:00
SabreCat
dbd485cb96 4.249.1 2022-11-09 16:11:49 -06:00
CuriousMagpie
4c62a48f5d chore(typo): who knew, that Y was actually important... 2022-11-09 16:11:45 -06:00
SabreCat
11496f3e0c Merge branch 'release' into develop 2022-11-09 15:33:32 -06:00
dependabot[bot]
9a3e3aaf42 build(deps): bump superagent from 8.0.2 to 8.0.3 (#14320)
Bumps [superagent](https://github.com/visionmedia/superagent) from 8.0.2 to 8.0.3.
- [Release notes](https://github.com/visionmedia/superagent/releases)
- [Changelog](https://github.com/visionmedia/superagent/blob/master/HISTORY.md)
- [Commits](https://github.com/visionmedia/superagent/compare/v8.0.2...v8.0.3)

---
updated-dependencies:
- dependency-name: superagent
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-09 14:16:10 -05:00
dependabot[bot]
d9250fd780 build(deps): bump stripe from 10.13.0 to 10.16.0 (#14332)
Bumps [stripe](https://github.com/stripe/stripe-node) from 10.13.0 to 10.16.0.
- [Release notes](https://github.com/stripe/stripe-node/releases)
- [Changelog](https://github.com/stripe/stripe-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stripe/stripe-node/compare/v10.13.0...v10.16.0)

---
updated-dependencies:
- dependency-name: stripe
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-08 16:27:13 -05:00
dependabot[bot]
70a5124815 build(deps): bump passport from 0.5.0 to 0.6.0 (#14341)
Bumps [passport](https://github.com/jaredhanson/passport) from 0.5.0 to 0.6.0.
- [Release notes](https://github.com/jaredhanson/passport/releases)
- [Changelog](https://github.com/jaredhanson/passport/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jaredhanson/passport/compare/v0.5.0...v0.6.0)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-08 16:26:14 -05:00
dependabot[bot]
532fa2816b build(deps): bump @babel/core from 7.19.6 to 7.20.2 (#14335)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.19.6 to 7.20.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.20.2/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-08 16:25:53 -05:00
dependabot[bot]
d22f191f83 build(deps-dev): bump chai from 4.3.6 to 4.3.7 (#14342)
Bumps [chai](https://github.com/chaijs/chai) from 4.3.6 to 4.3.7.
- [Release notes](https://github.com/chaijs/chai/releases)
- [Changelog](https://github.com/chaijs/chai/blob/4.x.x/History.md)
- [Commits](https://github.com/chaijs/chai/compare/v4.3.6...v4.3.7)

---
updated-dependencies:
- dependency-name: chai
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-08 16:25:20 -05:00
dependabot[bot]
2b49a800a5 build(deps-dev): bump sinon from 14.0.1 to 14.0.2 (#14343)
Bumps [sinon](https://github.com/sinonjs/sinon) from 14.0.1 to 14.0.2.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/main/docs/changelog.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v14.0.1...v14.0.2)

---
updated-dependencies:
- dependency-name: sinon
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-08 16:24:53 -05:00
SabreCat
0db927c726 4.249.0 2022-11-08 10:18:10 -06:00
SabreCat
6ee06f76e4 chore(i18n): update locale files
Also includes two fixes: empty Hourglass transaction logging and missing Recovery + Support guild category
2022-11-08 10:17:40 -06:00
SabreCat
978e8c4320 Merge branch 'release' into develop 2022-11-08 10:08:02 -06:00
Sabe Jones
5c7d537c61 Armoire and Backgrounds 2022/11 (#14329)
* chore(content): Add November Backgrounds and Enchanted Armoire Items

* fix(strings): typos and fullstops

* fix(style): de-whitespace

Co-authored-by: CuriousMagpie <eilatan@gmail.com>
2022-11-08 10:03:25 -06:00
Natalie L
0e6ece95a4 chore(fix): restore missing string (#14338)
* fix(string): questVice1Notes html changed to a mobile-device friendly format

* fix(strings): updated limited.json with "dateEnd" & "monthYYYY" months & put in chronological order

* fix(string): remove extra word from headSpecialSummer2022WarriorNotes

* fix(string): corrected armorSpecialSummer2022MageNotes

* fix: remove duplicated string and adjust upgrade button style

* fix(style): set border radii to 8px on upgrading-group id

* fix(payments): remove duplicate entry from another modal

* chore(fix): restore string inadvertently removed during a refactor

* chore(fix): comma dangle

Co-authored-by: SabreCat <sabe@habitica.com>
2022-11-08 09:59:40 -06:00
Weblate
b08ed8b0fb Translated using Weblate (Spanish)
Currently translated at 95.5% (2581 of 2701 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Spanish)

Currently translated at 95.5% (2581 of 2701 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (732 of 732 strings)

Translated using Weblate (German)

Currently translated at 97.0% (392 of 404 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (German)

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (German)

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 87.4% (2363 of 2701 strings)

Translated using Weblate (German)

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (German)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 97.3% (735 of 755 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 90.9% (190 of 209 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 96.1% (126 of 131 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 88.1% (356 of 404 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 91.9% (2483 of 2701 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 95.0% (172 of 181 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Spanish)

Currently translated at 95.5% (2581 of 2701 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (German)

Currently translated at 96.5% (390 of 404 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (143 of 143 strings)

Translated using Weblate (German)

Currently translated at 98.6% (141 of 143 strings)

Translated using Weblate (German)

Currently translated at 97.0% (391 of 403 strings)

Translated using Weblate (German)

Currently translated at 97.0% (391 of 403 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (Dutch)

Currently translated at 88.1% (2381 of 2701 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (2701 of 2701 strings)

Translated using Weblate (Dutch)

Currently translated at 96.9% (710 of 732 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (732 of 732 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (Spanish)

Currently translated at 95.5% (2581 of 2701 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.4% (728 of 732 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (German)

Currently translated at 99.1% (726 of 732 strings)

Translated using Weblate (German)

Currently translated at 100.0% (131 of 131 strings)

Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Dante S <dantepicachu11@gmail.com>
Co-authored-by: Hexe des Windes (she/her) <krausanna1@gmail.com>
Co-authored-by: Ike Osenberg <ike.osenberg@gmail.com>
Co-authored-by: Jonathan Garcia <jonathangarcia0@duck.com>
Co-authored-by: LiziKnight <liziknight0316@outlook.com>
Co-authored-by: Mara S. (Dolichotis) <marascherzer@gmail.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Nazar Paruna <nazarparuna@gmail.com>
Co-authored-by: Rhoslyn Ross <sofrdlf@vivaldi.net>
Co-authored-by: Sandra Marcial <sandramarcial80@gmail.com>
Co-authored-by: Sergey Shevelev <vlkgamer45@gmail.com>
Co-authored-by: UNI <nibi727171@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Wolf Forst <wiesenkatz@proton.me>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/de/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/it/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
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/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/uk/
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/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/front/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/es_419/
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/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/de/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/it/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pt_BR/
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/questscontent/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/it/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Groups
Translation: Habitica/Npc
Translation: Habitica/Questscontent
Translation: Habitica/Subscriber
2022-11-08 16:57:17 +01:00
SabreCat
fafaa29d72 4.248.4 2022-11-04 16:10:31 -05:00
SabreCat
3a088de7e8 fix(package): roll back to Passport 0.5 2022-11-04 16:10:22 -05:00
SabreCat
835da85119 4.248.3 2022-11-02 15:18:19 -05:00
SabreCat
f6e5360bdd Merge branch 'release' into develop 2022-11-02 14:54:09 -05:00
SabreCat
eee8ad2029 fix(css): correct broken task creation dropdown on groups 2022-11-02 14:19:43 -05:00
SabreCat
c7e73f9b85 4.248.2 2022-11-02 09:04:03 -05:00
SabreCat
9b1a726875 Revert "feat(subs): allow upgrade/downgrade on iOS"
This reverts commit 27440772f0.
2022-11-02 09:00:29 -05:00
SabreCat
accf7e2897 4.248.1 2022-11-01 21:10:41 -05:00
Phillip Thelen
9e98e56e9b Allow sub upgrades/downgrades on iOS (#14303)
* Allow sub upgrades/downgrades on iOS

* fix check

* fix(lint): line length

* fix(typo): customER

* fix tests

* Implement correct handling for when subs are up/downgraded

* fix lint errors

* fix test

Co-authored-by: SabreCat <sabe@habitica.com>
Co-authored-by: Sabe Jones <sabrecat@gmail.com>
2022-11-01 21:07:23 -05:00
SabreCat
6c85b1e047 chore(faq): update note on mobile Group Plans 2022-11-01 20:59:32 -05:00
SabreCat
7c553e535c feat(gifting): restore ability to send gift messages
by @CuriousMagpie
2022-11-01 20:56:21 -05:00
Natalie L
c16207c9ba Add Gift Messaging to Success Modal (#14270)
* initial commit: based on group-tracking-modal branch

* chore: merge group-tracking-modal

* update: create functions for each success condition

* chore: merge develop

* chore: work on successModal.vue & remove redundant code on groupPlan.vue

* update: remove `giftSubscriptionText4` from footer

* fix: correct groupPlan.vue file

* update: add messaging placeholder, clean up logic in a few places, update/add strings

* update: rearrange modal in order of display & test existence of 'gift-subscription' paymentType

* update: added props for receiverName so 'gift-subscription' works

* update: add close.svg & function
style: refactor CSS

* update: work on gift messaging

* update: work on gift messaging

* update: work on gift messaging

* update: let's make messages GO

* update: messages are a GO, we have LIFT OFF!

* fix: remove console log (oops)

Co-authored-by: SabreCat <sabe@habitica.com>
2022-11-01 20:55:40 -05:00
SabreCat
27440772f0 feat(subs): allow upgrade/downgrade on iOS
by @phillipthelen
2022-11-01 20:53:04 -05:00
SabreCat
aea0be3245 fix(subs): establish lock to prevent race condition 2022-11-01 20:47:21 -05:00
SabreCat
a2d5211b00 4.248.0 2022-10-31 14:28:24 -05:00
Weblate
53fb28cc48 Merge branch 'origin/develop' into Weblate. 2022-10-31 20:27:10 +01:00
SabreCat
1c0710b45b Merge branch 'release' into develop 2022-10-31 14:25:19 -05:00
Natalie L
3bc82a6692 chore(content): add Bone to Pick achievement (#14318)
* chore(content): add Bone to Pick achievement

* chore(content): update spritesmith-main.css

* chore(content): add more bone picking

* chore(content): more bone picking

* chore(content): bone picking

* chore(content): i gotta bone to pick here

* chore(content): final bone picking

* chore: add migration script

* chore: update habitica-images

Co-authored-by: SabreCat <sabe@habitica.com>
2022-10-31 14:19:56 -05:00
Weblate
2add227b97 Translated using Weblate (Russian)
Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (Russian)

Currently translated at 99.8% (2698 of 2701 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2701 of 2701 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (2701 of 2701 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (732 of 732 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (732 of 732 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (732 of 732 strings)

Translated using Weblate (German)

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (German)

Currently translated at 100.0% (2701 of 2701 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (209 of 209 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2701 of 2701 strings)

Translated using Weblate (German)

Currently translated at 99.8% (2696 of 2701 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (732 of 732 strings)

Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: LiziKnight <liziknight0316@outlook.com>
Co-authored-by: Mara S. (Dolichotis) <marascherzer@gmail.com>
Co-authored-by: Sandra Marcial <sandramarcial80@gmail.com>
Co-authored-by: Sergey Shevelev <vlkgamer45@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/backgrounds/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/it/
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/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/it/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translation: Habitica/Backgrounds
Translation: Habitica/Gear
Translation: Habitica/Subscriber
2022-10-31 17:53:38 +01:00
CuriousMagpie
4cc1f902c8 Merge remote-tracking branch 'upstream/develop' into develop 2022-10-31 10:47:24 -04:00
CuriousMagpie
1b52529822 chore(ladder update): 2022 Habitoween Ladder 2022-10-31 10:46:51 -04:00
SabreCat
e39a5a0628 fix(strings): set token typo 2022-10-30 20:15:37 -05:00
SabreCat
575aea2605 4.247.0 2022-10-30 19:56:10 -05:00
Weblate
222ba544d7 Merge branch 'origin/develop' into Weblate. 2022-10-31 01:55:41 +01:00
SabreCat
2372efa22e Merge branch 'release' into develop 2022-10-30 19:54:18 -05:00
Natalie L
b5c950ac96 chore(content): add November 2022 Mystery Items (#14317)
* chore(content): add November Mystery Items

* chore: merge release into 2022-11-mystery-items

* Revert "chore: merge release into 2022-11-mystery-items"

This reverts commit e12b61d73f.

Co-authored-by: SabreCat <sabe@habitica.com>
2022-10-30 19:52:55 -05:00
Weblate
18ec3eb355 Translated using Weblate (Spanish)
Currently translated at 95.7% (2581 of 2695 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 87.1% (2350 of 2695 strings)

Translated using Weblate (Spanish)

Currently translated at 95.7% (2581 of 2695 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (208 of 208 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 87.0% (2347 of 2695 strings)

Translated using Weblate (Spanish)

Currently translated at 95.7% (2580 of 2695 strings)

Translated using Weblate (Spanish)

Currently translated at 95.7% (2580 of 2695 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 87.0% (2345 of 2695 strings)

Translated using Weblate (Ukrainian)

Currently translated at 24.0% (647 of 2695 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (216 of 216 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (403 of 403 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2695 of 2695 strings)

Translated using Weblate (Ukrainian)

Currently translated at 23.4% (632 of 2695 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 86.9% (2343 of 2695 strings)

Translated using Weblate (German)

Currently translated at 95.5% (385 of 403 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 86.8% (2341 of 2695 strings)

Translated using Weblate (German)

Currently translated at 94.7% (382 of 403 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 86.7% (2339 of 2695 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Polish)

Currently translated at 74.1% (43 of 58 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 86.7% (2337 of 2695 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.5% (401 of 403 strings)

Translated using Weblate (Ukrainian)

Currently translated at 22.9% (619 of 2695 strings)

Translated using Weblate (Russian)

Currently translated at 99.8% (2692 of 2695 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 86.6% (2335 of 2695 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 86.6% (2334 of 2695 strings)

Translated using Weblate (Spanish)

Currently translated at 95.7% (2580 of 2695 strings)

Translated using Weblate (German)

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2695 of 2695 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (403 of 403 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (216 of 216 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Spanish)

Currently translated at 99.5% (233 of 234 strings)

Translated using Weblate (Spanish)

Currently translated at 95.7% (2580 of 2695 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 86.5% (2332 of 2695 strings)

Translated using Weblate (German)

Currently translated at 91.5% (369 of 403 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 86.1% (2322 of 2695 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (216 of 216 strings)

Translated using Weblate (German)

Currently translated at 99.1% (232 of 234 strings)

Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Ike Osenberg <ike.osenberg@gmail.com>
Co-authored-by: IvorTheBoneless <bohdanfiloenko657@gmail.com>
Co-authored-by: Lena Kubisa <lenorek.05.poczta@gmail.com>
Co-authored-by: LiziKnight <liziknight0316@outlook.com>
Co-authored-by: Mara S. (Dolichotis) <marascherzer@gmail.com>
Co-authored-by: Sandra Marcial <sandramarcial80@gmail.com>
Co-authored-by: Sara López <sarayupy@gmail.com>
Co-authored-by: Sergey Shevelev <vlkgamer45@gmail.com>
Co-authored-by: UNI <nibi727171@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pl/
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/gear/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ja/
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/npc/es/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/es/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/it/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Npc
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Subscriber
2022-10-28 18:35:58 +02:00
dependabot[bot]
62b4315b3d build(deps): bump apidoc from 0.53.0 to 0.53.1 (#14307)
Bumps [apidoc](https://github.com/apidoc/apidoc) from 0.53.0 to 0.53.1.
- [Release notes](https://github.com/apidoc/apidoc/releases)
- [Changelog](https://github.com/apidoc/apidoc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/apidoc/apidoc/compare/0.53.0...0.53.1)

---
updated-dependencies:
- dependency-name: apidoc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-28 11:24:26 -04:00
dependabot[bot]
56805e6c90 build(deps): bump rate-limiter-flexible from 2.3.11 to 2.4.0 (#14309)
Bumps [rate-limiter-flexible](https://github.com/animir/node-rate-limiter-flexible) from 2.3.11 to 2.4.0.
- [Release notes](https://github.com/animir/node-rate-limiter-flexible/releases)
- [Commits](https://github.com/animir/node-rate-limiter-flexible/compare/v2.3.11...v2.4.0)

---
updated-dependencies:
- dependency-name: rate-limiter-flexible
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-28 11:23:56 -04:00
dependabot[bot]
0c6070dd9a build(deps): bump short-uuid from 4.2.0 to 4.2.2 (#14311)
Bumps [short-uuid](https://github.com/oculus42/short-uuid) from 4.2.0 to 4.2.2.
- [Release notes](https://github.com/oculus42/short-uuid/releases)
- [Changelog](https://github.com/oculus42/short-uuid/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/oculus42/short-uuid/commits)

---
updated-dependencies:
- dependency-name: short-uuid
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-28 11:23:30 -04:00
dependabot[bot]
19c26c01e3 build(deps): bump @babel/core from 7.19.3 to 7.19.6 (#14312)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.19.3 to 7.19.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.19.6/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-28 11:23:09 -04:00
dependabot[bot]
0f3bc980d9 build(deps): bump smartbanner.js in /website/client (#14313)
Bumps [smartbanner.js](https://github.com/ain/smartbanner.js) from 1.19.0 to 1.19.1.
- [Release notes](https://github.com/ain/smartbanner.js/releases)
- [Commits](https://github.com/ain/smartbanner.js/compare/v1.19.0...v1.19.1)

---
updated-dependencies:
- dependency-name: smartbanner.js
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-28 11:22:40 -04:00
dependabot[bot]
7f87120d34 build(deps): bump core-js from 3.25.5 to 3.26.0 in /website/client (#14314)
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.25.5 to 3.26.0.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/commits/v3.26.0/packages/core-js)

---
updated-dependencies:
- dependency-name: core-js
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-28 11:22:19 -04:00
negue
f7a03d2eb5 improve transactions logs + split createSubscription (#14289)
* improve transactions logs + split createSubscription
2022-10-27 08:39:06 +02:00
Sabe Jones
90250d1a25 Establish lock to avoid race scenario in subscriptions (#14267)
* fix(subscription): establish lock to avoid race scenario

* fix(lint): import syntax

* fix(lint): whitespace, dependency cycle

* fix(subs): skip locking on gifts and groups

* fix(subs): correctly reset _subSignature

* fix(sub): use findOneAndUpdate for unlock

* fix(test): save newly created user for some reason

Co-authored-by: SabreCat <sabe@habitica.com>
2022-10-25 16:44:33 -05:00
Gabriel Araujo
22a0c72f6e Update docker-compose.dev.yml to prevent error message on first build (#14178)
* Remove deprecated version element from top-level

* Remove unncessary image for client and server services

Co-authored-by: Gabriel Araujo <gabriel.goncalves@solarisbank.de>
2022-10-12 15:53:05 -05:00
Anton de Regt
a4326498d1 Group category update issue (#14186)
* Overwrite arrays instead of merging

* Test removing a category

* Fix previous duplicated categories on group edit
2022-10-12 15:51:56 -05:00
Alex
8f26a22bd4 Update test.yml (#14187) 2022-10-12 15:47:46 -05:00
Antonio Sansotta
0b2cf5bceb Fixes issue #14175 - Consolidate Challenge category options list to common file (#14201)
* Issue #14175 - Created a categoryOptions.js file in website/common/script/content to store list of category Options. Imported and added the list to website/client/src/components : challenges/challengeModal, challenges/sidebar, groups/groupFormModal, and groups/groupSidebar. This replaced the statically typed lists that previously existed. Tested the challengeModal and sidebar but unable to test the groups files due to credit card requirements. -@Tundrian

* Issue #14175 - Created a categoryOptions.js file in website/common/script/content to store list of category Options. Imported and added the list to website/client/src/components : challenges/challengeModal, challenges/sidebar, groups/groupFormModal, and groups/groupSidebar. This replaced the statically typed lists that previously existed. Tested the challengeModal and sidebar but unable to test the groups files due to credit card requirements. -@Tundrian

Co-authored-by: SabreCat <sabe@habitica.com>
2022-10-12 15:46:36 -05:00
Sky Chrastina
f43a0d8289 Add checkmark to completed quests in shop (#14269)
* add 'completed' property to quest items

* show checkmark on completed quests in the shop

* add 'completed' property to quest items

* show checkmark on completed quests in the shop
2022-10-12 15:44:22 -05:00
dependabot[bot]
39be8db4f9 build(deps): bump axios from 0.25.0 to 0.27.2 in /website/client (#14007)
Bumps [axios](https://github.com/axios/axios) from 0.25.0 to 0.27.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.25.0...v0.27.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 17:34:16 -05:00
SabreCat
f0a1f11a16 Merge branch 'release' into develop 2022-10-11 17:32:25 -05:00
dependabot[bot]
84c4b3536c build(deps): bump minimist, minimist and mkdirp in /website/client (#14286)
Bumps [minimist](https://github.com/minimistjs/minimist), [minimist](https://github.com/minimistjs/minimist) and [mkdirp](https://github.com/isaacs/node-mkdirp). These dependencies needed to be updated together.

Updates `minimist` from 1.2.5 to 1.2.7
- [Release notes](https://github.com/minimistjs/minimist/releases)
- [Changelog](https://github.com/minimistjs/minimist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/minimistjs/minimist/compare/v1.2.5...v1.2.7)

Updates `minimist` from 1.2.0 to 1.2.7
- [Release notes](https://github.com/minimistjs/minimist/releases)
- [Changelog](https://github.com/minimistjs/minimist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/minimistjs/minimist/compare/v1.2.5...v1.2.7)

Updates `mkdirp` from 0.5.1 to 0.5.6
- [Release notes](https://github.com/isaacs/node-mkdirp/releases)
- [Changelog](https://github.com/isaacs/node-mkdirp/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-mkdirp/compare/0.5.1...v0.5.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
- dependency-name: minimist
  dependency-type: indirect
- dependency-name: mkdirp
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 15:53:10 -05:00
dependabot[bot]
cf834f57d7 build(deps): bump ansi-html and webpack-dev-server in /website/client (#14285)
Removes [ansi-html](https://github.com/Tjatse/ansi-html). It's no longer used after updating ancestor dependency [webpack-dev-server](https://github.com/webpack/webpack-dev-server). These dependencies need to be updated together.


Removes `ansi-html`

Updates `webpack-dev-server` from 3.11.2 to 3.11.3
- [Release notes](https://github.com/webpack/webpack-dev-server/releases)
- [Changelog](https://github.com/webpack/webpack-dev-server/blob/v3.11.3/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-server/compare/v3.11.2...v3.11.3)

---
updated-dependencies:
- dependency-name: ansi-html
  dependency-type: indirect
- dependency-name: webpack-dev-server
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 15:40:45 -05:00
dependabot[bot]
97be341ff6 build(deps): bump jwks-rsa from 2.1.4 to 2.1.5 (#14284)
Bumps [jwks-rsa](https://github.com/auth0/node-jwks-rsa) from 2.1.4 to 2.1.5.
- [Release notes](https://github.com/auth0/node-jwks-rsa/releases)
- [Changelog](https://github.com/auth0/node-jwks-rsa/blob/master/CHANGELOG.md)
- [Commits](https://github.com/auth0/node-jwks-rsa/compare/v2.1.4...v2.1.5)

---
updated-dependencies:
- dependency-name: jwks-rsa
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 15:40:30 -05:00
dependabot[bot]
15c68abafa build(deps): bump stripe from 8.222.0 to 10.13.0 (#14273)
Bumps [stripe](https://github.com/stripe/stripe-node) from 8.222.0 to 10.13.0.
- [Release notes](https://github.com/stripe/stripe-node/releases)
- [Changelog](https://github.com/stripe/stripe-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stripe/stripe-node/compare/v8.222.0...v10.13.0)

---
updated-dependencies:
- dependency-name: stripe
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 15:24:55 -05:00
dependabot[bot]
21a1b9449b build(deps): bump body-parser from 1.20.0 to 1.20.1 (#14271)
Bumps [body-parser](https://github.com/expressjs/body-parser) from 1.20.0 to 1.20.1.
- [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.0...1.20.1)

---
updated-dependencies:
- dependency-name: body-parser
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 15:22:57 -05:00
dependabot[bot]
0ec7784fb1 build(deps): bump @vue/cli-plugin-router in /website/client (#14121)
Bumps [@vue/cli-plugin-router](https://github.com/vuejs/vue-cli/tree/HEAD/packages/@vue/cli-plugin-router) from 4.5.15 to 5.0.8.
- [Release notes](https://github.com/vuejs/vue-cli/releases)
- [Changelog](https://github.com/vuejs/vue-cli/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vue-cli/commits/v5.0.8/packages/@vue/cli-plugin-router)

---
updated-dependencies:
- dependency-name: "@vue/cli-plugin-router"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:59:32 -05:00
dependabot[bot]
9ddd0f29d0 build(deps): bump passport from 0.5.0 to 0.6.0 (#14043)
Bumps [passport](https://github.com/jaredhanson/passport) from 0.5.0 to 0.6.0.
- [Release notes](https://github.com/jaredhanson/passport/releases)
- [Changelog](https://github.com/jaredhanson/passport/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jaredhanson/passport/compare/v0.5.0...v0.6.0)

---
updated-dependencies:
- dependency-name: passport
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:57:26 -05:00
dependabot[bot]
37791dfe8d build(deps): bump intro.js from 5.1.0 to 6.0.0 in /website/client (#14122)
Bumps [intro.js](https://github.com/usablica/intro.js) from 5.1.0 to 6.0.0.
- [Release notes](https://github.com/usablica/intro.js/releases)
- [Commits](https://github.com/usablica/intro.js/compare/v5.1.0...v6.0.0)

---
updated-dependencies:
- dependency-name: intro.js
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:56:22 -05:00
dependabot[bot]
0322b657b8 build(deps): bump winston from 3.8.1 to 3.8.2 (#14221)
Bumps [winston](https://github.com/winstonjs/winston) from 3.8.1 to 3.8.2.
- [Release notes](https://github.com/winstonjs/winston/releases)
- [Changelog](https://github.com/winstonjs/winston/blob/master/CHANGELOG.md)
- [Commits](https://github.com/winstonjs/winston/compare/v3.8.1...v3.8.2)

---
updated-dependencies:
- dependency-name: winston
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:55:34 -05:00
dependabot[bot]
cc39f6e4e9 build(deps): bump vue-router from 3.5.4 to 3.6.5 in /website/client (#14228)
Bumps [vue-router](https://github.com/vuejs/router) from 3.5.4 to 3.6.5.
- [Release notes](https://github.com/vuejs/router/releases)
- [Commits](https://github.com/vuejs/router/commits)

---
updated-dependencies:
- dependency-name: vue-router
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:55:11 -05:00
dependabot[bot]
452b516c67 build(deps): bump amplitude-js from 8.21.0 to 8.21.1 in /website/client (#14248)
Bumps [amplitude-js](https://github.com/amplitude/amplitude-javascript) from 8.21.0 to 8.21.1.
- [Release notes](https://github.com/amplitude/amplitude-javascript/releases)
- [Changelog](https://github.com/amplitude/Amplitude-JavaScript/blob/main/CHANGELOG.md)
- [Commits](https://github.com/amplitude/amplitude-javascript/compare/v8.21.0...v8.21.1)

---
updated-dependencies:
- dependency-name: amplitude-js
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:53:17 -05:00
dependabot[bot]
235eae32b0 build(deps): bump rate-limiter-flexible from 2.3.10 to 2.3.11 (#14249)
Bumps [rate-limiter-flexible](https://github.com/animir/node-rate-limiter-flexible) from 2.3.10 to 2.3.11.
- [Release notes](https://github.com/animir/node-rate-limiter-flexible/releases)
- [Commits](https://github.com/animir/node-rate-limiter-flexible/commits)

---
updated-dependencies:
- dependency-name: rate-limiter-flexible
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:52:59 -05:00
dependabot[bot]
de9f1be7b9 build(deps): bump js2xmlparser from 4.0.2 to 5.0.0 (#14251)
Bumps [js2xmlparser](https://github.com/michaelkourlas/node-js2xmlparser) from 4.0.2 to 5.0.0.
- [Release notes](https://github.com/michaelkourlas/node-js2xmlparser/releases)
- [Changelog](https://github.com/michaelkourlas/node-js2xmlparser/blob/master/CHANGES.md)
- [Commits](https://github.com/michaelkourlas/node-js2xmlparser/compare/v4.0.2...v5.0.0)

---
updated-dependencies:
- dependency-name: js2xmlparser
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:51:58 -05:00
dependabot[bot]
e75610447f build(deps): bump @hapi/hoek from 8.3.1 to 8.5.1 in /website/client (#14255)
Bumps [@hapi/hoek](https://github.com/hapijs/hoek) from 8.3.1 to 8.5.1.
- [Release notes](https://github.com/hapijs/hoek/releases)
- [Commits](https://github.com/hapijs/hoek/compare/v8.3.1...v8.5.1)

---
updated-dependencies:
- dependency-name: "@hapi/hoek"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:51:37 -05:00
dependabot[bot]
bd4c65cd3e build(deps): bump @babel/core from 7.18.13 to 7.19.3 (#14256)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.18.13 to 7.19.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.19.3/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:50:39 -05:00
dependabot[bot]
baf60dc951 build(deps): bump @google-cloud/trace-agent from 5.1.6 to 7.1.2 (#14259)
Bumps [@google-cloud/trace-agent](https://github.com/googleapis/cloud-trace-nodejs) from 5.1.6 to 7.1.2.
- [Release notes](https://github.com/googleapis/cloud-trace-nodejs/releases)
- [Changelog](https://github.com/googleapis/cloud-trace-nodejs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/cloud-trace-nodejs/compare/v5.1.6...v7.1.2)

---
updated-dependencies:
- dependency-name: "@google-cloud/trace-agent"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:50:14 -05:00
dependabot[bot]
70e88d601c build(deps): bump superagent from 7.1.6 to 8.0.2 (#14272)
Bumps [superagent](https://github.com/visionmedia/superagent) from 7.1.6 to 8.0.2.
- [Release notes](https://github.com/visionmedia/superagent/releases)
- [Changelog](https://github.com/visionmedia/superagent/blob/master/HISTORY.md)
- [Commits](https://github.com/visionmedia/superagent/compare/v7.1.6...v8.0.2)

---
updated-dependencies:
- dependency-name: superagent
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:46:33 -05:00
dependabot[bot]
104ec60adb build(deps): bump bcrypt from 5.0.1 to 5.1.0 (#14274)
Bumps [bcrypt](https://github.com/kelektiv/node.bcrypt.js) from 5.0.1 to 5.1.0.
- [Release notes](https://github.com/kelektiv/node.bcrypt.js/releases)
- [Changelog](https://github.com/kelektiv/node.bcrypt.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kelektiv/node.bcrypt.js/compare/v5.0.1...v5.1.0)

---
updated-dependencies:
- dependency-name: bcrypt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:46:15 -05:00
dependabot[bot]
e97454e0e7 build(deps): bump async from 2.6.3 to 3.2.3 (#14275)
Bumps [async](https://github.com/caolan/async) from 2.6.3 to 3.2.3.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/master/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v3.2.3)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:45:14 -05:00
dependabot[bot]
144baa98b1 build(deps-dev): bump sinon from 13.0.2 to 14.0.1 (#14276)
Bumps [sinon](https://github.com/sinonjs/sinon) from 13.0.2 to 14.0.1.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/main/docs/changelog.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v13.0.2...v14.0.1)

---
updated-dependencies:
- dependency-name: sinon
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:44:22 -05:00
dependabot[bot]
02e33853b1 build(deps-dev): bump chalk from 4.1.2 to 5.1.0 (#14277)
Bumps [chalk](https://github.com/chalk/chalk) from 4.1.2 to 5.1.0.
- [Release notes](https://github.com/chalk/chalk/releases)
- [Commits](https://github.com/chalk/chalk/compare/v4.1.2...v5.1.0)

---
updated-dependencies:
- dependency-name: chalk
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:43:40 -05:00
dependabot[bot]
8c0d41d084 build(deps): bump express from 4.18.1 to 4.18.2 (#14280)
Bumps [express](https://github.com/expressjs/express) from 4.18.1 to 4.18.2.
- [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.18.1...4.18.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:42:41 -05:00
dependabot[bot]
9d4f70371d build(deps): bump core-js from 3.24.1 to 3.25.5 in /website/client (#14282)
Bumps [core-js](https://github.com/zloirock/core-js) from 3.24.1 to 3.25.5.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/compare/v3.24.1...v3.25.5)

---
updated-dependencies:
- dependency-name: core-js
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 14:42:22 -05:00
SabreCat
57a090eea1 4.246.0 2022-10-11 11:59:08 -05:00
SabreCat
18534a21ff Merge branch 'develop' into release 2022-10-11 11:59:02 -05:00
Weblate
ed0a36a287 Merge branch 'origin/develop' into Weblate. 2022-10-11 18:58:07 +02:00
Weblate
e50f240366 Translated using Weblate (Polish)
Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 86.0% (2319 of 2695 strings)

Translated using Weblate (German)

Currently translated at 99.5% (215 of 216 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Spanish)

Currently translated at 95.0% (2562 of 2695 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (216 of 216 strings)

Translated using Weblate (Polish)

Currently translated at 94.6% (686 of 725 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (403 of 403 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (German)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Spanish)

Currently translated at 95.0% (2562 of 2695 strings)

Translated using Weblate (French)

Currently translated at 97.2% (392 of 403 strings)

Translated using Weblate (German)

Currently translated at 91.3% (368 of 403 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (German)

Currently translated at 98.2% (57 of 58 strings)

Translated using Weblate (German)

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Arabic)

Currently translated at 90.3% (338 of 374 strings)

Translated using Weblate (Japanese)

Currently translated at 99.2% (130 of 131 strings)

Translated using Weblate (Japanese)

Currently translated at 99.5% (233 of 234 strings)

Translated using Weblate (Japanese)

Currently translated at 96.5% (389 of 403 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (208 of 208 strings)

Translated using Weblate (French)

Currently translated at 100.0% (208 of 208 strings)

Translated using Weblate (German)

Currently translated at 100.0% (208 of 208 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.2% (130 of 131 strings)

Translated using Weblate (French)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (German)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (French)

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Polish)

Currently translated at 83.5% (606 of 725 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (216 of 216 strings)

Translated using Weblate (French)

Currently translated at 100.0% (216 of 216 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (403 of 403 strings)

Translated using Weblate (Russian)

Currently translated at 99.5% (215 of 216 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 92.3% (192 of 208 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 96.9% (127 of 131 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 89.7% (210 of 234 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 94.0% (379 of 403 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 95.0% (172 of 181 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2695 of 2695 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (208 of 208 strings)

Translated using Weblate (Japanese)

Currently translated at 99.1% (232 of 234 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Polish)

Currently translated at 80.4% (583 of 725 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (216 of 216 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (208 of 208 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (208 of 208 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Japanese)

Currently translated at 99.1% (232 of 234 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (403 of 403 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (403 of 403 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (403 of 403 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2695 of 2695 strings)

Translated using Weblate (Russian)

Currently translated at 99.8% (2692 of 2695 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2695 of 2695 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (2695 of 2695 strings)

Translated using Weblate (German)

Currently translated at 100.0% (2695 of 2695 strings)

Translated using Weblate (German)

Currently translated at 100.0% (2695 of 2695 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Polish)

Currently translated at 78.8% (572 of 725 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (216 of 216 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (216 of 216 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (German)

Currently translated at 99.9% (2694 of 2695 strings)

Translated using Weblate (German)

Currently translated at 99.9% (2694 of 2695 strings)

Translated using Weblate (German)

Currently translated at 99.8% (2692 of 2695 strings)

Translated using Weblate (German)

Currently translated at 99.8% (2692 of 2695 strings)

Translated using Weblate (German)

Currently translated at 99.8% (2691 of 2695 strings)

Translated using Weblate (Arabic)

Currently translated at 89.8% (336 of 374 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (208 of 208 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2695 of 2695 strings)

Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Benoit Hetru <me+hbtc@gahanka.net>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Faris Allahham <farislahham@gmail.com>
Co-authored-by: Hexe des Windes (she/her) <krausanna1@gmail.com>
Co-authored-by: Ike Osenberg <ike.osenberg@gmail.com>
Co-authored-by: Lena Kubisa <lenorek.05.poczta@gmail.com>
Co-authored-by: LiziKnight <liziknight0316@outlook.com>
Co-authored-by: Mara S. (Dolichotis) <marascherzer@gmail.com>
Co-authored-by: Nazar Paruna <nazarparuna@gmail.com>
Co-authored-by: RedBug312 <redbug312@gmail.com>
Co-authored-by: Sandra Marcial <sandramarcial80@gmail.com>
Co-authored-by: Sergey Shevelev <vlkgamer45@gmail.com>
Co-authored-by: UNI <nibi727171@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: mattya 226 <worldworld1114@gmail.com>
Co-authored-by: そら <comi4work@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pl/
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/character/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/de/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ar/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/de/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ja/
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/ar/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hant/
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/ja/
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/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hant/
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/groups/it/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/it/
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/limited/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/zh_Hant/
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/npc/it/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
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/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/de/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/fr/
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/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/ja/
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/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hant/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Character
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/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Spells
Translation: Habitica/Subscriber
2022-10-11 18:57:30 +02:00
Natalie L
4d1bbdd8d0 chore(content): add October Backgrounds and Enchanted Armoire Items (#14266)
* chore(content): add October Backgrounds and Enchanted Armoire Items

* fix(typo): fix a couple of descriptions

* chore(subproject): update images

* fix(typo): keepin our caps consistent

* fix(typo): whitespace, caps

Co-authored-by: SabreCat <sabe@habitica.com>
Co-authored-by: Sabe Jones <sabrecat@gmail.com>
2022-10-11 11:54:24 -05:00
Natalie L
d1928c9181 chore(content): add October Pet Quest Bundle (#14268)
* chore(content): add October Pet Quest Bundle

* update: change event start date to the correct one
2022-10-11 11:53:46 -05:00
Adam Tommasi
137f7d53dc add qty param for gem/gem-purchasable item apidoc (#14219)
* add qty param for gem/gem-purchasable item

* fix(lint): remove extra whitespace

Co-authored-by: SabreCat <sabe@habitica.com>
2022-10-07 16:33:38 -05:00
Natalie L
3f9d55254e fix(style): update to scoped colors & set footer background image to pointer-events:none (#14265)
* first pass

* second pass

* third pass

* fourth pass, not a lot of progress

* fifth pass, slowly gettin there

* breakpoint nonsense

* flexbox and breakpoint nonsense

* svg updates, css updates

* whitespace

* chore: merge upstream/develop

* style(breakpoints): added responsive breakpoints, social column alignment tweaks

* style(breakpoints): add xs-specific selectors and classes

* style: mobile footer

* style: mobile footer

* style: static landing page footer

* style: small tweaks to social layout

* fix(translations): update website/common/locales/de/gear.json to develop

* update(style): small updates

* update(style): cleaning up code

* update(style): cleaning up code

* update(style): mobile footer updates

* update(style): complete mobile footer & clean up code

* update(style): add tablet breakpoint and remove rogue terms link

* fix(style): update to scoped colors & set footer background image to pointer-events:none

* fix(style): removed redundant logo class definition
2022-10-07 16:10:29 -05:00
SabreCat
b60a76d7dd chore(subproject): update images 2022-10-07 15:48:38 -05:00
SabreCat
a5575b3593 4.245.2 2022-10-06 15:53:25 -05:00
SabreCat
ace964f2b3 fix(subs): escape group demographics without group 2022-10-06 15:53:19 -05:00
SabreCat
12b045093a 4.245.1 2022-09-30 15:25:31 -05:00
SabreCat
8cd9536bdc Merge branch 'develop' into release 2022-09-30 15:25:27 -05:00
Weblate
08f0374b46 Translated using Weblate (Arabic)
Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Arabic)

Currently translated at 89.3% (334 of 374 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (208 of 208 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (208 of 208 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2695 of 2695 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (2695 of 2695 strings)

Translated using Weblate (Spanish)

Currently translated at 95.0% (2562 of 2695 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (389 of 389 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Spanish)

Currently translated at 99.0% (205 of 207 strings)

Translated using Weblate (Spanish)

Currently translated at 96.8% (125 of 129 strings)

Translated using Weblate (Spanish)

Currently translated at 99.1% (230 of 232 strings)

Translated using Weblate (Spanish)

Currently translated at 95.1% (370 of 389 strings)

Translated using Weblate (German)

Currently translated at 93.5% (364 of 389 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Spanish)

Currently translated at 99.6% (752 of 755 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Spanish)

Currently translated at 99.0% (213 of 215 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Polish)

Currently translated at 71.3% (517 of 725 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Polish)

Currently translated at 70.4% (511 of 725 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Polish)

Currently translated at 70.3% (510 of 725 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Polish)

Currently translated at 69.7% (506 of 725 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Arabic)

Currently translated at 99.2% (139 of 140 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Arabic)

Currently translated at 31.4% (57 of 181 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Arabic)

Currently translated at 37.0% (20 of 54 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (389 of 389 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Spanish)

Currently translated at 95.2% (2562 of 2691 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (2691 of 2691 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2691 of 2691 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2691 of 2691 strings)

Translated using Weblate (French)

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (French)

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 92.8% (168 of 181 strings)

Translated using Weblate (French)

Currently translated at 100.0% (232 of 232 strings)

Translated using Weblate (French)

Currently translated at 99.1% (2669 of 2691 strings)

Translated using Weblate (Spanish)

Currently translated at 95.2% (2562 of 2691 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 94.4% (171 of 181 strings)

Translated using Weblate (Turkish)

Currently translated at 92.8% (168 of 181 strings)

Translated using Weblate (Swedish)

Currently translated at 91.7% (166 of 181 strings)

Translated using Weblate (Serbian)

Currently translated at 90.6% (164 of 181 strings)

Translated using Weblate (Slovak)

Currently translated at 88.9% (161 of 181 strings)

Translated using Weblate (Romanian)

Currently translated at 92.8% (168 of 181 strings)

Translated using Weblate (Portuguese)

Currently translated at 91.7% (166 of 181 strings)

Translated using Weblate (Polish)

Currently translated at 94.4% (171 of 181 strings)

Translated using Weblate (Dutch)

Currently translated at 97.2% (176 of 181 strings)

Translated using Weblate (Indonesian)

Currently translated at 88.9% (161 of 181 strings)

Translated using Weblate (Hungarian)

Currently translated at 89.5% (162 of 181 strings)

Translated using Weblate (Hebrew)

Currently translated at 73.4% (133 of 181 strings)

Translated using Weblate (French)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (French)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Spanish)

Currently translated at 97.2% (176 of 181 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 92.8% (168 of 181 strings)

Translated using Weblate (English (Pirate) (en@pirate))

Currently translated at 92.8% (168 of 181 strings)

Translated using Weblate (Danish)

Currently translated at 94.4% (171 of 181 strings)

Translated using Weblate (Czech)

Currently translated at 92.8% (168 of 181 strings)

Translated using Weblate (Bulgarian)

Currently translated at 91.7% (166 of 181 strings)

Translated using Weblate (French)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (French)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (German)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (215 of 215 strings)

Translated using Weblate (Spanish)

Currently translated at 95.2% (2562 of 2691 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 92.2% (2482 of 2691 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Ukrainian)

Currently translated at 22.7% (613 of 2691 strings)

Translated using Weblate (Spanish)

Currently translated at 95.2% (2562 of 2691 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (2691 of 2691 strings)

Translated using Weblate (Japanese)

Currently translated at 99.2% (2672 of 2691 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (German)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (German)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (German)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (German)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (232 of 232 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (German)

Currently translated at 93.3% (363 of 389 strings)

Translated using Weblate (Japanese)

Currently translated at 99.0% (2665 of 2691 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (German)

Currently translated at 100.0% (98 of 98 strings)

Translated using Weblate (German)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (German)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (German)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (German)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (German)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (German)

Currently translated at 93.3% (363 of 389 strings)

Translated using Weblate (German)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2691 of 2691 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (232 of 232 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.9% (2689 of 2691 strings)

Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Benoit Hetru <me+hbtc@gahanka.net>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Faris Allahham <farislahham@gmail.com>
Co-authored-by: Felix Wittwer <spam@felixwittwer.de>
Co-authored-by: Hexe des Windes (she/her) <krausanna1@gmail.com>
Co-authored-by: Ike Osenberg <ike.osenberg@gmail.com>
Co-authored-by: Juan Esteban Marín <juanmarin690@gmail.com>
Co-authored-by: Khsmty <me@taigasaito.org>
Co-authored-by: Lena Kubisa <lenorek.05.poczta@gmail.com>
Co-authored-by: Lio Zam <zerofux@web.de>
Co-authored-by: LiziKnight <liziknight0316@outlook.com>
Co-authored-by: Mara S. (Dolichotis) <marascherzer@gmail.com>
Co-authored-by: Nakonana <nanaki1989@web.de>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Nazar Paruna <nazarparuna@gmail.com>
Co-authored-by: Nina Łapaj <ninapaj@gmail.com>
Co-authored-by: Raithe <RaitheOfDureya@gmail.com>
Co-authored-by: Remigiusz Haziak <haziakremigiusz@gmail.com>
Co-authored-by: Salman Mujeeb <kingleopard22@gmail.com>
Co-authored-by: Sandra Marcial <sandramarcial80@gmail.com>
Co-authored-by: Sara López <sarayupy@gmail.com>
Co-authored-by: Sergey Shevelev <vlkgamer45@gmail.com>
Co-authored-by: Simon Fischer <simon.pascal.fischer@gmail.com>
Co-authored-by: UNI <nibi727171@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: mattya 226 <worldworld1114@gmail.com>
Co-authored-by: そら <comi4work@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ar/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/de/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hans/
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/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/de/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ar/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/ar/
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/front/ar/
Translate-URL: https://translate.habitica.com/projects/habitica/front/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/front/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/front/da/
Translate-URL: https://translate.habitica.com/projects/habitica/front/en@pirate/
Translate-URL: https://translate.habitica.com/projects/habitica/front/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/front/es/
Translate-URL: https://translate.habitica.com/projects/habitica/front/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/front/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/front/he/
Translate-URL: https://translate.habitica.com/projects/habitica/front/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/front/id/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/front/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/front/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/it/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/
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/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ar/
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/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/inventory/ar/
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/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/ar/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/ja/
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/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/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/rebirth/de/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/es/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/ja/
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/it/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/es/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/pl/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Defaulttasks
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Inventory
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Npc
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Settings
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2022-09-30 22:22:14 +02:00
SabreCat
3582e233be fix(strings): remove duplicate "next" key 2022-09-30 15:09:53 -05:00
SabreCat
3974adcb65 chore(event): set Gem sale dates 2022-09-30 14:57:15 -05:00
SabreCat
57d3fea523 Merge branch 'group-tracking-modal' into release 2022-09-30 14:47:08 -05:00
SabreCat
173d7a178c Merge branch 'sabrecat/next-hourglass' into release 2022-09-30 14:46:27 -05:00
SabreCat
7db093d2bb Merge branch 'sabrecat/panel-subscription' into release 2022-09-30 14:46:10 -05:00
SabreCat
06d2ffb37d Merge branch 'sabrecat/gems-multi-event' into release 2022-09-30 14:45:36 -05:00
SabreCat
40997854dd fix(test): restore event list function 2022-09-30 14:32:21 -05:00
SabreCat
4c4d0be31f fix(analytics): include client tracking parameter 2022-09-29 15:07:21 -05:00
SabreCat
b8cf1b895f 4.245.0 2022-09-28 15:34:17 -05:00
Natalie L
a08ecbe044 chore(content): add October 2022 Mystery Items (#14254) 2022-09-28 15:04:47 -05:00
Natalie L
f3771f4869 chore(content): add October 2022 Mystery Items (#14254) 2022-09-28 15:03:56 -05:00
SabreCat
e0eed8238e fix(test): stub full event list 2022-09-23 16:42:15 -05:00
SabreCat
6baf08d461 fix(test): update expectations for new logic 2022-09-23 16:10:11 -05:00
SabreCat
535fddf92d feat(sale): add fine print 2022-09-21 16:28:15 -05:00
SabreCat
ef97f301d9 Merge branch 'release' into sabrecat/gems-multi-event 2022-09-21 16:04:10 -05:00
SabreCat
eea79ce1b6 Merge branch 'develop' into sabrecat/gems-multi-event 2022-09-21 16:03:57 -05:00
Weblate
191fee524c Merge branch 'origin/develop' into Weblate. 2022-09-21 22:23:36 +02:00
SabreCat
098f53bfa9 4.244.1 2022-09-21 15:02:39 -05:00
negue
e9ee2d3fdd Transaction username mongodb mutation (#14231)
* migrate newest usernames in transactions

* fix lint

* change the parameters
2022-09-21 14:42:39 -05:00
SabreCat
4c988691cf fix(groups): show group task summary modal for site admins
also change "Watcher" to "Peeker" in fall healer gear
2022-09-21 14:39:46 -05:00
Weblate
3c8be16135 Translated using Weblate (Russian)
Currently translated at 99.8% (2686 of 2691 strings)

Translated using Weblate (Spanish)

Currently translated at 95.2% (2562 of 2691 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 96.6% (175 of 181 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (232 of 232 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.5% (2680 of 2691 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (232 of 232 strings)

Translated using Weblate (Russian)

Currently translated at 99.7% (2685 of 2691 strings)

Translated using Weblate (Spanish)

Currently translated at 95.2% (2562 of 2691 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (232 of 232 strings)

Translated using Weblate (Russian)

Currently translated at 99.1% (2667 of 2691 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (2691 of 2691 strings)

Translated using Weblate (Filipino)

Currently translated at 92.2% (167 of 181 strings)

Translated using Weblate (Filipino)

Currently translated at 94.5% (122 of 129 strings)

Translated using Weblate (Filipino)

Currently translated at 97.9% (96 of 98 strings)

Translated using Weblate (Filipino)

Currently translated at 40.9% (9 of 22 strings)

Translated using Weblate (Italian)

Currently translated at 98.9% (2664 of 2691 strings)

Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Ike Osenberg <ike.osenberg@gmail.com>
Co-authored-by: Nazar Paruna <nazarparuna@gmail.com>
Co-authored-by: Sandra Marcial <sandramarcial80@gmail.com>
Co-authored-by: Sergey Shevelev <vlkgamer45@gmail.com>
Co-authored-by: Vince <vincemorel.vilan.889@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: weizhen lv <lvwzhen@gmail.com>
Co-authored-by: そら <comi4work@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/character/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/front/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/front/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/it/
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/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/it/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/uk/
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Npc
Translation: Habitica/Subscriber
2022-09-21 19:12:26 +02:00
CuriousMagpie
9890e0079a fix: correct groupPlan.vue file 2022-09-21 12:02:35 -04:00
SabreCat
1530ab44e9 fix(gala): hide avatar customizations before start date 2022-09-20 16:57:39 -05:00
CuriousMagpie
586897fbfc Revert "update: remove another duplicate key from groups.json"
This reverts commit 014a4b653a.
2022-09-20 17:11:09 -04:00
SabreCat
f75a6eb11d fix(hourglass): handle missing planId 2022-09-20 15:25:37 -05:00
SabreCat
155d6d5af6 Merge branch 'develop' into sabrecat/next-hourglass 2022-09-20 15:18:44 -05:00
SabreCat
004f1ee2dc Merge branch 'develop' into sabrecat/panel-subscription 2022-09-20 15:11:29 -05:00
CuriousMagpie
c61bdaf563 Merge remote-tracking branch 'origin/group-tracking-modal' into group-tracking-modal 2022-09-20 15:36:58 -04:00
SabreCat
85e14bb100 Merge branch 'develop' into group-tracking-modal 2022-09-20 14:13:59 -05:00
CuriousMagpie
8e9b469d8d chore: merge develop 2022-09-20 13:11:43 -04:00
Weblate
6e5cac88fc Merge branch 'origin/develop' into Weblate. 2022-09-19 23:58:58 +02:00
Weblate
80acb70718 Translated using Weblate (German)
Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (German)

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (German)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Belarusian)

Currently translated at 81.4% (615 of 755 strings)

Translated using Weblate (Japanese)

Currently translated at 97.2% (176 of 181 strings)

Translated using Weblate (Spanish)

Currently translated at 96.2% (2562 of 2663 strings)

Translated using Weblate (Russian)

Currently translated at 99.2% (128 of 129 strings)

Translated using Weblate (Russian)

Currently translated at 99.9% (2662 of 2663 strings)

Translated using Weblate (Russian)

Currently translated at 99.4% (180 of 181 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2663 of 2663 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Czech)

Currently translated at 74.3% (1981 of 2663 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2663 of 2663 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (181 of 181 strings)

Translated using Weblate (Russian)

Currently translated at 99.8% (754 of 755 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (228 of 228 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2663 of 2663 strings)

Translated using Weblate (German)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (German)

Currently translated at 93.3% (363 of 389 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2663 of 2663 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (389 of 389 strings)

Translated using Weblate (Ukrainian)

Currently translated at 22.9% (612 of 2663 strings)

Translated using Weblate (Ukrainian)

Currently translated at 46.6% (352 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (2663 of 2663 strings)

Translated using Weblate (Spanish)

Currently translated at 96.2% (2562 of 2663 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 45.8% (346 of 755 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (German)

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Russian)

Currently translated at 99.7% (388 of 389 strings)

Translated using Weblate (Russian)

Currently translated at 99.8% (2660 of 2663 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (Russian)

Currently translated at 99.7% (388 of 389 strings)

Translated using Weblate (Russian)

Currently translated at 99.8% (2660 of 2663 strings)

Translated using Weblate (Russian)

Currently translated at 98.2% (57 of 58 strings)

Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: David Kővári <davson.kovari@gmail.com>
Co-authored-by: Ike Osenberg <ike.osenberg@gmail.com>
Co-authored-by: Mara S. (Dolichotis) <marascherzer@gmail.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Nazar Paruna <nazarparuna@gmail.com>
Co-authored-by: Sandra Marcial <sandramarcial80@gmail.com>
Co-authored-by: Sergey Shevelev <vlkgamer45@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: mattya 226 <worldworld1114@gmail.com>
Co-authored-by: そら <comi4work@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/it/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/uk/
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/it/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/
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/gear/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/de/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/it/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/be/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
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/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/it/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ru/
Translation: Habitica/Backgrounds
Translation: Habitica/Communityguidelines
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Npc
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Subscriber
2022-09-19 23:58:41 +02:00
SabreCat
24430861ce 4.244.0 2022-09-19 16:56:39 -05:00
SabreCat
e60285e7d9 Merge branch 'develop' into release 2022-09-19 16:56:35 -05:00
Natalie L
f030135c82 chore(content): add 2022 Fall Festival (#14244)
* chore(submodule): add August 2022 Mystery Items

* update(content): add 2022 Fall Festival content

* update(content): add 2022 Fall Festival content

* update(content): update event dates for release

* fix(lint): remove extra spaces
also correct typo in gear string keys

* feat(content): remaining descriptions

* fix(event): correct spell availability

* fix(test): work around first-match logic

Co-authored-by: Sabe Jones <sabrecat@gmail.com>
Co-authored-by: SabreCat <sabe@habitica.com>
2022-09-19 16:55:53 -05:00
SabreCat
2c29310466 chore(privacy): official update note 2022-09-19 16:19:20 -05:00
CuriousMagpie
13eef6e4cf chore: merge develop 2022-09-19 15:10:11 -04:00
dependabot[bot]
13c0d12045 build(deps): bump rate-limiter-flexible from 2.3.7 to 2.3.10 (#14236)
Bumps [rate-limiter-flexible](https://github.com/animir/node-rate-limiter-flexible) from 2.3.7 to 2.3.10.
- [Release notes](https://github.com/animir/node-rate-limiter-flexible/releases)
- [Commits](https://github.com/animir/node-rate-limiter-flexible/commits)

---
updated-dependencies:
- dependency-name: rate-limiter-flexible
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-19 14:33:30 -04:00
dependabot[bot]
6456984f57 build(deps): bump @babel/preset-env from 7.18.10 to 7.19.1 (#14232)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.18.10 to 7.19.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.19.1/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-19 14:32:25 -04:00
dependabot[bot]
cfc1a12930 build(deps): bump jose from 2.0.5 to 2.0.6 (#14230)
Bumps [jose](https://github.com/panva/jose) from 2.0.5 to 2.0.6.
- [Release notes](https://github.com/panva/jose/releases)
- [Changelog](https://github.com/panva/jose/blob/v2.0.6/CHANGELOG.md)
- [Commits](https://github.com/panva/jose/compare/v2.0.5...v2.0.6)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-19 14:31:53 -04:00
dependabot[bot]
9ba4687478 build(deps): bump amplitude-js from 8.18.5 to 8.21.0 in /website/client (#14223)
Bumps [amplitude-js](https://github.com/amplitude/amplitude-javascript) from 8.18.5 to 8.21.0.
- [Release notes](https://github.com/amplitude/amplitude-javascript/releases)
- [Changelog](https://github.com/amplitude/Amplitude-JavaScript/blob/main/CHANGELOG.md)
- [Commits](https://github.com/amplitude/amplitude-javascript/compare/v8.18.5...v8.21.0)

---
updated-dependencies:
- dependency-name: amplitude-js
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-19 14:30:19 -04:00
dependabot[bot]
6d987a9579 build(deps): bump apidoc from 0.52.0 to 0.53.0 (#14214)
Bumps [apidoc](https://github.com/apidoc/apidoc) from 0.52.0 to 0.53.0.
- [Release notes](https://github.com/apidoc/apidoc/releases)
- [Changelog](https://github.com/apidoc/apidoc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/apidoc/apidoc/compare/0.52.0...0.53.0)

---
updated-dependencies:
- dependency-name: apidoc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-19 14:27:35 -04:00
dependabot[bot]
4702479156 build(deps): bump vue and vue-template-compiler in /website/client (#14196)
Bumps [vue](https://github.com/vuejs/core) and [vue-template-compiler](https://github.com/vuejs/vue). These dependencies needed to be updated together.

Updates `vue` from 2.7.8 to 2.7.10
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/commits)

Updates `vue-template-compiler` from 2.7.8 to 2.7.10
- [Release notes](https://github.com/vuejs/vue/releases)
- [Changelog](https://github.com/vuejs/vue/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vue/compare/v2.7.8...v2.7.10)

---
updated-dependencies:
- dependency-name: vue
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: vue-template-compiler
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-19 14:26:17 -04:00
dependabot[bot]
b384cd4eb8 build(deps): bump dompurify from 2.3.10 to 2.4.0 in /website/client (#14191)
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 2.3.10 to 2.4.0.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/2.3.10...2.4.0)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-19 14:25:24 -04:00
dependabot[bot]
d2bd7dc325 build(deps): bump jquery from 3.6.0 to 3.6.1 in /website/client (#14190)
Bumps [jquery](https://github.com/jquery/jquery) from 3.6.0 to 3.6.1.
- [Release notes](https://github.com/jquery/jquery/releases)
- [Commits](https://github.com/jquery/jquery/compare/3.6.0...3.6.1)

---
updated-dependencies:
- dependency-name: jquery
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-19 14:24:01 -04:00
CuriousMagpie
719fab8d4b chore: merge develop in 2022-09-16 16:12:34 -04:00
Phillip Thelen
24841346dc Purge Facebook (#13696)
* Don't sign in user when trying to connect a social account that was already created

* Log social users into matching local auth accounts

If the social account has an email that already exists as a local user, instead of creating a new account log them into their account and add the social auth to the account

* If possible set local authentication email for social users

* Allow password reset emails to be sent to social login users

* lint fixes

* Fix issues and tests

* fix tests

* Fix lint error.

* purge Facebook.

Only keep it in some select places to allow for some compatablilty.

* Fix error

* fix error

* Let settings handle it when you don't have a password set but an email

* fix error

* Fix boolean logic

* fix json conversion

* .

* fix password reset for old social accounts

* Don't sign in user when trying to connect a social account that was already created

* Log social users into matching local auth accounts

If the social account has an email that already exists as a local user, instead of creating a new account log them into their account and add the social auth to the account

* If possible set local authentication email for social users

* Allow password reset emails to be sent to social login users

* lint fixes

* Fix issues and tests

* fix tests

* Fix lint error.

* purge Facebook.

Only keep it in some select places to allow for some compatablilty.

* Fix error

* fix error

* Let settings handle it when you don't have a password set but an email

* fix error

* Fix boolean logic

* fix json conversion

* fix password reset for old social accounts

* Revert "lint fixes"

This reverts commit c244b1651c.

# Conflicts:
#	website/client/src/components/auth/registerLoginReset.vue
#	website/client/src/components/static/contact.vue

* Revert "fix password reset for old social accounts"

This reverts commit 7e0069a80f.

* fix duplicate code

* chore(misc): remove irrelevant changes

* chore(privacy): update policy page with note about FB

Co-authored-by: SabreCat <sabe@habitica.com>
2022-09-15 18:22:52 -05:00
SabreCat
4c34c68d78 fix(test): stub newly relevant function 2022-09-15 12:20:11 -05:00
SabreCat
9a8d1854b9 fix(promo): handle Gems sale defined outside of single top event 2022-09-15 11:40:48 -05:00
SabreCat
10f5011781 chore(subproject): update habitica-images SHA 2022-09-15 11:35:27 -05:00
Natalie L
9a896470d5 Site Footer Update (#14134)
* first pass

* second pass

* third pass

* fourth pass, not a lot of progress

* fifth pass, slowly gettin there

* breakpoint nonsense

* flexbox and breakpoint nonsense

* svg updates, css updates

* whitespace

* chore: merge upstream/develop

* style(breakpoints): added responsive breakpoints, social column alignment tweaks

* style(breakpoints): add xs-specific selectors and classes

* style: mobile footer

* style: mobile footer

* style: static landing page footer

* style: small tweaks to social layout

* fix(translations): update website/common/locales/de/gear.json to develop

* update(style): small updates

* update(style): cleaning up code

* update(style): cleaning up code

* update(style): mobile footer updates

* update(style): complete mobile footer & clean up code

* update(style): add tablet breakpoint and remove rogue terms link
2022-09-14 16:35:24 -05:00
Natalie L
6b0b393e32 fix: Group Plans string duplication & style adjustment (#14148)
* fix(string): questVice1Notes html changed to a mobile-device friendly format

* fix(strings): updated limited.json with "dateEnd" & "monthYYYY" months & put in chronological order

* fix(string): remove extra word from headSpecialSummer2022WarriorNotes

* fix(string): corrected armorSpecialSummer2022MageNotes

* fix: remove duplicated string and adjust upgrade button style

* fix(style): set border radii to 8px on upgrading-group id

* fix(payments): remove duplicate entry from another modal

Co-authored-by: SabreCat <sabe@habitica.com>
2022-09-14 16:33:31 -05:00
CuriousMagpie
014a4b653a update: remove another duplicate key from groups.json 2022-09-14 17:24:29 -04:00
CuriousMagpie
37e5d6b40a update: remove duplicate key from groups.json 2022-09-14 17:06:33 -04:00
SabreCat
fb780c9a2d fix(admin): reactivity with subscriptions 2022-09-13 15:52:03 -05:00
Weblate
5f440f1bfa Merge branch 'origin/develop' into Weblate. 2022-09-13 17:11:21 +02:00
SabreCat
d4f9555f11 4.243.1 2022-09-13 10:11:17 -05:00
Weblate
203d97423a Translated using Weblate (Russian)
Currently translated at 97.9% (381 of 389 strings)

Translated using Weblate (Spanish)

Currently translated at 96.2% (2562 of 2663 strings)

Translated using Weblate (Danish)

Currently translated at 94.0% (127 of 135 strings)

Translated using Weblate (Danish)

Currently translated at 89.3% (84 of 94 strings)

Translated using Weblate (Danish)

Currently translated at 97.2% (175 of 180 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Danish)

Currently translated at 86.0% (185 of 215 strings)

Translated using Weblate (Danish)

Currently translated at 93.3% (126 of 135 strings)

Translated using Weblate (Danish)

Currently translated at 71.0% (147 of 207 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Danish)

Currently translated at 88.2% (83 of 94 strings)

Translated using Weblate (Danish)

Currently translated at 99.0% (110 of 111 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Danish)

Currently translated at 99.1% (226 of 228 strings)

Translated using Weblate (Danish)

Currently translated at 78.9% (307 of 389 strings)

Translated using Weblate (Danish)

Currently translated at 68.6% (1829 of 2663 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Danish)

Currently translated at 96.6% (174 of 180 strings)

Translated using Weblate (Danish)

Currently translated at 98.2% (57 of 58 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Danish)

Currently translated at 81.1% (613 of 755 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (98 of 98 strings)

Translated using Weblate (Danish)

Currently translated at 67.8% (492 of 725 strings)

Translated using Weblate (Danish)

Currently translated at 85.5% (184 of 215 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Ukrainian)

Currently translated at 22.9% (611 of 2663 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (Russian)

Currently translated at 97.4% (379 of 389 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Ukrainian)

Currently translated at 43.7% (330 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 42.7% (323 of 755 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2663 of 2663 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.2% (57 of 58 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Ukrainian)

Currently translated at 41.8% (316 of 755 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Ukrainian)

Currently translated at 41.7% (315 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Russian)

Currently translated at 99.5% (206 of 207 strings)

Translated using Weblate (Russian)

Currently translated at 95.6% (372 of 389 strings)

Translated using Weblate (Russian)

Currently translated at 99.0% (213 of 215 strings)

Translated using Weblate (Danish)

Currently translated at 98.6% (225 of 228 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (2663 of 2663 strings)

Translated using Weblate (German)

Currently translated at 92.5% (360 of 389 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2663 of 2663 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (2663 of 2663 strings)

Translated using Weblate (Spanish)

Currently translated at 96.2% (2562 of 2663 strings)

Translated using Weblate (Spanish)

Currently translated at 96.3% (2562 of 2659 strings)

Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Asta Jensen <asta.raae@live.dk>
Co-authored-by: Ike Osenberg <ike.osenberg@gmail.com>
Co-authored-by: KanI <twinklingnerd@gmail.com>
Co-authored-by: Mara S. (Dolichotis) <marascherzer@gmail.com>
Co-authored-by: Nazar Paruna <nazarparuna@gmail.com>
Co-authored-by: Sandra Marcial <sandramarcial80@gmail.com>
Co-authored-by: Sergey Shevelev <vlkgamer45@gmail.com>
Co-authored-by: UNI <nibi727171@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: そら <comi4work@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/da/
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/ru/
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/da/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/da/
Translate-URL: https://translate.habitica.com/projects/habitica/content/da/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/da/
Translate-URL: https://translate.habitica.com/projects/habitica/death/da/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/da/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/da/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hans/
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/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/it/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/
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/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/da/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/da/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/da/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/zh_Hans/
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/quests/da/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/da/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/uk/
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/settings/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/da/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/da/
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Death
Translation: Habitica/Defaulttasks
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
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
2022-09-13 17:11:08 +02:00
Natalie L
0f4711c358 chore(content): add 2022-09 Pet Quest bundle (#14218)
* chore(content): farmFriends quest bundle

* chore(content): 2022-09 Pet Quest Bundle

* fix(date): fix start date
2022-09-13 09:12:24 -05:00
SabreCat
1a86943711 fix(subscriptions): better next-hourglass logic 2022-09-12 15:17:39 -05:00
CuriousMagpie
21185b689c update: add 'type' to amazonModal.vue, removed extraneous analytics.js code 2022-09-12 11:42:39 -04:00
CuriousMagpie
5a85e0730c update: analytics debugging 2022-09-09 16:56:19 -04:00
CuriousMagpie
771558e1fd update: analytics debugging 2022-09-09 16:27:25 -04:00
CuriousMagpie
e6f903fd2e update: more analytics work 2022-09-09 15:37:08 -04:00
CuriousMagpie
5c13bf1980 update: add analytics to create and success modals 2022-09-08 17:34:36 -04:00
CuriousMagpie
36a4ec69d5 update: success modal (testing something) 2022-09-08 15:53:26 -04:00
CuriousMagpie
c6ba1d8402 update: success modal 2022-09-08 12:55:53 -04:00
SabreCat
0081bad831 feat(admin): track hourglasses as transactions 2022-09-06 16:15:04 -05:00
CuriousMagpie
abdb6244d3 updates: add comments to success modal to flag issues 2022-09-06 17:06:19 -04:00
SabreCat
90f1977a49 fix(admin): sometimes 0 is correct 2022-09-06 15:09:40 -05:00
SabreCat
bc33e4349d Merge branch 'develop' into sabrecat/panel-subscription 2022-09-06 14:19:46 -05:00
CuriousMagpie
61f3d8d61c Merge branch 'develop' into group-tracking-modal 2022-09-06 15:11:03 -04:00
SabreCat
38bf0b3721 4.243.0 2022-09-06 12:35:18 -05:00
SabreCat
88c8b545f4 Merge branch 'develop' into release 2022-09-06 12:35:14 -05:00
Weblate
184ee7262e Translated using Weblate (Japanese)
Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (111 of 111 strings)

Translated using Weblate (Ukrainian)

Currently translated at 41.3% (312 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 76.3% (158 of 207 strings)

Translated using Weblate (Spanish)

Currently translated at 96.3% (2562 of 2659 strings)

Translated using Weblate (Ukrainian)

Currently translated at 75.8% (157 of 207 strings)

Translated using Weblate (German)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (German)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (French)

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.6% (125 of 128 strings)

Translated using Weblate (French)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Russian)

Currently translated at 99.7% (2653 of 2659 strings)

Translated using Weblate (French)

Currently translated at 100.0% (2659 of 2659 strings)

Translated using Weblate (Spanish)

Currently translated at 96.3% (2562 of 2659 strings)

Translated using Weblate (Ukrainian)

Currently translated at 41.1% (311 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (German)

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (German)

Currently translated at 100.0% (2659 of 2659 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (2659 of 2659 strings)

Translated using Weblate (Spanish)

Currently translated at 96.3% (2562 of 2659 strings)

Translated using Weblate (Danish)

Currently translated at 80.5% (608 of 755 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Danish)

Currently translated at 99.2% (139 of 140 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Chinese (Hong Kong))

Currently translated at 100.0% (98 of 98 strings)

Translated using Weblate (Ukrainian)

Currently translated at 64.2% (133 of 207 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2659 of 2659 strings)

Translated using Weblate (Spanish)

Currently translated at 96.3% (2562 of 2659 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (718 of 718 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 93.5% (131 of 140 strings)

Translated using Weblate (Korean)

Currently translated at 79.3% (570 of 718 strings)

Translated using Weblate (German)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Russian)

Currently translated at 99.7% (2652 of 2659 strings)

Translated using Weblate (German)

Currently translated at 98.2% (57 of 58 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 92.2% (359 of 389 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (389 of 389 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (2659 of 2659 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (180 of 180 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (718 of 718 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.8% (2655 of 2659 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.5% (206 of 207 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (Japanese)

Currently translated at 99.2% (127 of 128 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Japanese)

Currently translated at 96.6% (376 of 389 strings)

Translated using Weblate (Japanese)

Currently translated at 96.6% (376 of 389 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.8% (2655 of 2659 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (2659 of 2659 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.8% (2655 of 2659 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (207 of 207 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2659 of 2659 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (128 of 128 strings)

Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Asta Jensen <asta.raae@live.dk>
Co-authored-by: Benoit Hetru <me+hbtc@gahanka.net>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Ike Osenberg <ike.osenberg@gmail.com>
Co-authored-by: JoanZeppeli <x17501668978@163.com>
Co-authored-by: Khsmty <me@taigasaito.org>
Co-authored-by: Mara S. (Dolichotis) <marascherzer@gmail.com>
Co-authored-by: Nazar Paruna <nazarparuna@gmail.com>
Co-authored-by: Sandra Marcial <sandramarcial80@gmail.com>
Co-authored-by: Sara de Nicolas <saradenicolas12@gmail.com>
Co-authored-by: Sciuridae <sweetvshoney@163.com>
Co-authored-by: Sergey Shevelev <vlkgamer45@gmail.com>
Co-authored-by: Simon Fischer <simon.pascal.fischer@gmail.com>
Co-authored-by: Tiffany Tai <mrbroccoli128@gmail.com>
Co-authored-by: UNI <nibi727171@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Zero <leedambak@gmail.com>
Co-authored-by: neko kyuri <Nekorin0621@gmail.com>
Co-authored-by: weizhen lv <lvwzhen@gmail.com>
Co-authored-by: そら <comi4work@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/da/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/zh_Hant_HK/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/content/da/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/de/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hans/
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/it/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/
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/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/
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/npc/it/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/de/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/da/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/uk/
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/it/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/
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/subscriber/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/de/
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/Groups
Translation: Habitica/Npc
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2022-09-06 19:34:47 +02:00
Natalie L
6df4ce251c chore(content): add September 2022 Backgrounds and Enchanted Armoire Items (#14200)
* chore(submodule): add August 2022 Mystery Items

* chore(content): add September 2022 Backgrounds and Enchanted Armoire Items

* fix(typo): periods so people don't grumble

* fix(typo): consistent caps

Co-authored-by: Sabe Jones <sabrecat@gmail.com>
2022-09-06 12:31:55 -05:00
CuriousMagpie
796d752974 update: success modal 2022-09-02 16:55:28 -04:00
SabreCat
008314676d fix(admin): reactivity on next hourglass 2022-09-01 17:16:51 -05:00
SabreCat
e383614107 4.242.1 2022-09-01 14:47:20 -05:00
Natalie L
2b21410abd fix(quest shop): correct forest friends end date (#14202) 2022-09-01 14:45:49 -05:00
SabreCat
f364b3c06f feat(admin): consecutive months editable field and automatic calcs 2022-08-31 16:09:20 -05:00
SabreCat
ae23ac12ff feat(admin): interactive subscription section fields 2022-08-30 16:56:43 -05:00
CuriousMagpie
e2bb7fda60 update: success modal 2022-08-30 17:43:36 -04:00
SabreCat
09d6dae75c feat(admin): provide reset cron button 2022-08-30 15:35:50 -05:00
SabreCat
bc5813fd10 Merge branch 'develop' into sabrecat/panel-subscription 2022-08-30 14:54:40 -05:00
CuriousMagpie
4464464c51 Merge branch 'develop' into group-tracking-modal 2022-08-30 15:44:09 -04:00
Weblate
56f956be5a Merge branch 'origin/develop' into Weblate. 2022-08-30 21:25:56 +02:00
Weblate
2b44d32b1c Translated using Weblate (Ukrainian)
Currently translated at 39.3% (81 of 206 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Ukrainian)

Currently translated at 41.1% (311 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (228 of 228 strings)

Translated using Weblate (Spanish)

Currently translated at 98.2% (57 of 58 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (718 of 718 strings)

Translated using Weblate (Russian)

Currently translated at 94.8% (369 of 389 strings)

Translated using Weblate (French)

Currently translated at 100.0% (389 of 389 strings)

Translated using Weblate (Portuguese)

Currently translated at 95.7% (45 of 47 strings)

Translated using Weblate (Ukrainian)

Currently translated at 92.9% (118 of 127 strings)

Translated using Weblate (Korean)

Currently translated at 78.6% (565 of 718 strings)

Translated using Weblate (Vietnamese)

Currently translated at 84.4% (174 of 206 strings)

Translated using Weblate (Korean)

Currently translated at 64.0% (132 of 206 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (2655 of 2655 strings)

Translated using Weblate (English (Pirate) (en@pirate))

Currently translated at 88.5% (124 of 140 strings)

Translated using Weblate (Korean)

Currently translated at 43.8% (79 of 180 strings)

Translated using Weblate (Korean)

Currently translated at 41.1% (74 of 180 strings)

Translated using Weblate (Korean)

Currently translated at 51.7% (30 of 58 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Korean)

Currently translated at 82.1% (620 of 755 strings)

Translated using Weblate (Korean)

Currently translated at 78.1% (561 of 718 strings)

Translated using Weblate (Korean)

Currently translated at 78.1% (561 of 718 strings)

Translated using Weblate (Korean)

Currently translated at 63.5% (131 of 206 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Japanese)

Currently translated at 92.8% (361 of 389 strings)

Translated using Weblate (Czech)

Currently translated at 74.4% (1977 of 2655 strings)

Translated using Weblate (Ukrainian)

Currently translated at 78.7% (100 of 127 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (French)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 92.5% (360 of 389 strings)

Translated using Weblate (French)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Czech)

Currently translated at 74.2% (1972 of 2655 strings)

Translated using Weblate (Italian)

Currently translated at 98.2% (57 of 58 strings)

Translated using Weblate (Romanian)

Currently translated at 98.9% (93 of 94 strings)

Translated using Weblate (Czech)

Currently translated at 73.8% (1962 of 2655 strings)

Translated using Weblate (Romanian)

Currently translated at 91.4% (86 of 94 strings)

Translated using Weblate (Czech)

Currently translated at 72.8% (1934 of 2655 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.2% (57 of 58 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Romanian)

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 (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Russian)

Currently translated at 98.2% (57 of 58 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (228 of 228 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (389 of 389 strings)

Translated using Weblate (Russian)

Currently translated at 92.8% (361 of 389 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2655 of 2655 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (98 of 98 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (718 of 718 strings)

Translated using Weblate (Romanian)

Currently translated at 93.5% (131 of 140 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (215 of 215 strings)

Translated using Weblate (Romanian)

Currently translated at 90.0% (126 of 140 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (389 of 389 strings)

Translated using Weblate (Spanish)

Currently translated at 99.2% (139 of 140 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (389 of 389 strings)

Translated using Weblate (Italian)

Currently translated at 99.2% (386 of 389 strings)

Translated using Weblate (Russian)

Currently translated at 99.7% (2649 of 2655 strings)

Translated using Weblate (Filipino)

Currently translated at 93.5% (131 of 140 strings)

Translated using Weblate (Filipino)

Currently translated at 96.4% (354 of 367 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2655 of 2655 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Filipino)

Currently translated at 81.3% (109 of 134 strings)

Translated using Weblate (Filipino)

Currently translated at 95.0% (133 of 140 strings)

Translated using Weblate (Filipino)

Currently translated at 36.3% (8 of 22 strings)

Translated using Weblate (Ukrainian)

Currently translated at 93.8% (214 of 228 strings)

Translated using Weblate (Spanish)

Currently translated at 98.5% (138 of 140 strings)

Translated using Weblate (Ukrainian)

Currently translated at 89.4% (204 of 228 strings)

Translated using Weblate (Russian)

Currently translated at 99.5% (2642 of 2655 strings)

Translated using Weblate (Russian)

Currently translated at 99.4% (2640 of 2655 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Russian)

Currently translated at 99.3% (2639 of 2655 strings)

Translated using Weblate (Filipino)

Currently translated at 50.0% (11 of 22 strings)

Translated using Weblate (Filipino)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Ukrainian)

Currently translated at 22.6% (602 of 2655 strings)

Translated using Weblate (Russian)

Currently translated at 99.2% (2636 of 2655 strings)

Translated using Weblate (German)

Currently translated at 100.0% (2655 of 2655 strings)

Translated using Weblate (German)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Ukrainian)

Currently translated at 29.6% (786 of 2655 strings)

Translated using Weblate (Ukrainian)

Currently translated at 33.4% (889 of 2655 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (98 of 98 strings)

Translated using Weblate (Filipino)

Currently translated at 72.9% (551 of 755 strings)

Translated using Weblate (Filipino)

Currently translated at 96.4% (135 of 140 strings)

Translated using Weblate (Filipino)

Currently translated at 93.4% (200 of 214 strings)

Translated using Weblate (Ukrainian)

Currently translated at 43.8% (1163 of 2655 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2655 of 2655 strings)

Translated using Weblate (French)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Filipino)

Currently translated at 72.8% (550 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 86.8% (198 of 228 strings)

Translated using Weblate (Russian)

Currently translated at 98.9% (2628 of 2655 strings)

Translated using Weblate (Korean)

Currently translated at 85.0% (108 of 127 strings)

Translated using Weblate (Ukrainian)

Currently translated at 57.8% (1535 of 2655 strings)

Translated using Weblate (Russian)

Currently translated at 98.7% (2623 of 2655 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (2655 of 2655 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Ukrainian)

Currently translated at 86.4% (197 of 228 strings)

Translated using Weblate (Ukrainian)

Currently translated at 62.7% (1667 of 2655 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (755 of 755 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (718 of 718 strings)

Translated using Weblate (Russian)

Currently translated at 99.5% (205 of 206 strings)

Translated using Weblate (Russian)

Currently translated at 98.7% (2622 of 2655 strings)

Translated using Weblate (German)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (374 of 374 strings)

Translated using Weblate (Filipino)

Currently translated at 83.4% (172 of 206 strings)

Translated using Weblate (Filipino)

Currently translated at 72.8% (550 of 755 strings)

Translated using Weblate (Filipino)

Currently translated at 72.1% (101 of 140 strings)

Translated using Weblate (Filipino)

Currently translated at 92.2% (344 of 373 strings)

Translated using Weblate (Ukrainian)

Currently translated at 27.1% (56 of 206 strings)

Translated using Weblate (French)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Ukrainian)

Currently translated at 73.4% (1951 of 2655 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (180 of 180 strings)

Translated using Weblate (Ukrainian)

Currently translated at 40.9% (309 of 755 strings)

Translated using Weblate (French)

Currently translated at 100.0% (215 of 215 strings)

Translated using Weblate (Korean)

Currently translated at 92.3% (339 of 367 strings)

Translated using Weblate (Filipino)

Currently translated at 86.3% (620 of 718 strings)

Translated using Weblate (Filipino)

Currently translated at 74.7% (564 of 755 strings)

Translated using Weblate (Filipino)

Currently translated at 74.2% (104 of 140 strings)

Translated using Weblate (Ukrainian)

Currently translated at 46.6% (96 of 206 strings)

Translated using Weblate (Ukrainian)

Currently translated at 75.5% (2005 of 2655 strings)

Translated using Weblate (Russian)

Currently translated at 98.1% (2606 of 2655 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (111 of 111 strings)

Translated using Weblate (Filipino)

Currently translated at 75.3% (569 of 755 strings)

Translated using Weblate (Filipino)

Currently translated at 78.5% (110 of 140 strings)

Translated using Weblate (Filipino)

Currently translated at 33.4% (889 of 2655 strings)

Translated using Weblate (Russian)

Currently translated at 98.1% (2606 of 2655 strings)

Translated using Weblate (Dutch)

Currently translated at 99.0% (213 of 215 strings)

Translated using Weblate (Ukrainian)

Currently translated at 64.9% (490 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 73.3% (554 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 71.8% (148 of 206 strings)

Translated using Weblate (Ukrainian)

Currently translated at 78.9% (180 of 228 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (228 of 228 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (228 of 228 strings)

Translated using Weblate (Ukrainian)

Currently translated at 75.7% (2010 of 2655 strings)

Translated using Weblate (Russian)

Currently translated at 98.1% (2606 of 2655 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2655 of 2655 strings)

Translated using Weblate (Ukrainian)

Currently translated at 96.1% (173 of 180 strings)

Translated using Weblate (Ukrainian)

Currently translated at 78.5% (44 of 56 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Ukrainian)

Currently translated at 78.6% (594 of 755 strings)

Translated using Weblate (Ukrainian)

Currently translated at 59.0% (75 of 127 strings)

Translated using Weblate (Ukrainian)

Currently translated at 59.0% (75 of 127 strings)

Translated using Weblate (Korean)

Currently translated at 51.7% (29 of 56 strings)

Translated using Weblate (Korean)

Currently translated at 62.1% (128 of 206 strings)

Translated using Weblate (Korean)

Currently translated at 81.8% (618 of 755 strings)

Translated using Weblate (Korean)

Currently translated at 81.4% (114 of 140 strings)

Translated using Weblate (Korean)

Currently translated at 64.5% (1713 of 2655 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (180 of 180 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (180 of 180 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (180 of 180 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Galician)

Currently translated at 90.0% (162 of 180 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Galician)

Currently translated at 97.6% (209 of 214 strings)

Translated using Weblate (Russian)

Currently translated at 97.5% (2590 of 2655 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (228 of 228 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2655 of 2655 strings)

Translated using Weblate (Galician)

Currently translated at 97.6% (209 of 214 strings)

Translated using Weblate (Korean)

Currently translated at 98.3% (183 of 186 strings)

Translated using Weblate (Galician)

Currently translated at 96.2% (206 of 214 strings)

Translated using Weblate (Galician)

Currently translated at 92.9% (199 of 214 strings)

Translated using Weblate (Galician)

Currently translated at 83.7% (108 of 129 strings)

Translated using Weblate (Galician)

Currently translated at 94.6% (176 of 186 strings)

Translated using Weblate (Galician)

Currently translated at 58.3% (419 of 718 strings)

Translated using Weblate (Galician)

Currently translated at 88.0% (118 of 134 strings)

Translated using Weblate (Galician)

Currently translated at 59.2% (122 of 206 strings)

Translated using Weblate (Galician)

Currently translated at 82.7% (625 of 755 strings)

Translated using Weblate (Galician)

Currently translated at 65.9% (62 of 94 strings)

Translated using Weblate (Galician)

Currently translated at 54.8% (125 of 228 strings)

Translated using Weblate (Galician)

Currently translated at 86.1% (316 of 367 strings)

Translated using Weblate (Galician)

Currently translated at 92.9% (199 of 214 strings)

Translated using Weblate (Galician)

Currently translated at 67.1% (1783 of 2655 strings)

Translated using Weblate (Galician)

Currently translated at 72.2% (39 of 54 strings)

Translated using Weblate (Galician)

Currently translated at 91.4% (43 of 47 strings)

Translated using Weblate (Galician)

Currently translated at 92.2% (344 of 373 strings)

Translated using Weblate (Galician)

Currently translated at 71.6% (91 of 127 strings)

Translated using Weblate (Galician)

Currently translated at 78.6% (169 of 215 strings)

Translated using Weblate (Galician)

Currently translated at 92.8% (52 of 56 strings)

Translated using Weblate (Galician)

Currently translated at 82.9% (107 of 129 strings)

Translated using Weblate (Galician)

Currently translated at 94.6% (176 of 186 strings)

Translated using Weblate (Galician)

Currently translated at 58.3% (419 of 718 strings)

Translated using Weblate (Galician)

Currently translated at 88.8% (119 of 134 strings)

Translated using Weblate (Galician)

Currently translated at 59.2% (122 of 206 strings)

Translated using Weblate (Galician)

Currently translated at 65.9% (62 of 94 strings)

Translated using Weblate (Galician)

Currently translated at 50.0% (4 of 8 strings)

Translated using Weblate (Galician)

Currently translated at 12.1% (17 of 140 strings)

Translated using Weblate (Galician)

Currently translated at 83.6% (51 of 61 strings)

Translated using Weblate (Galician)

Currently translated at 77.2% (17 of 22 strings)

Translated using Weblate (Galician)

Currently translated at 54.8% (125 of 228 strings)

Translated using Weblate (Galician)

Currently translated at 62.5% (5 of 8 strings)

Translated using Weblate (Galician)

Currently translated at 85.8% (315 of 367 strings)

Translated using Weblate (Galician)

Currently translated at 92.9% (199 of 214 strings)

Translated using Weblate (Galician)

Currently translated at 67.1% (1782 of 2655 strings)

Translated using Weblate (Galician)

Currently translated at 68.5% (37 of 54 strings)

Translated using Weblate (Galician)

Currently translated at 82.9% (39 of 47 strings)

Translated using Weblate (Galician)

Currently translated at 70.8% (90 of 127 strings)

Translated using Weblate (Japanese)

Currently translated at 97.8% (137 of 140 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (215 of 215 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.5% (138 of 140 strings)

Translated using Weblate (Korean)

Currently translated at 98.3% (183 of 186 strings)

Translated using Weblate (Korean)

Currently translated at 94.7% (127 of 134 strings)

Translated using Weblate (Russian)

Currently translated at 99.5% (205 of 206 strings)

Translated using Weblate (Russian)

Currently translated at 97.5% (2590 of 2655 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Russian)

Currently translated at 97.5% (2589 of 2655 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (215 of 215 strings)

Translated using Weblate (Filipino)

Currently translated at 84.6% (94 of 111 strings)

Translated using Weblate (Filipino)

Currently translated at 79.2% (111 of 140 strings)

Translated using Weblate (Filipino)

Currently translated at 94.1% (351 of 373 strings)

Translated using Weblate (Filipino)

Currently translated at 86.4% (96 of 111 strings)

Translated using Weblate (Filipino)

Currently translated at 82.1% (115 of 140 strings)

Translated using Weblate (Filipino)

Currently translated at 82.8% (116 of 140 strings)

Translated using Weblate (Filipino)

Currently translated at 87.5% (7 of 8 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Filipino)

Currently translated at 83.4% (172 of 206 strings)

Translated using Weblate (Filipino)

Currently translated at 83.5% (117 of 140 strings)

Translated using Weblate (Filipino)

Currently translated at 83.9% (173 of 206 strings)

Translated using Weblate (Filipino)

Currently translated at 34.0% (903 of 2655 strings)

Translated using Weblate (Russian)

Currently translated at 97.4% (2588 of 2655 strings)

Translated using Weblate (French)

Currently translated at 100.0% (2655 of 2655 strings)

Translated using Weblate (French)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (French)

Currently translated at 100.0% (373 of 373 strings)

Translated using Weblate (Filipino)

Currently translated at 34.0% (904 of 2655 strings)

Translated using Weblate (Korean)

Currently translated at 62.1% (128 of 206 strings)

Translated using Weblate (Korean)

Currently translated at 59.2% (122 of 206 strings)

Translated using Weblate (Russian)

Currently translated at 97.4% (2587 of 2655 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (180 of 180 strings)

Translated using Weblate (Ukrainian)

Currently translated at 94.4% (120 of 127 strings)

Translated using Weblate (French)

Currently translated at 99.6% (2647 of 2655 strings)

Translated using Weblate (Ukrainian)

Currently translated at 88.9% (113 of 127 strings)

Translated using Weblate (Korean)

Currently translated at 79.5% (101 of 127 strings)

Translated using Weblate (Russian)

Currently translated at 97.4% (2586 of 2655 strings)

Translated using Weblate (Russian)

Currently translated at 97.2% (2583 of 2655 strings)

Translated using Weblate (Russian)

Currently translated at 97.2% (2581 of 2655 strings)

Translated using Weblate (Filipino)

Currently translated at 87.3% (180 of 206 strings)

Translated using Weblate (French)

Currently translated at 99.3% (2639 of 2655 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Russian)

Currently translated at 97.0% (2576 of 2655 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (180 of 180 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (228 of 228 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (718 of 718 strings)

Translated using Weblate (Filipino)

Currently translated at 86.8% (179 of 206 strings)

Translated using Weblate (Filipino)

Currently translated at 87.5% (7 of 8 strings)

Translated using Weblate (Korean)

Currently translated at 74.8% (95 of 127 strings)

Translated using Weblate (French)

Currently translated at 99.1% (2632 of 2655 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 97.7% (702 of 718 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (137 of 137 strings)

Translated using Weblate (Filipino)

Currently translated at 34.0% (904 of 2655 strings)

Translated using Weblate (German)

Currently translated at 100.0% (206 of 206 strings)

Translated using Weblate (French)

Currently translated at 98.7% (2622 of 2655 strings)

Translated using Weblate (German)

Currently translated at 100.0% (2655 of 2655 strings)

Translated using Weblate (German)

Currently translated at 100.0% (2655 of 2655 strings)

Translated using Weblate (Russian)

Currently translated at 96.7% (2568 of 2655 strings)

Translated using Weblate (French)

Currently translated at 100.0% (718 of 718 strings)

Translated using Weblate (German)

Currently translated at 99.8% (2652 of 2655 strings)

Translated using Weblate (German)

Currently translated at 100.0% (718 of 718 strings)

Translated using Weblate (German)

Currently translated at 99.8% (2652 of 2655 strings)

Translated using Weblate (French)

Currently translated at 100.0% (206 of 206 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Korean)

Currently translated at 82.4% (113 of 137 strings)

Translated using Weblate (Filipino)

Currently translated at 42.3% (1125 of 2655 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (137 of 137 strings)

Translated using Weblate (Korean)

Currently translated at 38.3% (69 of 180 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Korean)

Currently translated at 77.9% (560 of 718 strings)

Translated using Weblate (Korean)

Currently translated at 94.7% (127 of 134 strings)

Translated using Weblate (Korean)

Currently translated at 94.7% (127 of 134 strings)

Translated using Weblate (Korean)

Currently translated at 92.6% (340 of 367 strings)

Translated using Weblate (Korean)

Currently translated at 90.1% (193 of 214 strings)

Translated using Weblate (Korean)

Currently translated at 85.1% (46 of 54 strings)

Translated using Weblate (Korean)

Currently translated at 71.6% (91 of 127 strings)

Translated using Weblate (Russian)

Currently translated at 96.5% (2563 of 2655 strings)

Translated using Weblate (Japanese)

Currently translated at 99.5% (214 of 215 strings)

Translated using Weblate (Russian)

Currently translated at 96.3% (2559 of 2655 strings)

Translated using Weblate (Korean)

Currently translated at 85.1% (46 of 54 strings)

Translated using Weblate (Korean)

Currently translated at 85.1% (46 of 54 strings)

Translated using Weblate (Korean)

Currently translated at 78.1% (107 of 137 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (373 of 373 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Korean)

Currently translated at 64.5% (1713 of 2655 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (373 of 373 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (373 of 373 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (373 of 373 strings)

Translated using Weblate (Korean)

Currently translated at 89.9% (116 of 129 strings)

Translated using Weblate (Korean)

Currently translated at 51.7% (29 of 56 strings)

Translated using Weblate (Korean)

Currently translated at 81.8% (618 of 755 strings)

Translated using Weblate (Korean)

Currently translated at 76.6% (105 of 137 strings)

Translated using Weblate (Korean)

Currently translated at 92.6% (340 of 367 strings)

Translated using Weblate (Korean)

Currently translated at 92.6% (340 of 367 strings)

Translated using Weblate (Korean)

Currently translated at 64.4% (1711 of 2655 strings)

Translated using Weblate (Korean)

Currently translated at 83.3% (45 of 54 strings)

Translated using Weblate (Korean)

Currently translated at 77.9% (560 of 718 strings)

Translated using Weblate (Korean)

Currently translated at 81.4% (44 of 54 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (2655 of 2655 strings)

Translated using Weblate (Japanese)

Currently translated at 99.0% (213 of 215 strings)

Translated using Weblate (Galician)

Currently translated at 44.6% (25 of 56 strings)

Translated using Weblate (Galician)

Currently translated at 95.6% (178 of 186 strings)

Translated using Weblate (Galician)

Currently translated at 59.2% (122 of 206 strings)

Translated using Weblate (Galician)

Currently translated at 82.7% (625 of 755 strings)

Translated using Weblate (Galician)

Currently translated at 56.1% (128 of 228 strings)

Translated using Weblate (Galician)

Currently translated at 67.1% (1784 of 2655 strings)

Translated using Weblate (Galician)

Currently translated at 83.7% (93 of 111 strings)

Translated using Weblate (Galician)

Currently translated at 21.1% (38 of 180 strings)

Translated using Weblate (Galician)

Currently translated at 50.0% (28 of 56 strings)

Translated using Weblate (Galician)

Currently translated at 96.2% (179 of 186 strings)

Translated using Weblate (Galician)

Currently translated at 58.3% (419 of 718 strings)

Translated using Weblate (Galician)

Currently translated at 89.5% (120 of 134 strings)

Translated using Weblate (Galician)

Currently translated at 59.2% (122 of 206 strings)

Translated using Weblate (Galician)

Currently translated at 83.1% (628 of 755 strings)

Translated using Weblate (Galician)

Currently translated at 13.8% (19 of 137 strings)

Translated using Weblate (Galician)

Currently translated at 56.1% (128 of 228 strings)

Translated using Weblate (Galician)

Currently translated at 87.1% (320 of 367 strings)

Translated using Weblate (Galician)

Currently translated at 92.9% (199 of 214 strings)

Translated using Weblate (Galician)

Currently translated at 67.3% (1789 of 2655 strings)

Translated using Weblate (Galician)

Currently translated at 68.5% (37 of 54 strings)

Translated using Weblate (Galician)

Currently translated at 95.7% (45 of 47 strings)

Translated using Weblate (Galician)

Currently translated at 89.8% (335 of 373 strings)

Translated using Weblate (Galician)

Currently translated at 74.0% (94 of 127 strings)

Translated using Weblate (Galician)

Currently translated at 76.7% (165 of 215 strings)

Translated using Weblate (Galician)

Currently translated at 3.8% (7 of 180 strings)

Translated using Weblate (Galician)

Currently translated at 96.4% (54 of 56 strings)

Translated using Weblate (Galician)

Currently translated at 50.0% (28 of 56 strings)

Translated using Weblate (Galician)

Currently translated at 57.1% (410 of 718 strings)

Translated using Weblate (Galician)

Currently translated at 88.0% (118 of 134 strings)

Translated using Weblate (Galician)

Currently translated at 58.7% (121 of 206 strings)

Translated using Weblate (Galician)

Currently translated at 82.1% (620 of 755 strings)

Translated using Weblate (Galician)

Currently translated at 67.0% (63 of 94 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Galician)

Currently translated at 91.1% (195 of 214 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Galician)

Currently translated at 73.2% (93 of 127 strings)

Translated using Weblate (Galician)

Currently translated at 74.4% (160 of 215 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (98 of 98 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (373 of 373 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (718 of 718 strings)

Translated using Weblate (German)

Currently translated at 99.8% (2651 of 2655 strings)

Translated using Weblate (German)

Currently translated at 99.8% (2651 of 2655 strings)

Translated using Weblate (Russian)

Currently translated at 99.8% (754 of 755 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (206 of 206 strings)

Translated using Weblate (Korean)

Currently translated at 75.9% (104 of 137 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (206 of 206 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2655 of 2655 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (755 of 755 strings)

Co-authored-by: Adriana Alupei <a.ady96@yahoo.com>
Co-authored-by: Adrián Chaves Fernández <adrian@chaves.io>
Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Anton de Regt <antonderegt@pm.me>
Co-authored-by: Benoit Hetru <me+hbtc@gahanka.net>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: David Kővári <davson.kovari@gmail.com>
Co-authored-by: Felix Wittwer <spam@felixwittwer.de>
Co-authored-by: Forst Wolf <forestwolf@spam.care>
Co-authored-by: Forstwolf <forestwolf@spam.care>
Co-authored-by: Goggle K <afc731@gmail.com>
Co-authored-by: Hexe des Windes (she/her) <krausanna1@gmail.com>
Co-authored-by: Hyun Yeol Kim <hyunyeolkim@gmail.com>
Co-authored-by: JMFO16 <fournier.olivera.jm@gmail.com>
Co-authored-by: Kedr <sergeysamori.ua@gmail.com>
Co-authored-by: Leslie Munguía <moongeeuh@gmail.com>
Co-authored-by: Linda Li <wli62442@gmail.com>
Co-authored-by: Martim Pinto Paiva <pintopaivam@gmail.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Nazar Paruna <nazarparuna@gmail.com>
Co-authored-by: Richard Gould <rgould@u2622.ca>
Co-authored-by: Sandra Marcial <sandramarcial80@gmail.com>
Co-authored-by: Sara López <sarayupy@gmail.com>
Co-authored-by: Sara de Nicolas <saradenicolas12@gmail.com>
Co-authored-by: Sergey Shevelev <vlkgamer45@gmail.com>
Co-authored-by: Tran Lam Van Khoa <lamvankhoat1@gmail.com>
Co-authored-by: UNI <nibi727171@gmail.com>
Co-authored-by: Ventus Meigo <at.fbfzd@gmail.com>
Co-authored-by: Vince <vincemorel.vilan.889@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Zero <leedambak@gmail.com>
Co-authored-by: datschka <datschka@gmx.at>
Co-authored-by: hekin zhou <1916360372@qq.com>
Co-authored-by: jiangshanghan <jsh1215@hash.fyi>
Co-authored-by: kat o(`ω´ )o <memesarerealkool@gmail.com>
Co-authored-by: neko kyuri <Nekorin0621@gmail.com>
Co-authored-by: parkbird <kgh9812@naver.com>
Co-authored-by: そら <comi4work@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/de/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/en@pirate/
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/achievements/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/it/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hans/
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/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ko/
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/challenge/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/character/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/character/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/character/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/ko/
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/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/content/it/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/content/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/death/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/ko/
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/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/front/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/front/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ko/
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/gear/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/it/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/inventory/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/inventory/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/es/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ko/
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/questscontent/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/vi/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/fil/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/it/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/pt_BR/
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/Defaulttasks
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Inventory
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/Rebirth
Translation: Habitica/Settings
Translation: Habitica/Spells
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2022-08-30 21:25:33 +02:00
SabreCat
11347e5679 Merge branch 'release' into develop 2022-08-30 14:25:25 -05:00
SabreCat
a04479e689 4.242.0 2022-08-30 14:21:26 -05:00
SabreCat
4ce4e55e80 chore(git): update subproject commit 2022-08-30 14:20:50 -05:00
Natalie L
755f51b674 chore(content): add September 2022 Mystery Items (#14199)
* chore(submodule): add August 2022 Mystery Items

* chore(content): add September 2022 Mystery Items

* fix(typo): verb form

Co-authored-by: Sabe Jones <sabrecat@gmail.com>
2022-08-30 14:06:06 -05:00
SabreCat
6c1b21117f fix(teams): Stealth calc and small screens 2022-08-29 17:09:06 -05:00
SabreCat
14441701c9 Merge branch 'release' into develop 2022-08-29 15:51:45 -05:00
CuriousMagpie
a3e6aff330 update: more work on success modal 2022-08-29 16:12:53 -04:00
CuriousMagpie
a523d0b894 Merge branch 'develop' into group-tracking-modal 2022-08-29 13:33:15 -04:00
CuriousMagpie
8a809c3828 update: work on success modal, update to payment mixin to calculate monthly billing date 2022-08-26 17:58:51 -04:00
Anton de Regt
ccb821fd6f Fix redirect after register issue (#14182) 2022-08-26 15:56:58 -05:00
Natalie L
3664a1ebb1 fix(tavern): update Pause Damage Description, and Staff list (#14174) 2022-08-26 15:51:21 -05:00
Natalie L
509cb00374 fix(api): add API version (#14177) 2022-08-26 15:46:43 -05:00
theneelshah
bc4770577a Fix prop change handler for guild challenges. (#14169)
Fix props' change handler which is called when guild is changed from
notification.

Tests:

+ Guild challenges updated successfully when guild is changed from
notification center.

Co-authored-by: neel <neel@helpshift.com>
2022-08-26 15:45:32 -05:00
Jason Mishi Carvalho
f158852be5 Grey out skill when insufficient mana fixes #13286 (#14100)
* disable spell if user doesn't have enough mana

* differenciate insufficient mana and disabled spell

* linting

* reduce opacity, no hover state when insufficient mana

* display that lvl insufficient in spell tooltip

* change spell text color when spell has no effect

change spell-text color to blue-500 when spell has no effect
2022-08-26 15:22:04 -05:00
CuriousMagpie
40f433b099 update: work on success modal 2022-08-25 15:47:24 -04:00
CuriousMagpie
9a1266677a Merge remote-tracking branch 'upstream/develop' into group-tracking-modal 2022-08-25 13:20:54 -04:00
SabreCat
ee0f6fd78f fix(test): same thing for v3 2022-08-25 10:35:49 -05:00
SabreCat
3284611bbf fix(test): adjust expectation for exploit fix 2022-08-25 10:22:16 -05:00
SabreCat
986d38af69 4.241.4 2022-08-25 09:24:17 -05:00
SabreCat
7129639bbf fix(misc): correct one groups issue and two others 2022-08-25 09:24:08 -05:00
SabreCat
6aabf7b19a 4.241.3 2022-08-24 14:18:20 -05:00
SabreCat
a5d9448af1 fix(cron): don't process group tasks during user cron 2022-08-24 14:18:07 -05:00
SabreCat
6e19a0ef2e Merge branch 'release' into develop 2022-08-24 11:06:11 -05:00
SabreCat
bc8b1884b7 4.241.2 2022-08-24 11:05:36 -05:00
SabreCat
1aae9638ec fix(tasks): address regressions from group plan rollout 2022-08-24 11:05:19 -05:00
dependabot[bot]
e6b0c1e488 build(deps): bump @babel/core from 7.18.10 to 7.18.13 (#14184)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.18.10 to 7.18.13.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.13/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-23 19:57:59 -04:00
dependabot[bot]
d5bbc9599c build(deps): bump core-js from 3.23.5 to 3.24.1 in /website/client (#14154)
Bumps [core-js](https://github.com/zloirock/core-js) from 3.23.5 to 3.24.1.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/compare/v3.23.5...v3.24.1)

---
updated-dependencies:
- dependency-name: core-js
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-23 19:49:41 -04:00
SabreCat
a2f191089c 4.241.1 2022-08-23 14:38:09 -05:00
SabreCat
75c8486b1a fix(checklists): allow scoring own items 2022-08-23 14:38:05 -05:00
Sabe Jones
20854057ad fix(migration): handle orphaned assignments 2022-08-23 19:18:01 +00:00
SabreCat
ae3f064197 4.241.0 2022-08-23 12:49:25 -05:00
SabreCat
67ee0b72d3 fix(tasks): don't show reset counter control on group tasks 2022-08-23 12:40:32 -05:00
SabreCat
aebf13810f fix(tasks): remove spurious uncheck notification 2022-08-23 11:33:06 -05:00
SabreCat
971891dd6b fix(tasks): no really, address not-found error 2022-08-23 10:48:21 -05:00
SabreCat
395b8db932 fix(tasks): fix unassigned error case 2022-08-23 09:54:44 -05:00
SabreCat
da5c3f9602 fix(tasks): address not-found error on open uncheck 2022-08-23 09:36:23 -05:00
SabreCat
4c85b933cb fix(tests): correct one last v3 test and wrap v4 2022-08-22 21:59:51 -05:00
SabreCat
82abdaa0c4 WIP(tests): finish cleaning up v3 integrations 2022-08-22 21:47:55 -05:00
SabreCat
02c50b6126 WIP(tests): fix various assign requests and needs-work flow 2022-08-22 20:45:22 -05:00
SabreCat
3ab88bbb3f fix(tests): short circuit getter, adjust expectations 2022-08-22 18:49:56 -05:00
SabreCat
5251598369 fix(cron): fix score down breaking during middleware 2022-08-22 16:38:51 -05:00
SabreCat
149da578fd fix(teams): fix fix fix
Removed testing banner
Fixed a JS console error when assigning a user to a previously open task
Fixed a potential abuse where user might be able to score someone else's 
task via API call
Fixed an issue where finding tasks by alias could return tasks belonging 
to other users
Fixed an issue that was appending the user's party ID to their list of 
Guilds
Fixed an issue where group tasks were not receiving the default tag 
needed for filtering them on user's personal list
2022-08-22 16:16:23 -05:00
SabreCat
35d963a397 fix(teams): tweak FAQ and fix sync test 2022-08-22 14:43:21 -05:00
SabreCat
cccd8c3b1b Revert "fix(tests): catch non-array parameter"
This reverts commit 595c131398.
2022-08-22 14:17:06 -05:00
SabreCat
631d7111a5 fix(teams): send @username in notifications 2022-08-22 11:39:53 -05:00
SabreCat
89c07529ea feat(teams): add FAQ entry
Also a few client side fixes
2022-08-22 11:27:08 -05:00
SabreCat
595c131398 fix(tests): catch non-array parameter 2022-08-19 17:13:48 -05:00
SabreCat
f063b9e81c fix(tests): sanity and common 2022-08-19 16:10:18 -05:00
SabreCat
49a20218a5 fix(onboarding): improve modal launching and clicky behavior 2022-08-19 14:09:41 -05:00
SabreCat
95714599f0 feat(onboarding): welcome modal 2022-08-19 13:04:48 -05:00
SabreCat
5893312d75 Merge branch 'develop' into sabrecat/teams-rebase 2022-08-17 14:28:43 -05:00
CuriousMagpie
d70dd2e6dd update: add analytics event upon group creation/upgrade (temp code & console.logs commented out) 2022-08-15 17:13:51 -04:00
SabreCat
71fa4d6cb7 Merge branch 'release' into develop 2022-08-15 15:36:18 -05:00
SabreCat
922b2e985a 4.240.0 2022-08-15 15:29:12 -05:00
SabreCat
b82239811c chore(content): add items to featured 2022-08-15 15:28:47 -05:00
Natalie L
f0b5637e9e chore(content): add Porcelain Magic Hatching Potion (#14168)
* chore(submodule): add August 2022 Mystery Items

* chore: update habitica-images

* chore(content): add Porcelain Magic Hatching Potion

* chore(content): update moonglow potion availability

* fix(events): no gap between events

Co-authored-by: Sabe Jones <sabrecat@gmail.com>
2022-08-15 15:14:23 -05:00
CuriousMagpie
f078d19e4b Merge branch 'develop' into group-tracking-modal 2022-08-15 13:49:35 -04:00
dependabot[bot]
2c93b3e2e3 build(deps): bump @babel/preset-env from 7.18.6 to 7.18.10 (#14158)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.18.6 to 7.18.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.10/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-15 13:31:37 -04:00
dependabot[bot]
72a9417de9 build(deps): bump @babel/core from 7.18.6 to 7.18.10 (#14157)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.18.6 to 7.18.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.10/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-15 13:06:55 -04:00
CuriousMagpie
e08d0f4016 update: style changes, add upgraded group demographics to success modal, add description counter 2022-08-12 16:44:58 -04:00
CuriousMagpie
0435e3537a update: add temp button & function for triggering success modal; add comments 2022-08-11 16:39:49 -04:00
SabreCat
0aadee550e fix(admin): add data value for expand state 2022-08-11 14:16:27 -05:00
CuriousMagpie
f487837b4b Merge branch 'develop' into group-tracking-modal 2022-08-11 15:02:40 -04:00
SabreCat
b593db2150 feat(admin): show subscription data 2022-08-10 16:20:32 -05:00
CuriousMagpie
a07c2e6268 update: dropdown and delete duplicated file 2022-08-10 16:48:09 -04:00
SabreCat
e3c552dd54 WIP(dropdown): add placeholder text 2022-08-10 16:02:56 -04:00
CuriousMagpie
8d1f7e77ed update: styling and dropdown 2022-08-10 14:00:46 -04:00
SabreCat
1701fc702b Merge branch 'develop' into sabrecat/teams-rebase 2022-08-09 11:55:46 -05:00
SabreCat
16dc6a1b4c Merge branch 'release' into develop 2022-08-09 11:54:45 -05:00
SabreCat
d3a91aab72 4.239.0 2022-08-09 11:52:26 -05:00
Natalie L
0528ee1761 fix(dates): update end dates for quest bundle (#14167)
* chore(submodule): add August 2022 Mystery Items

* chore(content): add Woodland Wizard achievement

* chore(content): add Forest Friends quest bundle

* fix(typo): whitespace

* fix(dates): update end date for quest bundle

Co-authored-by: Sabe Jones <sabrecat@gmail.com>
2022-08-09 11:50:05 -05:00
CuriousMagpie
cfd601e7bf update: styling and some function work 2022-08-08 17:16:41 -04:00
SabreCat
1b4d670b0a fix(tasks): styles and wordings 2022-08-08 15:50:37 -05:00
CuriousMagpie
5d81c63897 update: upgrade groups sign up page (slightly) 2022-08-08 16:36:14 -04:00
SabreCat
3654e01fee Merge branch 'develop' into sabrecat/teams-rebase 2022-08-08 12:13:29 -05:00
SabreCat
fdbeda19e2 fix(tasks): icons and select lists 2022-08-08 12:13:15 -05:00
dependabot[bot]
db723d79a4 build(deps): bump dompurify from 2.3.8 to 2.3.10 in /website/client (#14132)
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 2.3.8 to 2.3.10.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/2.3.8...2.3.10)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-05 11:22:23 -04:00
dependabot[bot]
d5d1bfbd99 build(deps): bump terser from 4.6.7 to 4.8.1 in /website/client (#14133)
Bumps [terser](https://github.com/terser/terser) from 4.6.7 to 4.8.1.
- [Release notes](https://github.com/terser/terser/releases)
- [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/terser/terser/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-05 11:21:48 -04:00
dependabot[bot]
d06f4f4e1e build(deps-dev): bump @babel/plugin-proposal-optional-chaining (#14135)
Bumps [@babel/plugin-proposal-optional-chaining](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-proposal-optional-chaining) from 7.18.6 to 7.18.9.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.9/packages/babel-plugin-proposal-optional-chaining)

---
updated-dependencies:
- dependency-name: "@babel/plugin-proposal-optional-chaining"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-05 11:21:20 -04:00
dependabot[bot]
82c4260fca build(deps-dev): bump run-rs from 0.7.6 to 0.7.7 (#14138)
Bumps [run-rs](https://github.com/vkarpov15/run-rs) from 0.7.6 to 0.7.7.
- [Release notes](https://github.com/vkarpov15/run-rs/releases)
- [Changelog](https://github.com/vkarpov15/run-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vkarpov15/run-rs/compare/0.7.6...0.7.7)

---
updated-dependencies:
- dependency-name: run-rs
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-05 11:20:57 -04:00
dependabot[bot]
0f3a26a490 build(deps): bump @babel/register from 7.18.6 to 7.18.9 (#14140)
Bumps [@babel/register](https://github.com/babel/babel/tree/HEAD/packages/babel-register) from 7.18.6 to 7.18.9.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.9/packages/babel-register)

---
updated-dependencies:
- dependency-name: "@babel/register"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-05 11:20:40 -04:00
dependabot[bot]
9c2963e557 build(deps): bump vue and vue-template-compiler in /website/client (#14143)
Bumps [vue](https://github.com/vuejs/core) and [vue-template-compiler](https://github.com/vuejs/vue). These dependencies needed to be updated together.

Updates `vue` from 2.6.14 to 2.7.8
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/commits)

Updates `vue-template-compiler` from 2.6.14 to 2.7.8
- [Release notes](https://github.com/vuejs/vue/releases)
- [Changelog](https://github.com/vuejs/vue/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vue/compare/v2.6.14...v2.7.8)

---
updated-dependencies:
- dependency-name: vue
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: vue-template-compiler
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-05 11:20:08 -04:00
CuriousMagpie
cff6c5674f update: add styling 2022-08-04 17:26:04 -04:00
CuriousMagpie
edcb3f4289 update: making dropdown go with PROPS 2022-08-03 12:07:30 -04:00
CuriousMagpie
bd28a282df updates: add demographic strings & drop-down and translated array code 2022-08-02 18:34:09 -04:00
SabreCat
9b9503b141 feat(tasks): make task copy/mirror pref per-group 2022-08-02 16:09:49 -05:00
CuriousMagpie
999071a15c still trying to get modal to work 2022-08-02 16:05:41 -04:00
CuriousMagpie
9bee9d0a06 working on editing code so the modal emits from group-plans instead of from within the file 2022-08-01 17:11:15 -04:00
SabreCat
a418752041 Merge branch 'develop' into sabrecat/teams-rebase 2022-08-01 15:40:08 -05:00
CuriousMagpie
f5b632e3e5 Merge remote-tracking branch 'upstream/develop' into group-tracking-modal 2022-08-01 15:55:00 -04:00
SabreCat
7f8e44ff49 fix(groups): style and data sync fixes 2022-07-28 15:53:29 -05:00
CuriousMagpie
8ecd152b41 moved modal file from /groups into /group-plans 2022-07-27 18:25:56 -04:00
SabreCat
e0e9381584 fix(groups): many, mostly style, fixes 2022-07-26 16:48:27 -05:00
CuriousMagpie
ba22c18cd9 separating group plan creation modal out from groupPlan.vue 2022-07-26 16:59:53 -04:00
SabreCat
18db432f7f feat(tasks): functional summary modal 2022-07-25 22:31:33 -05:00
SabreCat
30d3892fb4 Merge branch 'develop' into sabrecat/teams-rebase 2022-07-25 15:04:43 -05:00
SabreCat
e06a0e5e7f WIP(tasks): new summary modal 2022-07-21 15:35:59 -05:00
SabreCat
153561dd42 Merge branch 'develop' into sabrecat/teams-rebase 2022-07-18 15:50:20 -05:00
SabreCat
7b067de4b9 chore(analytics): add tracking for task mirroring preference 2022-07-05 15:10:36 -05:00
SabreCat
9453b1269e Merge branch 'develop' into sabrecat/teams-rebase 2022-07-05 14:34:53 -05:00
SabreCat
cf536a82f8 fix(teams): more style updates 2022-06-30 16:55:47 -05:00
SabreCat
5967e4356c fix(teams): style updates 2022-06-29 15:52:54 -05:00
SabreCat
6ebfa976fe Merge branch 'develop' into sabrecat/teams-rebase 2022-06-29 14:30:25 -05:00
SabreCat
ddd5f20609 fix(teams): don't complain about move route when not moving 2022-06-29 09:06:08 -05:00
SabreCat
a3f61306d3 feat(teams): user preference toggle for mirroring 2022-06-28 16:18:24 -05:00
SabreCat
712b85ce84 fix(teams): complete task sorting 2022-06-24 16:43:41 -05:00
SabreCat
9142588ba7 fix(tasks): better tasksOrder maintenance 2022-06-23 16:44:21 -05:00
SabreCat
b657172a2b Merge branch 'develop' into sabrecat/teams-rebase 2022-06-21 13:50:50 -05:00
SabreCat
b790b87ca8 Merge branch 'develop' into sabrecat/teams-rebase 2022-06-21 09:34:17 -05:00
SabreCat
9d3059fc30 fix(teams): copy in assignee username during migration 2022-06-16 16:04:55 -05:00
SabreCat
647371accc Merge branch 'develop' into sabrecat/teams-rebase 2022-06-16 14:11:47 -05:00
SabreCat
8b084e627e WIP(teams): show open tasks on user view 2022-06-16 13:25:09 -05:00
SabreCat
4ac1a3e717 Merge branch 'develop' into sabrecat/teams-rebase 2022-06-14 14:46:50 -05:00
SabreCat
a0177fa44d WIP(teams): display assigned tasks on user's personal board 2022-06-13 16:53:29 -05:00
SabreCat
9fec111c4d Merge branch 'develop' into sabrecat/teams-rebase 2022-06-06 15:15:16 -05:00
SabreCat
a559c1add8 refactor(tasks): get rid of behind-the-scenes task cloning 2022-06-03 16:40:09 -05:00
SabreCat
5868849034 Merge branch 'develop' into sabrecat/teams-rebase 2022-06-03 16:04:35 -05:00
SabreCat
c98c7ab26c Merge branch 'develop' into sabrecat/teams-rebase 2022-05-31 15:51:16 -05:00
SabreCat
1ef7924ba5 Merge branch 'develop' into sabrecat/teams-rebase 2022-05-24 09:39:01 -05:00
SabreCat
7651e6a540 Merge branch 'develop' into sabrecat/teams-rebase 2022-05-19 14:30:29 -05:00
SabreCat
bb20c44fde fix(needs-work): don't show Gold/Experience depletion for manager 2022-05-19 14:28:35 -05:00
SabreCat
eb99ca0411 fix(cron): reset completions even if Daily wasn't due 2022-05-17 14:02:18 -05:00
SabreCat
9c24d43a13 Merge branch 'develop' into sabrecat/teams-rebase 2022-05-17 09:34:51 -05:00
SabreCat
bf9a7ea7d9 fix(teams): take user to relevant group from task assignment click 2022-05-13 14:26:14 -05:00
SabreCat
ca1dbd2fc4 Merge branch 'develop' into sabrecat/teams-rebase 2022-05-12 13:58:15 -05:00
SabreCat
04107ed6d3 fix(tasks): 12px padding, not 16 2022-05-10 15:36:43 -05:00
SabreCat
209b7bd1aa Merge branch 'develop' into sabrecat/teams-rebase 2022-05-10 13:58:14 -05:00
SabreCat
db354875ee fix(teams): adjust task title spacing, don't damage user for team Daily 2022-05-06 14:23:09 -05:00
SabreCat
40af14b061 fix(multiassign): don't allow nonmanagers to uncheck from footer 2022-05-05 15:16:30 -05:00
SabreCat
1d048e0c35 Merge branch 'develop' into sabrecat/teams-rebase 2022-05-05 13:56:36 -05:00
SabreCat
6a5f467a35 fix(teams): hover states, missing snackbars 2022-05-04 17:02:09 -05:00
SabreCat
86b0d6d86c fix(cron): handle when leader not found 2022-05-04 14:10:57 -05:00
SabreCat
565d33f6a7 Merge branch 'develop' into sabrecat/teams-rebase 2022-05-03 15:53:11 -05:00
SabreCat
588e5dd487 Merge branch 'develop' into sabrecat/teams-rebase 2022-04-29 14:42:08 -05:00
SabreCat
d8fbf9420e fix(teams): delete assignedUsers on open tasks 2022-04-28 15:48:40 -05:00
SabreCat
edb606814c feat(teams): beta testing banner 2022-04-22 15:47:55 -05:00
SabreCat
32823e3760 Merge branch 'develop' into sabrecat/teams-rebase 2022-04-21 14:10:27 -05:00
SabreCat
48f5ffc997 Merge branch 'develop' into sabrecat/teams-rebase 2022-04-15 14:26:19 -05:00
SabreCat
90375e3bc4 Merge branch 'develop' into sabrecat/teams-rebase 2022-04-14 15:36:26 -05:00
SabreCat
c4a92ba384 Merge branch 'develop' into sabrecat/teams-rebase 2022-04-12 14:48:45 -05:00
SabreCat
8f7e5d544e Merge branch 'develop' into sabrecat/teams-rebase 2022-04-07 14:55:16 -05:00
SabreCat
40113b0458 Merge branch 'develop' into sabrecat/teams-rebase 2022-04-05 13:56:25 -05:00
SabreCat
48be0a38bf Merge branch 'develop' into sabrecat/teams-rebase 2022-03-31 14:49:35 -05:00
SabreCat
459b327e2d feat(nav): clicking "Group" goes to first group 2022-03-30 15:53:48 -05:00
SabreCat
d3fde93762 fix(cron): don't adjust task decay by "assignments" for open Daily 2022-03-30 13:06:50 -05:00
SabreCat
aa81c330af Merge branch 'develop' into sabrecat/teams-rebase 2022-03-30 11:35:33 -05:00
SabreCat
7283d112f4 Merge branch 'develop' into sabrecat/teams-rebase 2022-03-29 14:10:18 -05:00
SabreCat
f1b0aa2e7c Merge branch 'develop' into sabrecat/teams-rebase 2022-03-28 16:34:31 -05:00
SabreCat
354f3578a2 Merge branch 'develop' into sabrecat/teams-rebase 2022-03-23 20:23:29 -05:00
SabreCat
c5ef803458 Merge branch 'develop' into sabrecat/teams-rebase 2022-03-22 16:29:47 -05:00
SabreCat
40801c0d32 Merge branch 'develop' into sabrecat/teams-rebase 2022-03-15 15:05:25 -05:00
SabreCat
e9ca17bbd8 Merge branch 'develop' into sabrecat/teams-rebase 2022-03-14 14:33:36 -05:00
SabreCat
5a638ab4b8 Merge branch 'develop' into sabrecat/teams-rebase 2022-03-09 16:37:31 -06:00
SabreCat
7fa4e6f791 fix(teams): don't show status footer for Habits or Rewards 2022-03-09 11:24:19 -06:00
SabreCat
61be42bf05 WIP(teams): data migration draft 2022-03-04 18:17:43 -06:00
SabreCat
9d4bf22720 fix(teams): Close button and padding fix 2022-03-04 15:15:50 -06:00
SabreCat
24349bed0a fix(teams): display and logic adjustments 2022-03-01 16:18:57 -06:00
SabreCat
17b93322aa fix(teams): reload completed To Do 2022-02-28 16:28:54 -06:00
SabreCat
ef6d92e7af Merge branch 'develop' into sabrecat/teams-rebase 2022-02-28 09:50:07 -06:00
SabreCat
35ed158dd9 WIP(teams): updated completion states
and fixed an issue with cron saving
2022-02-24 16:23:46 -06:00
SabreCat
31cbcf53a2 Merge branch 'develop' into sabrecat/teams-rebase 2022-02-24 14:54:34 -06:00
SabreCat
3a2fd28199 WIP(tasks): start task refresh process 2022-02-22 16:50:35 -06:00
SabreCat
1473408752 Merge branch 'develop' into sabrecat/teams-rebase 2022-02-22 12:18:59 -06:00
SabreCat
53babfb9fe WIP(teams): snakey checkmark 2022-02-18 17:41:32 -06:00
SabreCat
64694c3a29 WIP(teams): partial single-assign Dailies styling 2022-02-17 22:00:45 -06:00
SabreCat
1bd2ec0463 fix(branch): reset package files to develop 2022-02-17 16:07:53 -06:00
SabreCat
4e9625454c WIP(teams): new single assign footer states for To Do's 2022-02-17 15:52:59 -06:00
SabreCat
a58dd35fbe WIP(teams): tiny checkmarks 2022-02-17 15:52:59 -06:00
SabreCat
54088f5374 WIP(teams): fixes from demo session 2022-02-17 15:52:59 -06:00
SabreCat
9eebcf9b16 fix(deps): bad babel-eslint version? 2022-02-17 15:52:58 -06:00
SabreCat
44722a0d4c chore(deps): update package locks 2022-02-17 15:52:58 -06:00
SabreCat
7eae0a83f9 4.221.2 2022-02-17 15:52:40 -06:00
SabreCat
294b94206f fix(tz): remove moment-timezone completely 2022-02-17 15:51:55 -06:00
SabreCat
7e73c336dd WIP(teams): stylish and functional multi assign checkboxes 2022-02-17 15:51:28 -06:00
SabreCat
82d3545c08 WIP(teams): various fixes 2022-02-17 15:51:28 -06:00
SabreCat
e312ea943f WIP(teams): partial style implementation for status rows 2022-02-17 15:51:27 -06:00
SabreCat
a4a1595ec7 WIP(teams): very janky multi status 2022-02-17 15:51:27 -06:00
SabreCat
4b07e3a116 WIP(teams): start of multi state implementation 2022-02-17 15:51:27 -06:00
SabreCat
13e645fa4b fix(teams): correct some unassignment bugs 2022-02-17 15:51:27 -06:00
SabreCat
0d876472a3 WIP(teams): fix initial assignment sync, add Daily handling 2022-02-17 15:51:26 -06:00
SabreCat
9e527f4f35 WIP(teams): can do To Do's 2022-02-17 15:51:09 -06:00
SabreCat
eaa5f821a4 WIP(multi-assign): functioning multi 2022-02-17 15:50:44 -06:00
SabreCat
a495db8480 WIP(assignment): change to object style
to do--let assignment API accept an array
2022-02-17 15:50:42 -06:00
SabreCat
fa99458ca4 WIP(multiassign): resume shared completion implementation 2022-02-17 15:50:23 -06:00
Sabe Jones
1f81e1971b fix(cron): remove now redundant logic 2022-02-17 15:49:56 -06:00
Sabe Jones
45fc2b62e3 fix(cron): process team board tasks assigned to the user 2022-02-17 15:49:56 -06:00
Sabe Jones
f843564444 fix(storybook): temporarily disable story 2022-02-17 15:49:55 -06:00
Sabe Jones
fb216fba8e fix(cron): reset checklists as needed 2022-02-17 15:49:54 -06:00
Sabe Jones
603cc93957 fix(ui): further cursor tweaks for Teams 2022-02-17 15:49:54 -06:00
Sabe Jones
63e0875f32 fix(teams): allow ticking team checklists 2022-02-17 15:49:34 -06:00
Sabe Jones
029e41472f fix(teams): filter further to hide team tasks, don't override 2022-02-17 15:49:19 -06:00
Sabe Jones
1752c08fd9 fix(teams): allow managers to reorder tasks 2022-02-17 15:49:19 -06:00
Sabe Jones
395d9e7650 fix(teams): maybe goodish style? 2022-02-17 15:49:18 -06:00
Sabe Jones
61d396204f fix(teams): more layout tweakage 2022-02-17 15:49:17 -06:00
Sabe Jones
f233c511cc fix(teams): layout issues, error, change timezone format 2022-02-17 15:49:16 -06:00
Sabe Jones
0806391ab8 feat(teams): Day Start and adjust uncheck wording 2022-02-17 15:48:55 -06:00
Sabe Jones
dcaba7f186 fix(teams): update beta banner wording 2022-02-17 15:47:55 -06:00
Sabe Jones
59dc97b75f WIP(teams): fixes, beta banner 2022-02-17 15:47:37 -06:00
Sabe Jones
85a9ea726c WIP(teams): draft of server literal-actual-cron script 2022-02-17 15:47:23 -06:00
Sabe Jones
d53813adc7 WIP(teams): add some analytics, remove extraneous logic 2022-02-17 15:47:02 -06:00
Sabe Jones
221dd7a81e fix(tasks): more lock icon revision, no error on manager uncheck 2022-02-17 15:46:45 -06:00
Sabe Jones
99bf6349e1 fix(assignment): update single select dropdown style 2022-02-17 15:46:44 -06:00
Sabe Jones
801b902bb8 fix(tasks): manager lock icon, coerce task value to Number 2022-02-17 15:46:43 -06:00
Sabe Jones
072b09e030 feat(teams): quick add
Also fixes issue with lock icons
Also hides task copies from personal task board
2022-02-17 15:46:42 -06:00
Sabe Jones
a5680836bd feat(teams): new disapproval workflow, managers can uncheck tasks 2022-02-17 15:46:23 -06:00
Sabe Jones
bb9ba61d12 feat(teams): show team name 2022-02-17 15:44:58 -06:00
Sabe Jones
a88f97831a fix(teams): update single select style and correct create/edit issue 2022-02-17 15:44:58 -06:00
Sabe Jones
ae0528e5cd WIP(teams): fix initially unassigned task, add completedBy data 2022-02-17 15:44:57 -06:00
Sabe Jones
74345adf6b fix(teams): make selectSingle workflows functional 2022-02-17 15:44:56 -06:00
negue
7dbee4caed clone selectMulti to selectSingle for assignedMember 2022-02-17 15:44:55 -06:00
Sabe Jones
f4feb09fbc fix(cron): actually process cron for assigned tasks 2022-02-17 15:44:54 -06:00
Sabe Jones
6cddb3bf82 WIP(teams): more partial fixing 2022-02-17 15:44:36 -06:00
Sabe Jones
248e1c6fe9 WIP(teams): reimplement open tasking 2022-02-17 15:43:28 -06:00
Sabe Jones
6e39c79cff WIP(teams): simplify task footers 2022-02-17 15:41:07 -06:00
Sabe Jones
5bf4e18ce8 WIP(teams): don't damage leader for incomplete team Dailies 2022-02-17 15:41:06 -06:00
Sabe Jones
cab4a2a8fa WIP(teams): begin simplification 2022-02-17 15:41:05 -06:00
499 changed files with 17652 additions and 11608 deletions

View File

@@ -2,6 +2,9 @@ name: Test
on: [push, pull_request]
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest

View File

@@ -2,6 +2,7 @@
"name": "Habitica V3 API Documentation",
"title": "Habitica",
"url": "https://habitica.com",
"version": "3.0.0",
"sampleUrl": null,
"header": {
"title": "Introduction",

View File

@@ -1,4 +1,3 @@
version: "3"
services:
client:
build:
@@ -9,7 +8,6 @@ services:
- server
environment:
- BASE_URL=http://server:3000
image: habitica
networks:
- habitica
ports:
@@ -27,7 +25,6 @@ services:
- mongo
environment:
- NODE_DB_URI=mongodb://mongo/habitrpg
image: habitica
networks:
- habitica
ports:

View File

@@ -0,0 +1,97 @@
/* eslint-disable no-console */
import { model as UserModel } from '../../../website/server/models/user';
import { TransactionModel } from '../../../website/server/models/transaction';
const MIGRATION_NAME = '20220915_transactions_user_name';
/* transaction config */
const transactionPerRun = 500;
const progressCount = 1000;
const transactionQuery = {
migration: { $ne: MIGRATION_NAME }, // skip already migrated entries
'transactionType': { $in: ['gift_send', 'gift_receive'] },
};
let count = 0;
async function updateTransaction (transaction, userNameMap) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (userNameMap.has(transaction.reference)) {
set['referenceText'] = userNameMap.get(transaction.reference);
} else {
set['referenceText'] = 'Account not found';
}
if (count % progressCount === 0) {
console.warn(`${count} ${transaction._id}`);
}
return TransactionModel.updateOne({
_id: transaction._id
}, { $set: set }).exec();
}
export default async function processTransactions () {
const fields = {
_id: 1,
reference: 1,
referenceText: 1,
};
const userNameMap = new Map();
while (true) { // eslint-disable-line no-constant-condition
const foundTransactions = await TransactionModel // eslint-disable-line no-await-in-loop
.find(transactionQuery)
.limit(transactionPerRun)
.sort({reference: 1})
.select(fields)
.lean()
.exec();
if (foundTransactions.length === 0) {
console.warn('All appropriate transactions found and modified.');
console.warn(`\n${count} transactions processed\n`);
break;
}
// check for unknown users and load the names
const userIdsToLoad = [];
for (const foundTransaction of foundTransactions) {
const userId = foundTransaction.reference;
if (userNameMap.has(userId)) {
continue;
}
userIdsToLoad.push(userId);
}
const users = await UserModel // eslint-disable-line no-await-in-loop
.find({
_id: { $in: userIdsToLoad }
})
.select({
_id: 1,
'auth.local.username': 1,
})
.lean()
.exec();
for (const user of users) {
const localUserName = user.auth?.local?.username;
if (!localUserName) {
console.warn(`\nNo Username found for ID: ${user._id}\n`);
continue;
}
userNameMap.set(user._id, localUserName)
}
await Promise.all(foundTransactions.map(t => updateTransaction(t, userNameMap))); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -0,0 +1,86 @@
/*
* Award Habitoween ladder items to participants in this month's Habitoween festivities
*/
/* eslint-disable no-console */
const MIGRATION_NAME = '20221031_habitoween_ladder'; // Update when running in future years
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {};
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,
};
set.migration = MIGRATION_NAME;
if (user && user.items && user.items.pets && user.items.pets['JackOLantern-RoyalPurple']) {
set['items.mounts.JackOLantern-RoyalPurple'] = true;
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Glow']) {
set['items.pets.JackOLantern-RoyalPurple'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Glow']) {
set['items.mounts.JackOLantern-Glow'] = true;
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Ghost']) {
set['items.pets.JackOLantern-Glow'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) {
set['items.mounts.JackOLantern-Ghost'] = true;
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) {
set['items.pets.JackOLantern-Ghost'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) {
set['items.mounts.JackOLantern-Base'] = true;
} else {
set['items.pets.JackOLantern-Base'] = 5;
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$inc: inc, $set: set}).exec();
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2022-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,119 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20221031_pet_set_group_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Wolf-Skeleton']
&& pets['TigerCub-Skeleton']
&& pets['PandaCub-Skeleton']
&& pets['LionCub-Skeleton']
&& pets['Fox-Skeleton']
&& pets['FlyingPig-Skeleton']
&& pets['Dragon-Skeleton']
&& pets['Cactus-Skeleton']
&& pets['BearCub-Skeleton']
&& pets['Gryphon-Skeleton']
&& pets['Hedgehog-Skeleton']
&& pets['Deer-Skeleton']
&& pets['Egg-Skeleton']
&& pets['Rat-Skeleton']
&& pets['Octopus-Skeleton']
&& pets['Seahorse-Skeleton']
&& pets['Parrot-Skeleton']
&& pets['Rooster-Skeleton']
&& pets['Spider-Skeleton']
&& pets['Owl-Skeleton']
&& pets['Penguin-Skeleton']
&& pets['TRex-Skeleton']
&& pets['Rock-Skeleton']
&& pets['Bunny-Skeleton']
&& pets['Slime-Skeleton']
&& pets['Sheep-Skeleton']
&& pets['Cuttlefish-Skeleton']
&& pets['Whale-Skeleton']
&& pets['Cheetah-Skeleton']
&& pets['Horse-Skeleton']
&& pets['Frog-Skeleton']
&& pets['Snake-Skeleton']
&& pets['Unicorn-Skeleton']
&& pets['Sabretooth-Skeleton']
&& pets['Monkey-Skeleton']
&& pets['Snail-Skeleton']
&& pets['Falcon-Skeleton']
&& pets['Treeling-Skeleton']
&& pets['Axolotl-Skeleton']
&& pets['Turtle-Skeleton']
&& pets['Armadillo-Skeleton']
&& pets['Cow-Skeleton']
&& pets['Beetle-Skeleton']
&& pets['Ferret-Skeleton']
&& pets['Sloth-Skeleton']
&& pets['Triceratops-Skeleton']
&& pets['GuineaPig-Skeleton']
&& pets['Peacock-Skeleton']
&& pets['Butterfly-Skeleton']
&& pets['Nudibranch-Skeleton']
&& pets['Hippo-Skeleton']
&& pets['Yarn-Skeleton']
&& pets['Pterodactyl-Skeleton']
&& pets['Badger-Skeleton']
&& pets['Squirrel-Skeleton']
&& pets['SeaSerpent-Skeleton']
&& pets['Kangaroo-Skeleton']
&& pets['Alligator-Skeleton']
&& pets['Velociraptor-Skeleton']
&& pets['Dolphin-Skeleton']
&& pets['Robot-Skeleton']) {
set['achievements.boneToPick'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
let query = {
// migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2022-01-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]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -0,0 +1,71 @@
import filter from 'lodash/filter';
import find from 'lodash/find';
import isArray from 'lodash/isArray';
import { model as Group } from '../../website/server/models/group';
import { model as User } from '../../website/server/models/user';
import * as Tasks from '../../website/server/models/task';
async function updateTeamTasks (team) {
const toSave = [];
const teamTasks = await Tasks.Task.find({
'group.id': team._id,
}).exec();
const teamBoardTasks = filter(teamTasks, task => !task.userId);
const teamUserTasks = filter(teamTasks, task => task.userId);
for (const boardTask of teamBoardTasks) {
if (isArray(boardTask.group.assignedUsers)) {
boardTask.group.approval = undefined;
boardTask.group.assignedDate = undefined;
boardTask.group.assigningUsername = undefined;
boardTask.group.sharedCompletion = undefined;
for (const assignedUserId of boardTask.group.assignedUsers) {
const assignedUser = await User.findById(assignedUserId, 'auth'); // eslint-disable-line no-await-in-loop
const userTask = find(teamUserTasks, task => task.userId === assignedUserId
&& task.group.taskId === boardTask._id);
if (!boardTask.group.assignedUsersDetail) boardTask.group.assignedUsersDetail = {};
if (userTask && assignedUser) {
boardTask.group.assignedUsersDetail[assignedUserId] = {
assignedDate: userTask.group.assignedDate,
assignedUsername: assignedUser.auth.local.username,
assigningUsername: userTask.group.assigningUsername,
completed: userTask.completed || false,
completedDate: userTask.dateCompleted,
};
} else if (assignedUser) {
boardTask.group.assignedUsersDetail[assignedUserId] = {
assignedDate: new Date(),
assignedUsername: assignedUser.auth.local.username,
assigningUsername: null,
completed: false,
completedDate: null,
};
} else {
const taskIndex = boardTask.group.assignedUsers.indexOf(assignedUserId);
boardTask.group.assignedUsers.splice(taskIndex, 1);
}
if (userTask) toSave.push(Tasks.Task.findByIdAndDelete(userTask._id));
}
boardTask.markModified('group');
toSave.push(boardTask.save());
}
}
return Promise.all(toSave);
}
export default async function processTeams () {
const activeTeams = await Group.find({
'purchased.plan.customerId': { $exists: true },
$or: [
{ 'purchased.plan.dateTerminated': { $exists: false } },
{ 'purchased.plan.dateTerminated': null },
{ 'purchased.plan.dateTerminated': { $gt: new Date() } },
],
}).exec();
const taskPromises = activeTeams.map(updateTeamTasks);
return Promise.all(taskPromises);
}

2416
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,22 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "4.238.1",
"version": "4.251.0",
"main": "./website/server/index.js",
"dependencies": {
"@babel/core": "^7.18.6",
"@babel/preset-env": "^7.18.6",
"@babel/register": "^7.18.6",
"@google-cloud/trace-agent": "^5.1.6",
"@babel/core": "^7.19.6",
"@babel/preset-env": "^7.20.2",
"@babel/register": "^7.18.9",
"@google-cloud/trace-agent": "^7.1.2",
"@parse/node-apn": "^5.1.3",
"@slack/webhook": "^6.1.0",
"accepts": "^1.3.8",
"amazon-payments": "^0.2.9",
"amplitude": "^6.0.0",
"apidoc": "^0.52.0",
"apidoc": "^0.53.1",
"apple-auth": "^1.0.7",
"bcrypt": "^5.0.1",
"body-parser": "^1.20.0",
"bcrypt": "^5.1.0",
"body-parser": "^1.20.1",
"bootstrap": "^4.6.0",
"compression": "^1.7.4",
"cookie-session": "^2.0.0",
@@ -27,7 +27,7 @@
"eslint": "^6.8.0",
"eslint-config-habitrpg": "^6.2.0",
"eslint-plugin-mocha": "^5.0.0",
"express": "^4.18.1",
"express": "^4.18.2",
"express-basic-auth": "^1.2.1",
"express-validator": "^5.2.0",
"glob": "^8.0.3",
@@ -41,9 +41,9 @@
"helmet": "^4.6.0",
"image-size": "^1.0.2",
"in-app-purchase": "^1.11.3",
"js2xmlparser": "^4.0.2",
"js2xmlparser": "^5.0.0",
"jsonwebtoken": "^8.5.1",
"jwks-rsa": "^2.1.4",
"jwks-rsa": "^2.1.5",
"lodash": "^4.17.21",
"merge-stream": "^2.0.0",
"method-override": "^3.0.0",
@@ -54,27 +54,27 @@
"nconf": "^0.12.0",
"node-gcm": "^1.0.5",
"on-headers": "^1.0.2",
"passport": "^0.5.0",
"passport": "^0.6.0",
"passport-facebook": "^3.0.0",
"passport-google-oauth2": "^0.2.0",
"passport-google-oauth20": "2.0.0",
"paypal-rest-sdk": "^1.8.1",
"pp-ipn": "^1.1.0",
"ps-tree": "^1.0.0",
"rate-limiter-flexible": "^2.3.7",
"rate-limiter-flexible": "^2.4.0",
"redis": "^3.1.2",
"regenerator-runtime": "^0.13.9",
"regenerator-runtime": "^0.13.11",
"remove-markdown": "^0.5.0",
"rimraf": "^3.0.2",
"short-uuid": "^4.2.0",
"stripe": "^8.222.0",
"superagent": "^7.1.6",
"short-uuid": "^4.2.2",
"stripe": "^10.13.0",
"superagent": "^8.0.5",
"universal-analytics": "^0.5.3",
"useragent": "^2.1.9",
"uuid": "^8.3.2",
"validator": "^13.7.0",
"vinyl-buffer": "^1.0.1",
"winston": "^3.8.1",
"winston": "^3.8.2",
"winston-loggly-bulk": "^3.2.1",
"xml2js": "^0.4.23"
},
@@ -111,18 +111,18 @@
},
"devDependencies": {
"axios": "^0.27.2",
"chai": "^4.3.6",
"chai": "^4.3.7",
"chai-as-promised": "^7.1.1",
"chai-moment": "^0.1.0",
"chalk": "^4.1.2",
"chalk": "^5.1.2",
"cross-spawn": "^7.0.3",
"expect.js": "^0.3.1",
"istanbul": "^1.1.0-alpha.1",
"mocha": "^5.1.1",
"monk": "^7.3.4",
"require-again": "^2.0.0",
"run-rs": "^0.7.6",
"sinon": "^13.0.2",
"run-rs": "^0.7.7",
"sinon": "^14.0.2",
"sinon-chai": "^3.7.0",
"sinon-stub-promise": "^4.0.0"
},

105
scripts/team-cron.js Normal file
View File

@@ -0,0 +1,105 @@
import forEach from 'lodash/forEach';
import { model as Group } from '../website/server/models/group';
import { model as User } from '../website/server/models/user';
import * as Tasks from '../website/server/models/task';
import { daysSince, shouldDo } from '../website/common/script/cron';
const TASK_VALUE_CHANGE_FACTOR = 0.9747;
const MIN_TASK_VALUE = -47.27;
async function updateTeamTasks (team) {
const toSave = [];
let teamLeader = await User.findOne({ _id: team.leader }, 'preferences').exec();
if (!teamLeader) { // why would this happen?
teamLeader = {
preferences: { }, // when options are sanitized this becomes CDS 0 at UTC
};
}
if (
!team.cron || !team.cron.lastProcessed
|| daysSince(team.cron.lastProcessed, teamLeader.preferences) > 0
) {
const tasks = await Tasks.Task.find({
'group.id': team._id,
userId: { $exists: false },
$or: [
{ type: 'todo', completed: false },
{ type: { $in: ['habit', 'daily'] } },
],
}).exec();
const tasksByType = {
habits: [], dailys: [], todos: [], rewards: [],
};
forEach(tasks, task => tasksByType[`${task.type}s`].push(task));
forEach(tasksByType.habits, habit => {
if (!(habit.up && habit.down) && habit.value !== 0) {
habit.value *= 0.5;
if (Math.abs(habit.value) < 0.1) habit.value = 0;
toSave.push(habit.save());
}
});
forEach(tasksByType.todos, todo => {
if (!todo.completed) {
const delta = TASK_VALUE_CHANGE_FACTOR ** todo.value;
todo.value -= delta;
if (todo.value < MIN_TASK_VALUE) todo.value = MIN_TASK_VALUE;
toSave.push(todo.save());
}
});
forEach(tasksByType.dailys, daily => {
let processChecklist = false;
let assignments = 0;
let completions = 0;
for (const assignedUser in daily.group.assignedUsersDetail) {
if (Object.prototype.hasOwnProperty.call(daily.group.assignedUsersDetail, assignedUser)) {
assignments += 1;
if (daily.group.assignedUsersDetail[assignedUser].completed) {
completions += 1;
daily.group.assignedUsersDetail[assignedUser].completed = false;
}
}
}
if (completions > 0) daily.markModified('group.assignedUsersDetail');
if (daily.completed) {
processChecklist = true;
daily.completed = false;
} else if (shouldDo(team.cron.lastProcessed, daily, teamLeader.preferences)) {
processChecklist = true;
const delta = TASK_VALUE_CHANGE_FACTOR ** daily.value;
if (assignments > 0) {
daily.value -= ((completions / assignments) * delta);
}
if (daily.value < MIN_TASK_VALUE) daily.value = MIN_TASK_VALUE;
}
daily.isDue = shouldDo(new Date(), daily, teamLeader.preferences);
if (processChecklist && daily.checklist.length > 0) {
daily.checklist.forEach(i => { i.completed = false; });
}
toSave.push(daily.save());
});
if (!team.cron) team.cron = {};
team.cron.lastProcessed = new Date();
toSave.push(team.save());
}
return Promise.all(toSave);
}
export default async function processTeamsCron () {
const activeTeams = await Group.find({
'purchased.plan.customerId': { $exists: true },
$or: [
{ 'purchased.plan.dateTerminated': { $exists: false } },
{ 'purchased.plan.dateTerminated': null },
{ 'purchased.plan.dateTerminated': { $gt: new Date() } },
],
}).exec();
const cronPromises = activeTeams.map(updateTeamTasks);
return Promise.all(cronPromises);
}

View File

@@ -13,11 +13,6 @@ function getUser () {
username: 'username',
email: 'email@email',
},
facebook: {
emails: [{
value: 'email@facebook',
}],
},
google: {
emails: [{
value: 'email@google',
@@ -62,30 +57,12 @@ describe('emails', () => {
expect(data).to.have.property('canSend', true);
});
it('returns correct user data [facebook users]', () => {
const attachEmail = requireAgain(pathToEmailLib);
const { getUserInfo } = attachEmail;
const user = getUser();
delete user.profile.name;
delete user.auth.local.email;
delete user.auth.google.emails;
delete user.auth.apple.emails;
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
expect(data).to.have.property('name', user.auth.local.username);
expect(data).to.have.property('email', user.auth.facebook.emails[0].value);
expect(data).to.have.property('_id', user._id);
expect(data).to.have.property('canSend', true);
});
it('returns correct user data [google users]', () => {
const attachEmail = requireAgain(pathToEmailLib);
const { getUserInfo } = attachEmail;
const user = getUser();
delete user.profile.name;
delete user.auth.local.email;
delete user.auth.facebook.emails;
delete user.auth.apple.emails;
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
@@ -103,7 +80,6 @@ describe('emails', () => {
delete user.profile.name;
delete user.auth.local.email;
delete user.auth.google.emails;
delete user.auth.facebook.emails;
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
@@ -118,7 +94,6 @@ describe('emails', () => {
const { getUserInfo } = attachEmail;
const user = getUser();
delete user.auth.local.email;
delete user.auth.facebook;
delete user.auth.google;
delete user.auth.apple;

View File

@@ -246,7 +246,7 @@ describe('Password Utilities', () => {
it('returns false if the user has no local auth', async () => {
const user = await generateUser({
auth: {
facebook: {},
google: {},
},
});
const res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({

View File

@@ -326,6 +326,7 @@ describe('Apple Payments', () => {
it('errors when a user is already subscribed', async () => {
payments.createSubscription.restore();
user = new User();
await user.save();
await applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing);

View File

@@ -11,10 +11,13 @@ import {
generateGroup,
} from '../../../../helpers/api-unit.helper';
import * as worldState from '../../../../../website/server/libs/worldState';
import { TransactionModel } from '../../../../../website/server/models/transaction';
describe('payments/index', () => {
let user; let group; let data; let
plan;
let user;
let group;
let data;
let plan;
beforeEach(async () => {
user = new User();
@@ -104,6 +107,23 @@ describe('payments/index', () => {
expect(recipient.purchased.plan.extraMonths).to.eql(3);
});
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 })
.exec();
expect(transactions).to.have.lengthOf(1);
});
it('does not set negative extraMonths if plan has past dateTerminated date', async () => {
const dateTerminated = moment().subtract(2, 'months').toDate();
recipient.purchased.plan.dateTerminated = dateTerminated;
@@ -672,10 +692,12 @@ describe('payments/index', () => {
context('No Active Promotion', () => {
beforeEach(() => {
sinon.stub(worldState, 'getCurrentEvent').returns(null);
sinon.stub(worldState, 'getCurrentEventList').returns([]);
});
afterEach(() => {
worldState.getCurrentEvent.restore();
worldState.getCurrentEventList.restore();
});
it('does not apply a discount', async () => {
@@ -692,14 +714,14 @@ describe('payments/index', () => {
context('Active Promotion', () => {
beforeEach(() => {
sinon.stub(worldState, 'getCurrentEvent').returns({
sinon.stub(worldState, 'getCurrentEventList').returns([{
...common.content.events.fall2020,
event: 'fall2020',
});
}]);
});
afterEach(() => {
worldState.getCurrentEvent.restore();
worldState.getCurrentEventList.restore();
});
it('applies a discount', async () => {

View File

@@ -1,12 +1,10 @@
import { each, find, findIndex } from 'lodash';
import { model as Challenge } from '../../../../website/server/models/challenge';
import { each, findIndex } from 'lodash';
import { model as Group } from '../../../../website/server/models/group';
import { model as User } from '../../../../website/server/models/user';
import * as Tasks from '../../../../website/server/models/task';
describe('Group Task Methods', () => {
let guild; let leader; let challenge; let
task;
let guild; let leader; let task;
const tasksToTest = {
habit: {
text: 'test habit',
@@ -31,10 +29,6 @@ describe('Group Task Methods', () => {
},
};
function findLinkedTask (updatedLeadersTask) {
return updatedLeadersTask.group.taskId === task._id;
}
beforeEach(async () => {
guild = new Group({
name: 'test party',
@@ -47,19 +41,9 @@ describe('Group Task Methods', () => {
guild.leader = leader._id;
challenge = new Challenge({
name: 'Test Challenge',
shortName: 'Test',
leader: leader._id,
group: guild._id,
});
leader.challenges = [challenge._id];
await Promise.all([
guild.save(),
leader.save(),
challenge.save(),
]);
});
@@ -78,7 +62,15 @@ describe('Group Task Methods', () => {
});
it('syncs an assigned task to a user', async () => {
await guild.syncTask(task, leader);
await guild.syncTask(task, [leader], leader);
const updatedTask = await Tasks.Task.findOne({ _id: task._id });
expect(updatedTask.group.assignedUsers).to.contain(leader._id);
expect(updatedTask.group.assignedUsersDetail[leader._id]).to.exist;
});
it('creates tags for a user when task is synced', async () => {
await guild.syncTask(task, [leader], leader);
const updatedLeader = await User.findOne({ _id: leader._id });
const tagIndex = findIndex(updatedLeader.tags, { id: guild._id });
@@ -88,197 +80,6 @@ describe('Group Task Methods', () => {
expect(newTag.name).to.equal(guild.name);
expect(newTag.group).to.equal(guild._id);
});
it('create tags for a user when task is synced', async () => {
await guild.syncTask(task, leader);
const updatedLeader = await User.findOne({ _id: leader._id });
const updatedLeadersTasks = await Tasks.Task.find({ _id: { $in: updatedLeader.tasksOrder[`${taskType}s`] } });
const syncedTask = find(updatedLeadersTasks, findLinkedTask);
expect(task.group.assignedUsers).to.contain(leader._id);
expect(syncedTask).to.exist;
});
it('syncs updated info for assigned task to a user', async () => {
await guild.syncTask(task, leader);
const updatedTaskName = 'Update Task name';
task.text = updatedTaskName;
await guild.syncTask(task, leader);
const updatedLeader = await User.findOne({ _id: leader._id });
const updatedLeadersTasks = await Tasks.Task.find({ _id: { $in: updatedLeader.tasksOrder[`${taskType}s`] } });
const syncedTask = find(updatedLeadersTasks, findLinkedTask);
expect(task.group.assignedUsers).to.contain(leader._id);
expect(syncedTask).to.exist;
expect(syncedTask.text).to.equal(task.text);
});
it('syncs checklist items to an assigned user', async () => {
await guild.syncTask(task, leader);
const updatedLeader = await User.findOne({ _id: leader._id });
const updatedLeadersTasks = await Tasks.Task.find({ _id: { $in: updatedLeader.tasksOrder[`${taskType}s`] } });
const syncedTask = find(updatedLeadersTasks, findLinkedTask);
if (task.type !== 'daily' && task.type !== 'todo') return;
expect(syncedTask.checklist.length).to.equal(task.checklist.length);
expect(syncedTask.checklist[0].text).to.equal(task.checklist[0].text);
});
describe('syncs updated info', async () => {
let newMember;
beforeEach(async () => {
newMember = new User({
guilds: [guild._id],
});
await newMember.save();
await guild.syncTask(task, leader);
await guild.syncTask(task, newMember);
});
it('syncs updated info for assigned task to all users', async () => {
const updatedTaskName = 'Update Task name';
task.text = updatedTaskName;
task.group.approval.required = true;
await guild.updateTask(task);
const updatedLeader = await User.findOne({ _id: leader._id });
const updatedLeadersTasks = await Tasks.Task.find({ _id: { $in: updatedLeader.tasksOrder[`${taskType}s`] } });
const syncedTask = find(updatedLeadersTasks, findLinkedTask);
const updatedMember = await User.findOne({ _id: newMember._id });
const updatedMemberTasks = await Tasks.Task.find({ _id: { $in: updatedMember.tasksOrder[`${taskType}s`] } });
const syncedMemberTask = find(updatedMemberTasks, findLinkedTask);
expect(task.group.assignedUsers).to.contain(leader._id);
expect(syncedTask).to.exist;
expect(syncedTask.text).to.equal(task.text);
expect(syncedTask.group.approval.required).to.equal(true);
expect(task.group.assignedUsers).to.contain(newMember._id);
expect(syncedMemberTask).to.exist;
expect(syncedMemberTask.text).to.equal(task.text);
expect(syncedMemberTask.group.approval.required).to.equal(true);
});
it('syncs a new checklist item to all assigned users', async () => {
if (task.type !== 'daily' && task.type !== 'todo') return;
const newCheckListItem = {
text: 'Checklist Item 1',
completed: false,
};
task.checklist.push(newCheckListItem);
await guild.updateTask(task, { newCheckListItem });
const updatedLeader = await User.findOne({ _id: leader._id });
const updatedLeadersTasks = await Tasks.Task.find({ _id: { $in: updatedLeader.tasksOrder[`${taskType}s`] } });
const syncedTask = find(updatedLeadersTasks, findLinkedTask);
const updatedMember = await User.findOne({ _id: newMember._id });
const updatedMemberTasks = await Tasks.Task.find({ _id: { $in: updatedMember.tasksOrder[`${taskType}s`] } });
const syncedMemberTask = find(updatedMemberTasks, findLinkedTask);
expect(syncedTask.checklist.length).to.equal(task.checklist.length);
expect(syncedTask.checklist[1].text).to.equal(task.checklist[1].text);
expect(syncedMemberTask.checklist.length).to.equal(task.checklist.length);
expect(syncedMemberTask.checklist[1].text).to.equal(task.checklist[1].text);
});
it('syncs updated info for checklist in assigned task to all users when flag is passed', async () => {
if (task.type !== 'daily' && task.type !== 'todo') return;
const updateCheckListText = 'Updated checklist item';
if (task.checklist) {
task.checklist[0].text = updateCheckListText;
}
await guild.updateTask(task, { updateCheckListItems: [task.checklist[0]] });
const updatedLeader = await User.findOne({ _id: leader._id });
const updatedLeadersTasks = await Tasks.Task.find({ _id: { $in: updatedLeader.tasksOrder[`${taskType}s`] } });
const syncedTask = find(updatedLeadersTasks, findLinkedTask);
const updatedMember = await User.findOne({ _id: newMember._id });
const updatedMemberTasks = await Tasks.Task.find({ _id: { $in: updatedMember.tasksOrder[`${taskType}s`] } });
const syncedMemberTask = find(updatedMemberTasks, findLinkedTask);
expect(syncedTask.checklist.length).to.equal(task.checklist.length);
expect(syncedTask.checklist[0].text).to.equal(updateCheckListText);
expect(syncedMemberTask.checklist.length).to.equal(task.checklist.length);
expect(syncedMemberTask.checklist[0].text).to.equal(updateCheckListText);
});
it('removes a checklist item in assigned task to all users when flag is passed with checklist id', async () => {
if (task.type !== 'daily' && task.type !== 'todo') return;
await guild.updateTask(task, { removedCheckListItemId: task.checklist[0].id });
const updatedLeader = await User.findOne({ _id: leader._id });
const updatedLeadersTasks = await Tasks.Task.find({ _id: { $in: updatedLeader.tasksOrder[`${taskType}s`] } });
const syncedTask = find(updatedLeadersTasks, findLinkedTask);
const updatedMember = await User.findOne({ _id: newMember._id });
const updatedMemberTasks = await Tasks.Task.find({ _id: { $in: updatedMember.tasksOrder[`${taskType}s`] } });
const syncedMemberTask = find(updatedMemberTasks, findLinkedTask);
expect(syncedTask.checklist.length).to.equal(0);
expect(syncedMemberTask.checklist.length).to.equal(0);
});
});
it('removes assigned tasks when master task is deleted', async () => {
await guild.syncTask(task, leader);
await guild.removeTask(task);
const updatedLeader = await User.findOne({ _id: leader._id });
const updatedLeadersTasks = await Tasks.Task.find({ userId: leader._id, type: taskType });
const syncedTask = find(updatedLeadersTasks, findLinkedTask);
expect(updatedLeader.tasksOrder[`${taskType}s`]).to.not.include(task._id);
expect(syncedTask).to.not.exist;
});
it('unlinks and deletes group tasks for a user when remove-all is specified', async () => {
await guild.syncTask(task, leader);
await guild.unlinkTask(task, leader, 'remove-all');
const updatedLeader = await User.findOne({ _id: leader._id });
const updatedLeadersTasks = await Tasks.Task.find({ _id: { $in: updatedLeader.tasksOrder[`${taskType}s`] } });
const syncedTask = find(updatedLeadersTasks, findLinkedTask);
expect(task.group.assignedUsers).to.not.contain(leader._id);
expect(syncedTask).to.not.exist;
});
it('unlinks and keeps group tasks for a user when keep-all is specified', async () => {
await guild.syncTask(task, leader);
let updatedLeader = await User.findOne({ _id: leader._id });
let updatedLeadersTasks = await Tasks.Task.find({ _id: { $in: updatedLeader.tasksOrder[`${taskType}s`] } });
const syncedTask = find(updatedLeadersTasks, findLinkedTask);
await guild.unlinkTask(task, leader, 'keep-all');
updatedLeader = await User.findOne({ _id: leader._id });
updatedLeadersTasks = await Tasks.Task.find({ _id: { $in: updatedLeader.tasksOrder[`${taskType}s`] } });
const updatedSyncedTask = find(
updatedLeadersTasks,
updatedLeadersTask => updatedLeadersTask._id === syncedTask._id,
);
expect(task.group.assignedUsers).to.not.contain(leader._id);
expect(updatedSyncedTask).to.exist;
expect(updatedSyncedTask.group._id).to.be.undefined;
});
});
});
});

View File

@@ -246,13 +246,23 @@ describe('Task Model', () => {
expect(foundTasks[0].text).to.eql(taskWithAlias.text);
});
it('scopes alias lookup to user', async () => {
it('scopes alias lookup to user when querying aliases only', async () => {
await Tasks.Task.findMultipleByIdOrAlias([taskWithAlias.alias], user._id);
expect(Tasks.Task.find).to.be.calledOnce;
expect(Tasks.Task.find).to.be.calledWithMatch({
alias: { $in: [taskWithAlias.alias] },
userId: user._id,
});
});
it('scopes alias lookup to user when querying aliases and IDs', async () => {
await Tasks.Task.findMultipleByIdOrAlias([taskWithAlias.alias, secondTask._id], user._id);
expect(Tasks.Task.find).to.be.calledOnce;
expect(Tasks.Task.find).to.be.calledWithMatch({
$or: [
{ _id: { $in: [] } },
{ _id: { $in: [secondTask._id] } },
{ alias: { $in: [taskWithAlias.alias] } },
],
userId: user._id,
@@ -270,10 +280,7 @@ describe('Task Model', () => {
expect(Tasks.Task.find).to.be.calledOnce;
expect(Tasks.Task.find).to.be.calledWithMatch({
$or: [
{ _id: { $in: [] } },
{ alias: { $in: [taskWithAlias.alias] } },
],
alias: { $in: [taskWithAlias.alias] },
userId: user._id,
foo: 'bar',
});

View File

@@ -1,4 +1,3 @@
import { find } from 'lodash';
import {
createAndPopulateGroup,
translate as t,
@@ -11,10 +10,6 @@ describe('POST /group/:groupId/remove-manager', () => {
const groupType = 'guild';
let nonManager;
function findAssignedTask (memberTask) {
return memberTask.group.id === groupToUpdate._id;
}
beforeEach(async () => {
const { group, groupLeader, members } = await createAndPopulateGroup({
groupDetails: {
@@ -63,28 +58,4 @@ describe('POST /group/:groupId/remove-manager', () => {
expect(updatedGroup.managers[nonLeader._id]).to.not.exist;
});
it('removes group approval notifications from a manager that is removed', async () => {
await leader.post(`/groups/${groupToUpdate._id}/add-manager`, {
managerId: nonLeader._id,
});
const task = await leader.post(`/tasks/group/${groupToUpdate._id}`, {
text: 'test todo',
type: 'todo',
requiresApproval: true,
});
await nonLeader.post(`/tasks/${task._id}/assign/${nonManager._id}`);
const memberTasks = await nonManager.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
await nonManager.post(`/tasks/${syncedTask._id}/score/up`);
const updatedGroup = await leader.post(`/groups/${groupToUpdate._id}/remove-manager`, {
managerId: nonLeader._id,
});
await nonLeader.sync();
expect(nonLeader.notifications.length).to.equal(1); // user gets mystery items
expect(updatedGroup.managers[nonLeader._id]).to.not.exist;
});
});

View File

@@ -11,6 +11,12 @@ describe('PUT /group', () => {
const groupName = 'Test Public Guild';
const groupType = 'guild';
const groupUpdatedName = 'Test Public Guild Updated';
const groupCategories = [
{
slug: 'initialCat',
name: 'Initial Category',
},
];
beforeEach(async () => {
const { group, groupLeader, members } = await createAndPopulateGroup({
@@ -18,6 +24,7 @@ describe('PUT /group', () => {
name: groupName,
type: groupType,
privacy: 'public',
categories: groupCategories,
},
members: 1,
});
@@ -61,6 +68,35 @@ describe('PUT /group', () => {
expect(updatedGroup.categories[0].name).to.eql(categories[0].name);
});
it('removes the initial group category', async () => {
const categories = [];
const updatedGroup = await leader.put(`/groups/${groupToUpdate._id}`, {
categories,
});
expect(updatedGroup.categories.length).to.equal(0);
});
it('removes duplicate group categories', async () => {
const categories = [
{
slug: 'newCat',
name: 'New Category',
},
{
slug: 'newCat',
name: 'New Category',
},
];
const updatedGroup = await leader.put(`/groups/${groupToUpdate._id}`, {
categories,
});
expect(updatedGroup.categories.length).to.equal(1);
});
it('allows an admin to update a guild', async () => {
const updatedGroup = await adminUser.put(`/groups/${groupToUpdate._id}`, {
name: groupUpdatedName,

View File

@@ -13,6 +13,9 @@ describe('POST /tasks/clearCompletedTodos', () => {
{ 'purchased.plan.customerId': 'group-unlimited' },
);
const challenge = await generateChallenge(user, guild);
await user.put('/user', {
'preferences.tasks.mirrorGroupTasks': [guild._id],
});
await user.post(`/challenges/${challenge._id}/join`);
const initialTodoCount = user.tasksOrder.todos.length;
@@ -33,7 +36,7 @@ describe('POST /tasks/clearCompletedTodos', () => {
text: 'todo 7',
type: 'todo',
});
await user.post(`/tasks/${groupTask._id}/assign/${user._id}`);
await user.post(`/tasks/${groupTask._id}/assign`, [user._id]);
const tasks = await user.get('/tasks/user?type=todos');
expect(tasks.length).to.equal(initialTodoCount + 7);

View File

@@ -30,7 +30,7 @@ describe('POST /tasks/:taskId/checklist/:itemId/score', () => {
expect(savedTask.checklist[0].completed).to.equal(true);
});
it('can use a alias to score a checklist item', async () => {
it('can use an alias to score a checklist item', async () => {
const task = await user.post('/tasks/user', {
type: 'daily',
text: 'Daily with checklist',

View File

@@ -1,4 +1,3 @@
import { find } from 'lodash';
import {
translate as t,
createAndPopulateGroup,
@@ -8,10 +7,6 @@ describe('Groups DELETE /tasks/:id', () => {
let user; let guild; let member; let member2; let
task;
function findAssignedTask (memberTask) {
return memberTask.group.id === guild._id;
}
beforeEach(async () => {
const { group, members, groupLeader } = await createAndPopulateGroup({
groupDetails: {
@@ -35,8 +30,7 @@ describe('Groups DELETE /tasks/:id', () => {
notes: 1976,
});
await user.post(`/tasks/${task._id}/assign/${member._id}`);
await user.post(`/tasks/${task._id}/assign/${member2._id}`);
await user.post(`/tasks/${task._id}/assign`, [member._id, member2._id]);
});
it('deletes a group task', async () => {
@@ -64,81 +58,4 @@ describe('Groups DELETE /tasks/:id', () => {
message: t('messageTaskNotFound'),
});
});
it('removes deleted taskʾs approval pending notifications from managers', async () => {
await user.post(`/groups/${guild._id}/add-manager`, {
managerId: member2._id,
});
await user.put(`/tasks/${task._id}/`, {
requiresApproval: true,
});
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
await member.post(`/tasks/${syncedTask._id}/score/up`);
await user.sync();
await member2.sync();
expect(user.notifications.length).to.equal(3); // mystery items
expect(user.notifications[2].type).to.equal('GROUP_TASK_APPROVAL');
expect(member2.notifications.length).to.equal(3);
expect(member2.notifications[2].type).to.equal('GROUP_TASK_APPROVAL');
await member2.del(`/tasks/${task._id}`);
await user.sync();
await member2.sync();
expect(user.notifications.length).to.equal(2);
expect(member2.notifications.length).to.equal(2);
});
it('deletes task from assigned user', async () => {
await user.del(`/tasks/${task._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
expect(syncedTask).to.not.exist;
});
it('deletes task from all assigned users', async () => {
await user.del(`/tasks/${task._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
const member2Tasks = await member2.get('/tasks/user');
const member2SyncedTask = find(member2Tasks, findAssignedTask);
expect(syncedTask).to.not.exist;
expect(member2SyncedTask).to.not.exist;
});
it('prevents a user from deleting a task they are assigned to', async () => {
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
await expect(member.del(`/tasks/${syncedTask._id}`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('cantDeleteAssignedGroupTasks'),
});
});
it('allows a user to delete a task after leaving a group', async () => {
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
await member.post(`/groups/${guild._id}/leave`);
await member.del(`/tasks/${syncedTask._id}`);
await expect(member.get(`/tasks/${syncedTask._id}`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: 'Task not found.',
});
});
});

View File

@@ -1,78 +0,0 @@
import { find } from 'lodash';
import {
createAndPopulateGroup,
} from '../../../../../helpers/api-integration/v3';
describe('GET /approvals/group/:groupId', () => {
let user; let guild; let member; let addlMember; let task; let syncedTask; let
addlSyncedTask;
function findAssignedTask (memberTask) {
return memberTask.group.id === guild._id;
}
beforeEach(async () => {
const { group, members, groupLeader } = await createAndPopulateGroup({
groupDetails: {
name: 'Test Guild',
type: 'guild',
},
members: 2,
upgradeToGroupPlan: true,
});
guild = group;
user = groupLeader;
member = members[0]; // eslint-disable-line prefer-destructuring
addlMember = members[1]; // eslint-disable-line prefer-destructuring
task = await user.post(`/tasks/group/${guild._id}`, {
text: 'test todo',
type: 'todo',
requiresApproval: true,
});
await user.post(`/tasks/${task._id}/assign/${member._id}`);
await user.post(`/tasks/${task._id}/assign/${addlMember._id}`);
const memberTasks = await member.get('/tasks/user');
syncedTask = find(memberTasks, findAssignedTask);
const addlMemberTasks = await addlMember.get('/tasks/user');
addlSyncedTask = find(addlMemberTasks, findAssignedTask);
try {
await member.post(`/tasks/${syncedTask._id}/score/up`);
} catch (e) {
// eslint-disable-next-line no-empty
}
try {
await addlMember.post(`/tasks/${addlSyncedTask._id}/score/up`);
} catch (e) {
// eslint-disable-next-line no-empty
}
});
it('provides only user\'s own tasks when user is not the group leader', async () => {
const approvals = await member.get(`/approvals/group/${guild._id}`);
expect(approvals[0]._id).to.equal(syncedTask._id);
expect(approvals[1]).to.not.exist;
});
it('allows group leaders to get a list of tasks that need approval', async () => {
const approvals = await user.get(`/approvals/group/${guild._id}`);
expect(approvals[0]._id).to.equal(syncedTask._id);
expect(approvals[1]._id).to.equal(addlSyncedTask._id);
});
it('allows managers to get a list of tasks that need approval', async () => {
await user.post(`/groups/${guild._id}/add-manager`, {
managerId: member._id,
});
const approvals = await member.get(`/approvals/group/${guild._id}`);
expect(approvals[0]._id).to.equal(syncedTask._id);
expect(approvals[1]._id).to.equal(addlSyncedTask._id);
});
});

View File

@@ -1,261 +0,0 @@
import { find } from 'lodash';
import {
createAndPopulateGroup,
translate as t,
} from '../../../../../helpers/api-integration/v3';
describe('POST /tasks/:id/approve/:userId', () => {
let user; let guild; let member; let member2; let
task;
function findAssignedTask (memberTask) {
return memberTask.group.id === guild._id;
}
beforeEach(async () => {
const { group, members, groupLeader } = await createAndPopulateGroup({
groupDetails: {
name: 'Test Guild',
type: 'guild',
},
members: 2,
upgradeToGroupPlan: true,
});
guild = group;
user = groupLeader;
member = members[0]; // eslint-disable-line prefer-destructuring
member2 = members[1]; // eslint-disable-line prefer-destructuring
task = await user.post(`/tasks/group/${guild._id}`, {
text: 'test todo',
type: 'todo',
requiresApproval: true,
});
});
it('errors when user is not assigned', async () => {
await expect(user.post(`/tasks/${task._id}/approve/${member._id}`))
.to.eventually.be.rejected.and.to.eql({
code: 404,
error: 'NotFound',
message: t('messageTaskNotFound'),
});
});
it('errors when user is not the group leader', async () => {
await user.post(`/tasks/${task._id}/assign/${member._id}`);
await expect(member.post(`/tasks/${task._id}/approve/${member._id}`))
.to.eventually.be.rejected.and.to.eql({
code: 401,
error: 'NotAuthorized',
message: t('onlyGroupLeaderCanEditTasks'),
});
});
it('approves an assigned user', async () => {
await user.post(`/tasks/${task._id}/assign/${member._id}`);
let memberTasks = await member.get('/tasks/user');
let syncedTask = find(memberTasks, findAssignedTask);
await member.post(`/tasks/${syncedTask._id}/score/up`);
await user.post(`/tasks/${task._id}/approve/${member._id}`);
await member.sync();
expect(member.notifications.length).to.equal(3);
expect(member.notifications[2].type).to.equal('GROUP_TASK_APPROVED');
expect(member.notifications[2].data.message).to.equal(t('yourTaskHasBeenApproved', { taskText: task.text }));
memberTasks = await member.get('/tasks/user');
syncedTask = find(memberTasks, findAssignedTask);
expect(syncedTask.group.approval.approved).to.be.true;
expect(syncedTask.group.approval.approvingUser).to.equal(user._id);
expect(syncedTask.group.approval.dateApproved).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type
});
it('allows a manager to approve an assigned user', async () => {
await user.post(`/groups/${guild._id}/add-manager`, {
managerId: member2._id,
});
await member2.post(`/tasks/${task._id}/assign/${member._id}`);
let memberTasks = await member.get('/tasks/user');
let syncedTask = find(memberTasks, findAssignedTask);
await member.post(`/tasks/${syncedTask._id}/score/up`);
await member2.post(`/tasks/${task._id}/approve/${member._id}`);
await member.sync();
expect(member.notifications.length).to.equal(3);
expect(member.notifications[2].type).to.equal('GROUP_TASK_APPROVED');
expect(member.notifications[2].data.message).to.equal(t('yourTaskHasBeenApproved', { taskText: task.text }));
memberTasks = await member.get('/tasks/user');
syncedTask = find(memberTasks, findAssignedTask);
expect(syncedTask.group.approval.approved).to.be.true;
expect(syncedTask.group.approval.approvingUser).to.equal(member2._id);
expect(syncedTask.group.approval.dateApproved).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type
});
it('removes approval pending notifications from managers', async () => {
await user.post(`/groups/${guild._id}/add-manager`, {
managerId: member2._id,
});
await member2.post(`/tasks/${task._id}/assign/${member._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
await member.post(`/tasks/${syncedTask._id}/score/up`);
await user.sync();
await member2.sync();
expect(user.notifications.length).to.equal(3);
expect(user.notifications[2].type).to.equal('GROUP_TASK_APPROVAL');
expect(member2.notifications.length).to.equal(2);
expect(member2.notifications[1].type).to.equal('GROUP_TASK_APPROVAL');
await member2.post(`/tasks/${task._id}/approve/${member._id}`);
await user.sync();
await member2.sync();
expect(user.notifications.length).to.equal(2);
expect(member2.notifications.length).to.equal(1);
});
it('prevents double approval on a task', async () => {
await user.post(`/groups/${guild._id}/add-manager`, {
managerId: member2._id,
});
await member2.post(`/tasks/${task._id}/assign/${member._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
await member.post(`/tasks/${syncedTask._id}/score/up`);
await member2.post(`/tasks/${task._id}/approve/${member._id}`);
await expect(user.post(`/tasks/${task._id}/approve/${member._id}`))
.to.eventually.be.rejected.and.to.eql({
code: 401,
error: 'NotAuthorized',
message: t('canOnlyApproveTaskOnce'),
});
});
it('prevents approving a task if it is not waiting for approval', async () => {
await user.post(`/tasks/${task._id}/assign/${member._id}`);
await expect(user.post(`/tasks/${task._id}/approve/${member._id}`))
.to.eventually.be.rejected.and.to.eql({
code: 401,
error: 'NotAuthorized',
message: t('taskApprovalWasNotRequested'),
});
});
it('completes master task when single-completion task is approved', async () => {
const sharedCompletionTask = await user.post(`/tasks/group/${guild._id}`, {
text: 'shared completion todo',
type: 'todo',
requiresApproval: true,
sharedCompletion: 'singleCompletion',
});
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member._id}`);
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member2._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
await member.post(`/tasks/${syncedTask._id}/score/up`);
await user.post(`/tasks/${sharedCompletionTask._id}/approve/${member._id}`);
const groupTasks = await user.get(`/tasks/group/${guild._id}?type=completedTodos`);
const masterTask = find(groupTasks, groupTask => groupTask._id === sharedCompletionTask._id);
expect(masterTask.completed).to.equal(true);
});
it('deletes other assigned user tasks when single-completion task is approved', async () => {
const sharedCompletionTask = await user.post(`/tasks/group/${guild._id}`, {
text: 'shared completion todo',
type: 'todo',
requiresApproval: true,
sharedCompletion: 'singleCompletion',
});
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member._id}`);
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member2._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
await member.post(`/tasks/${syncedTask._id}/score/up`);
await user.post(`/tasks/${sharedCompletionTask._id}/approve/${member._id}`);
const member2Tasks = await member2.get('/tasks/user');
const syncedTask2 = find(
member2Tasks,
memberTask => memberTask.group.taskId === sharedCompletionTask._id,
);
expect(syncedTask2).to.equal(undefined);
});
it('does not complete master task when not all user tasks are approved if all assigned must complete', async () => {
const sharedCompletionTask = await user.post(`/tasks/group/${guild._id}`, {
text: 'shared completion todo',
type: 'todo',
requiresApproval: true,
sharedCompletion: 'allAssignedCompletion',
});
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member._id}`);
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member2._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
await member.post(`/tasks/${syncedTask._id}/score/up`);
await user.post(`/tasks/${sharedCompletionTask._id}/approve/${member._id}`);
const groupTasks = await user.get(`/tasks/group/${guild._id}`);
const masterTask = find(groupTasks, groupTask => groupTask._id === sharedCompletionTask._id);
expect(masterTask.completed).to.equal(false);
});
it('completes master task when all user tasks are approved if all assigned must complete', async () => {
const sharedCompletionTask = await user.post(`/tasks/group/${guild._id}`, {
text: 'shared completion todo',
type: 'todo',
requiresApproval: true,
sharedCompletion: 'allAssignedCompletion',
});
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member._id}`);
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member2._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
await member.post(`/tasks/${syncedTask._id}/score/up`);
const member2Tasks = await member2.get('/tasks/user');
const member2SyncedTask = find(member2Tasks, findAssignedTask);
await member2.post(`/tasks/${member2SyncedTask._id}/score/up`);
await user.post(`/tasks/${sharedCompletionTask._id}/approve/${member._id}`);
await user.post(`/tasks/${sharedCompletionTask._id}/approve/${member2._id}`);
const groupTasks = await user.get(`/tasks/group/${guild._id}?type=completedTodos`);
const masterTask = find(groupTasks, groupTask => groupTask._id === sharedCompletionTask._id);
expect(masterTask.completed).to.equal(true);
});
});

View File

@@ -1,4 +1,3 @@
import { find } from 'lodash';
import {
createAndPopulateGroup,
translate as t,
@@ -8,10 +7,6 @@ describe('POST /tasks/:id/needs-work/:userId', () => {
let user; let guild; let member; let member2; let
task;
function findAssignedTask (memberTask) {
return memberTask.group.id === guild._id;
}
beforeEach(async () => {
const { group, members, groupLeader } = await createAndPopulateGroup({
groupDetails: {
@@ -37,14 +32,15 @@ describe('POST /tasks/:id/needs-work/:userId', () => {
it('errors when user is not assigned', async () => {
await expect(user.post(`/tasks/${task._id}/needs-work/${member._id}`))
.to.eventually.be.rejected.and.to.eql({
code: 404,
error: 'NotFound',
message: t('messageTaskNotFound'),
code: 400,
error: 'BadRequest',
message: 'Task not completed by this user.',
});
});
it('errors when user is not the group leader', async () => {
await user.post(`/tasks/${task._id}/assign/${member._id}`);
await user.post(`/tasks/${task._id}/assign`, [member._id]);
await member.post(`/tasks/${task._id}/score/up`);
await expect(member.post(`/tasks/${task._id}/needs-work/${member._id}`))
.to.eventually.be.rejected.and.to.eql({
code: 401,
@@ -54,132 +50,64 @@ describe('POST /tasks/:id/needs-work/:userId', () => {
});
it('marks a task as needing more work', async () => {
await member.sync();
const initialNotifications = member.notifications.length;
await user.post(`/tasks/${task._id}/assign/${member._id}`);
let memberTasks = await member.get('/tasks/user');
let syncedTask = find(memberTasks, findAssignedTask);
await user.post(`/tasks/${task._id}/assign`, [member._id]);
// score task to require approval
await member.post(`/tasks/${syncedTask._id}/score/up`);
await member.post(`/tasks/${task._id}/score/up`);
await user.post(`/tasks/${task._id}/needs-work/${member._id}`);
[memberTasks] = await Promise.all([member.get('/tasks/user'), member.sync()]);
syncedTask = find(memberTasks, findAssignedTask);
// Check that the notification approval request has been removed
expect(syncedTask.group.approval.requested).to.equal(false);
expect(syncedTask.group.approval.requestedDate).to.equal(undefined);
// Check that the notification is correct
await member.sync();
expect(member.notifications.length).to.equal(initialNotifications + 3);
const notification = member.notifications[member.notifications.length - 1];
expect(notification.type).to.equal('GROUP_TASK_NEEDS_WORK');
const taskText = syncedTask.text;
const managerName = user.profile.name;
const taskText = task.text;
const managerName = user.auth.local.username;
expect(notification.data.message).to.equal(t('taskNeedsWork', { taskText, managerName }));
expect(notification.data.task.id).to.equal(syncedTask._id);
expect(notification.data.task.id).to.equal(task._id);
expect(notification.data.task.text).to.equal(taskText);
expect(notification.data.group.id).to.equal(syncedTask.group.id);
expect(notification.data.group.id).to.equal(task.group.id);
expect(notification.data.group.name).to.equal(guild.name);
expect(notification.data.manager.id).to.equal(user._id);
expect(notification.data.manager.name).to.equal(managerName);
// Check that the managers' GROUP_TASK_APPROVAL notifications have been removed
await user.sync();
expect(user.notifications.find(n => { // eslint-disable-line arrow-body-style
return n.data.taskId === syncedTask._id && n.type === 'GROUP_TASK_APPROVAL';
})).to.equal(undefined);
});
it('allows a manager to mark a task as needing work', async () => {
await member.sync();
const initialNotifications = member.notifications.length;
await user.post(`/groups/${guild._id}/add-manager`, {
managerId: member2._id,
});
await member2.post(`/tasks/${task._id}/assign/${member._id}`);
let memberTasks = await member.get('/tasks/user');
let syncedTask = find(memberTasks, findAssignedTask);
await member2.post(`/tasks/${task._id}/assign`, [member._id]);
// score task to require approval
await member.post(`/tasks/${syncedTask._id}/score/up`);
const initialNotifications = member.notifications.length;
await member.post(`/tasks/${task._id}/score/up`);
await member2.post(`/tasks/${task._id}/needs-work/${member._id}`);
[memberTasks] = await Promise.all([member.get('/tasks/user'), member.sync()]);
syncedTask = find(memberTasks, findAssignedTask);
// Check that the notification approval request has been removed
expect(syncedTask.group.approval.requested).to.equal(false);
expect(syncedTask.group.approval.requestedDate).to.equal(undefined);
await member.sync();
expect(member.notifications.length).to.equal(initialNotifications + 3);
const notification = member.notifications[member.notifications.length - 1];
expect(notification.type).to.equal('GROUP_TASK_NEEDS_WORK');
const taskText = syncedTask.text;
const managerName = member2.profile.name;
const taskText = task.text;
const managerName = member2.auth.local.username;
expect(notification.data.message).to.equal(t('taskNeedsWork', { taskText, managerName }));
expect(notification.data.task.id).to.equal(syncedTask._id);
expect(notification.data.task.id).to.equal(task._id);
expect(notification.data.task.text).to.equal(taskText);
expect(notification.data.group.id).to.equal(syncedTask.group.id);
expect(notification.data.group.id).to.equal(task.group.id);
expect(notification.data.group.name).to.equal(guild.name);
expect(notification.data.manager.id).to.equal(member2._id);
expect(notification.data.manager.name).to.equal(managerName);
// Check that the managers' GROUP_TASK_APPROVAL notifications have been removed
await Promise.all([user.sync(), member2.sync()]);
expect(user.notifications.find(n => { // eslint-disable-line arrow-body-style
return n.data.taskId === syncedTask._id && n.type === 'GROUP_TASK_APPROVAL';
})).to.equal(undefined);
expect(member2.notifications.find(n => { // eslint-disable-line arrow-body-style
return n.data.taskId === syncedTask._id && n.type === 'GROUP_TASK_APPROVAL';
})).to.equal(undefined);
});
it('prevents marking a task as needing work if it was already approved', async () => {
await user.post(`/groups/${guild._id}/add-manager`, {
managerId: member2._id,
});
await member2.post(`/tasks/${task._id}/assign/${member._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
await member.post(`/tasks/${syncedTask._id}/score/up`);
await member2.post(`/tasks/${task._id}/approve/${member._id}`);
await expect(user.post(`/tasks/${task._id}/needs-work/${member._id}`))
.to.eventually.be.rejected.and.to.eql({
code: 401,
error: 'NotAuthorized',
message: t('canOnlyApproveTaskOnce'),
});
});
it('prevents marking a task as needing work if it is not waiting for approval', async () => {
await user.post(`/tasks/${task._id}/assign/${member._id}`);
await expect(user.post(`/tasks/${task._id}/needs-work/${member._id}`))
.to.eventually.be.rejected.and.to.eql({
code: 401,
error: 'NotAuthorized',
message: t('taskApprovalWasNotRequested'),
});
});
});

View File

@@ -8,10 +8,6 @@ describe('POST /tasks/:id/score/:direction', () => {
let user; let guild; let member; let member2; let
task;
function findAssignedTask (memberTask) {
return memberTask.group.id === guild._id;
}
beforeEach(async () => {
const { group, members, groupLeader } = await createAndPopulateGroup({
groupDetails: {
@@ -30,209 +26,50 @@ describe('POST /tasks/:id/score/:direction', () => {
task = await user.post(`/tasks/group/${guild._id}`, {
text: 'test todo',
type: 'todo',
requiresApproval: true,
});
await user.post(`/tasks/${task._id}/assign/${member._id}`);
await user.post(`/tasks/${task._id}/assign`, [member._id]);
});
it('prevents user from scoring a task that needs to be approved', async () => {
await user.update({
'preferences.language': 'cs',
});
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
const direction = 'up';
const response = await member.post(`/tasks/${syncedTask._id}/score/${direction}`);
expect(response.data.requiresApproval).to.equal(true);
expect(response.message).to.equal(t('taskApprovalHasBeenRequested'));
const updatedTask = await member.get(`/tasks/${syncedTask._id}`);
await user.sync();
expect(user.notifications.length).to.equal(3);
expect(user.notifications[2].type).to.equal('GROUP_TASK_APPROVAL');
expect(user.notifications[2].data.message).to.equal(t('userHasRequestedTaskApproval', {
user: member.auth.local.username,
taskName: updatedTask.text,
taskId: updatedTask._id,
direction,
}, 'cs')); // This test only works if we have the notification translated
expect(user.notifications[2].data.groupId).to.equal(guild._id);
expect(updatedTask.group.approval.requested).to.equal(true);
expect(updatedTask.group.approval.requestedDate).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type
});
it('sends notifications to all managers', async () => {
await user.post(`/groups/${guild._id}/add-manager`, {
managerId: member2._id,
});
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
const direction = 'up';
await member.post(`/tasks/${syncedTask._id}/score/${direction}`);
const updatedTask = await member.get(`/tasks/${syncedTask._id}`);
await user.sync();
await member2.sync();
expect(user.notifications.length).to.equal(3);
expect(user.notifications[2].type).to.equal('GROUP_TASK_APPROVAL');
expect(user.notifications[2].data.message).to.equal(t('userHasRequestedTaskApproval', {
user: member.auth.local.username,
taskName: updatedTask.text,
taskId: updatedTask._id,
direction,
}));
expect(user.notifications[2].data.groupId).to.equal(guild._id);
expect(member2.notifications.length).to.equal(2);
expect(member2.notifications[1].type).to.equal('GROUP_TASK_APPROVAL');
expect(member2.notifications[1].data.message).to.equal(t('userHasRequestedTaskApproval', {
user: member.auth.local.username,
taskName: updatedTask.text,
taskId: updatedTask._id,
direction,
}));
expect(member2.notifications[1].data.groupId).to.equal(guild._id);
});
it('errors when approval has already been requested', async () => {
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
await member.post(`/tasks/${syncedTask._id}/score/up`);
const response = await member.post(`/tasks/${syncedTask._id}/score/up`);
expect(response.data.requiresApproval).to.equal(true);
expect(response.message).to.equal(t('taskRequiresApproval'));
});
it('allows a user to score an approved task', async () => {
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
await member.post(`/tasks/${syncedTask._id}/score/up`);
await user.post(`/tasks/${task._id}/approve/${member._id}`);
await member.post(`/tasks/${syncedTask._id}/score/up`);
const updatedTask = await member.get(`/tasks/${syncedTask._id}`);
expect(updatedTask.completed).to.equal(true);
expect(updatedTask.dateCompleted).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type
});
it('completes master task when single-completion task is completed', async () => {
const sharedCompletionTask = await user.post(`/tasks/group/${guild._id}`, {
text: 'shared completion todo',
type: 'todo',
requiresApproval: false,
sharedCompletion: 'singleCompletion',
});
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(
memberTasks,
memberTask => memberTask.group.taskId === sharedCompletionTask._id,
);
await member.post(`/tasks/${syncedTask._id}/score/up`);
it('completes single-assigned task', async () => {
await member.post(`/tasks/${task._id}/score/up`);
const groupTasks = await user.get(`/tasks/group/${guild._id}?type=completedTodos`);
const masterTask = find(groupTasks, groupTask => groupTask._id === sharedCompletionTask._id);
const sourceTask = find(groupTasks, groupTask => groupTask._id === task._id);
expect(masterTask.completed).to.equal(true);
expect(sourceTask.completed).to.equal(true);
});
it('deletes other assigned user tasks when single-completion task is completed', async () => {
const sharedCompletionTask = await user.post(`/tasks/group/${guild._id}`, {
text: 'shared completion todo',
type: 'todo',
requiresApproval: false,
sharedCompletion: 'singleCompletion',
it('errors when task has already been completed', async () => {
await member.post(`/tasks/${task._id}/score/up`);
await expect(member.post(`/tasks/${task._id}/score/up`)).to.be.rejected.and.to.eventually.eql({
code: 401,
error: 'NotAuthorized',
message: t('sessionOutdated'),
});
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member._id}`);
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member2._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(
memberTasks,
memberTask => memberTask.group.taskId === sharedCompletionTask._id,
);
await member.post(`/tasks/${syncedTask._id}/score/up`);
const member2Tasks = await member2.get('/tasks/user');
const syncedTask2 = find(
member2Tasks,
memberTask => memberTask.group.taskId === sharedCompletionTask._id,
);
expect(syncedTask2).to.equal(undefined);
});
it('does not complete master task when not all user tasks are completed if all assigned must complete', async () => {
const sharedCompletionTask = await user.post(`/tasks/group/${guild._id}`, {
text: 'shared completion todo',
type: 'todo',
requiresApproval: false,
sharedCompletion: 'allAssignedCompletion',
});
it('does not complete multi-assigned task when not all assignees have completed', async () => {
await user.post(`/tasks/${task._id}/assign`, [member2._id]);
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member._id}`);
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member2._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(
memberTasks,
memberTask => memberTask.group.taskId === sharedCompletionTask._id,
);
await member.post(`/tasks/${syncedTask._id}/score/up`);
await member.post(`/tasks/${task._id}/score/up`);
const groupTasks = await user.get(`/tasks/group/${guild._id}`);
const masterTask = find(groupTasks, groupTask => groupTask._id === sharedCompletionTask._id);
const sourceTask = find(groupTasks, groupTask => groupTask._id === task._id);
expect(masterTask.completed).to.equal(false);
expect(sourceTask.completed).to.equal(false);
});
it('completes master task when all user tasks are completed if all assigned must complete', async () => {
const sharedCompletionTask = await user.post(`/tasks/group/${guild._id}`, {
text: 'shared completion todo',
type: 'todo',
requiresApproval: false,
sharedCompletion: 'allAssignedCompletion',
});
it('completes multi-assigned task when all assignees have completed', async () => {
await user.post(`/tasks/${task._id}/assign`, [member2._id]);
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member._id}`);
await user.post(`/tasks/${sharedCompletionTask._id}/assign/${member2._id}`);
const memberTasks = await member.get('/tasks/user');
const member2Tasks = await member2.get('/tasks/user');
const syncedTask = find(
memberTasks,
memberTask => memberTask.group.taskId === sharedCompletionTask._id,
);
const syncedTask2 = find(
member2Tasks,
memberTask => memberTask.group.taskId === sharedCompletionTask._id,
);
await member.post(`/tasks/${syncedTask._id}/score/up`);
await member2.post(`/tasks/${syncedTask2._id}/score/up`);
await member.post(`/tasks/${task._id}/score/up`);
await member2.post(`/tasks/${task._id}/score/up`);
const groupTasks = await user.get(`/tasks/group/${guild._id}?type=completedTodos`);
const masterTask = find(groupTasks, groupTask => groupTask._id === sharedCompletionTask._id);
const sourceTask = find(groupTasks, groupTask => groupTask._id === task._id);
expect(masterTask.completed).to.equal(true);
expect(sourceTask.completed).to.equal(true);
});
});

View File

@@ -39,7 +39,7 @@ describe('POST /tasks/:taskId/assign/:memberId', () => {
});
it('returns error when task is not found', async () => {
await expect(user.post(`/tasks/${generateUUID()}/assign/${member._id}`))
await expect(user.post(`/tasks/${generateUUID()}/assign`, [member._id]))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
@@ -56,7 +56,7 @@ describe('POST /tasks/:taskId/assign/:memberId', () => {
notes: 1976,
});
await expect(user.post(`/tasks/${nonGroupTask._id}/assign/${member._id}`))
await expect(user.post(`/tasks/${nonGroupTask._id}/assign`, [member._id]))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
@@ -67,7 +67,7 @@ describe('POST /tasks/:taskId/assign/:memberId', () => {
it('returns error when user is not a member of the group', async () => {
const nonUser = await generateUser();
await expect(nonUser.post(`/tasks/${task._id}/assign/${member._id}`))
await expect(nonUser.post(`/tasks/${task._id}/assign`, [member._id]))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
@@ -76,7 +76,7 @@ describe('POST /tasks/:taskId/assign/:memberId', () => {
});
it('returns error when non leader tries to create a task', async () => {
await expect(member2.post(`/tasks/${task._id}/assign/${member._id}`))
await expect(member2.post(`/tasks/${task._id}/assign`, [member._id]))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
@@ -84,49 +84,23 @@ describe('POST /tasks/:taskId/assign/:memberId', () => {
});
});
it('allows user to assign themselves (claim)', async () => {
await member.post(`/tasks/${task._id}/assign/${member._id}`);
const groupTask = await user.get(`/tasks/group/${guild._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
expect(groupTask[0].group.assignedUsers).to.contain(member._id);
expect(syncedTask).to.exist;
});
it('sends notifications to group leader and managers when a task is claimed', async () => {
await user.post(`/groups/${guild._id}/add-manager`, {
managerId: member2._id,
});
await member.post(`/tasks/${task._id}/assign/${member._id}`);
await user.sync();
await member2.sync();
const groupTask = await user.get(`/tasks/group/${guild._id}`);
expect(user.notifications.length).to.equal(3); // includes Guild Joined achievement
expect(user.notifications[2].type).to.equal('GROUP_TASK_CLAIMED');
expect(user.notifications[2].data.taskId).to.equal(groupTask[0]._id);
expect(user.notifications[2].data.groupId).to.equal(guild._id);
expect(member2.notifications.length).to.equal(2);
expect(member2.notifications[1].type).to.equal('GROUP_TASK_CLAIMED');
expect(member2.notifications[1].data.taskId).to.equal(groupTask[0]._id);
expect(member2.notifications[1].data.groupId).to.equal(guild._id);
});
it('assigns a task to a user', async () => {
await user.post(`/tasks/${task._id}/assign/${member._id}`);
await user.post(`/tasks/${task._id}/assign`, [member._id]);
const groupTask = await user.get(`/tasks/group/${guild._id}`);
await member.put('/user', {
'preferences.tasks.mirrorGroupTasks': [guild._id],
});
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
expect(groupTask[0].group.assignedUsers).to.contain(member._id);
expect(groupTask[0].group.assignedUsersDetail[member._id]).to.exist;
expect(syncedTask).to.exist;
});
it('sends a notification to assigned user', async () => {
await user.post(`/tasks/${task._id}/assign/${member._id}`);
await user.post(`/tasks/${task._id}/assign`, [member._id]);
await member.sync();
const groupTask = await user.get(`/tasks/group/${guild._id}`);
@@ -137,20 +111,27 @@ describe('POST /tasks/:taskId/assign/:memberId', () => {
});
it('assigns a task to multiple users', async () => {
await user.post(`/tasks/${task._id}/assign/${member._id}`);
await user.post(`/tasks/${task._id}/assign/${member2._id}`);
await user.post(`/tasks/${task._id}/assign`, [member._id, member2._id]);
const groupTask = await user.get(`/tasks/group/${guild._id}`);
await member.put('/user', {
'preferences.tasks.mirrorGroupTasks': [guild._id],
});
const memberTasks = await member.get('/tasks/user');
const member1SyncedTask = find(memberTasks, findAssignedTask);
await member2.put('/user', {
'preferences.tasks.mirrorGroupTasks': [guild._id],
});
const member2Tasks = await member2.get('/tasks/user');
const member2SyncedTask = find(member2Tasks, findAssignedTask);
expect(groupTask[0].group.assignedUsers).to.contain(member._id);
expect(groupTask[0].group.assignedUsers).to.contain(member2._id);
expect(groupTask[0].group.assignedUsersDetail[member._id]).to.exist;
expect(member1SyncedTask).to.exist;
expect(groupTask[0].group.assignedUsers).to.contain(member2._id);
expect(groupTask[0].group.assignedUsersDetail[member2._id]).to.exist;
expect(member2SyncedTask).to.exist;
});
@@ -159,13 +140,17 @@ describe('POST /tasks/:taskId/assign/:memberId', () => {
managerId: member2._id,
});
await member2.post(`/tasks/${task._id}/assign/${member._id}`);
await member2.post(`/tasks/${task._id}/assign`, [member._id]);
const groupTask = await member2.get(`/tasks/group/${guild._id}`);
await member.put('/user', {
'preferences.tasks.mirrorGroupTasks': [guild._id],
});
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
expect(groupTask[0].group.assignedUsers).to.contain(member._id);
expect(groupTask[0].group.assignedUsersDetail[member._id]).to.exist;
expect(syncedTask).to.exist;
});
});

View File

@@ -37,7 +37,7 @@ describe('POST /tasks/:taskId/unassign/:memberId', () => {
notes: 1976,
});
await user.post(`/tasks/${task._id}/assign/${member._id}`);
await user.post(`/tasks/${task._id}/assign`, [member._id]);
});
it('returns error when task is not found', async () => {
@@ -96,7 +96,7 @@ describe('POST /tasks/:taskId/unassign/:memberId', () => {
});
it('unassigns a user and only that user from a task', async () => {
await user.post(`/tasks/${task._id}/assign/${member2._id}`);
await user.post(`/tasks/${task._id}/assign`, [member2._id]);
await user.post(`/tasks/${task._id}/unassign/${member._id}`);
@@ -105,6 +105,9 @@ describe('POST /tasks/:taskId/unassign/:memberId', () => {
const memberTasks = await member.get('/tasks/user');
const member1SyncedTask = find(memberTasks, findAssignedTask);
await member2.put('/user', {
'preferences.tasks.mirrorGroupTasks': [guild._id],
});
const member2Tasks = await member2.get('/tasks/user');
const member2SyncedTask = find(member2Tasks, findAssignedTask);
@@ -130,20 +133,7 @@ describe('POST /tasks/:taskId/unassign/:memberId', () => {
expect(syncedTask).to.not.exist;
});
it('allows a user to unassign themselves', async () => {
await member.post(`/tasks/${task._id}/unassign/${member._id}`);
const groupTask = await user.get(`/tasks/group/${guild._id}`);
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
expect(groupTask[0].group.assignedUsers).to.not.contain(member._id);
expect(syncedTask).to.not.exist;
});
// @TODO: Which do we want? The user to unassign themselves or not. This test was in
// here, but then we had a request to allow to unaissgn.
xit('returns error when non leader tries to unassign their a task', async () => {
it('returns error when non leader tries to unassign a task', async () => {
await expect(member.post(`/tasks/${task._id}/unassign/${member._id}`))
.to.eventually.be.rejected.and.eql({
code: 401,

View File

@@ -1,4 +1,3 @@
import { find } from 'lodash';
import {
createAndPopulateGroup, translate as t,
} from '../../../../../helpers/api-integration/v3';
@@ -11,10 +10,6 @@ describe('PUT /tasks/:id', () => {
let habit;
let todo;
function findAssignedTask (memberTask) {
return memberTask.group.id === guild._id;
}
beforeEach(async () => {
const { group, members, groupLeader } = await createAndPopulateGroup({
groupDetails: {
@@ -44,8 +39,7 @@ describe('PUT /tasks/:id', () => {
notes: 1976,
});
await user.post(`/tasks/${habit._id}/assign/${member._id}`);
await user.post(`/tasks/${habit._id}/assign/${member2._id}`);
await user.post(`/tasks/${habit._id}/assign`, [member._id, member2._id]);
});
it('updates a group task', async () => {
@@ -56,28 +50,6 @@ describe('PUT /tasks/:id', () => {
expect(savedHabit.notes).to.eql('some new notes');
});
it('updates a group task - approval is required', async () => {
// allow to manage
await user.post(`/groups/${guild._id}/add-manager`, {
managerId: member._id,
});
// change the habit
habit = await member.put(`/tasks/${habit._id}`, {
text: 'new text!',
requiresApproval: true,
});
const memberTasks = await member2.get('/tasks/user');
const syncedTask = find(memberTasks, memberTask => memberTask.group.taskId === habit._id);
// score up to trigger approval
const response = await member2.post(`/tasks/${syncedTask._id}/score/up`);
expect(response.data.requiresApproval).to.equal(true);
expect(response.message).to.equal(t('taskApprovalHasBeenRequested'));
});
it('member updates a group task value - not allowed', async () => {
// change the todo
await expect(member.put(`/tasks/${habit._id}`, {
@@ -120,7 +92,7 @@ describe('PUT /tasks/:id', () => {
],
});
await user.post(`/tasks/${habit._id}/assign/${member._id}`);
await user.post(`/tasks/${habit._id}/assign`, [member._id]);
// change the checklist text
habit = await user.put(`/tasks/${habit._id}`, {
@@ -137,63 +109,4 @@ describe('PUT /tasks/:id', () => {
expect(habit.checklist.length).to.eql(2);
});
it('updates the linked tasks', async () => {
await user.put(`/tasks/${habit._id}`, {
text: 'some new text',
up: false,
down: false,
notes: 'some new notes',
});
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
expect(syncedTask.text).to.eql('some new text');
expect(syncedTask.up).to.eql(false);
expect(syncedTask.down).to.eql(false);
});
it('updates the linked tasks for all assigned users', async () => {
await user.put(`/tasks/${habit._id}`, {
text: 'some new text',
up: false,
down: false,
notes: 'some new notes',
});
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
const member2Tasks = await member2.get('/tasks/user');
const member2SyncedTask = find(member2Tasks, findAssignedTask);
expect(syncedTask.text).to.eql('some new text');
expect(syncedTask.up).to.eql(false);
expect(syncedTask.down).to.eql(false);
expect(member2SyncedTask.text).to.eql('some new text');
expect(member2SyncedTask.up).to.eql(false);
expect(member2SyncedTask.down).to.eql(false);
});
it('updates the linked tasks', async () => {
await user.post(`/groups/${guild._id}/add-manager`, {
managerId: member2._id,
});
await member2.put(`/tasks/${habit._id}`, {
text: 'some new text',
up: false,
down: false,
notes: 'some new notes',
});
const memberTasks = await member.get('/tasks/user');
const syncedTask = find(memberTasks, findAssignedTask);
expect(syncedTask.text).to.eql('some new text');
expect(syncedTask.up).to.eql(false);
expect(syncedTask.down).to.eql(false);
});
});

View File

@@ -289,45 +289,6 @@ describe('DELETE /user', () => {
});
});
context('user with Facebook auth', async () => {
beforeEach(async () => {
user = await generateUser({
auth: {
facebook: {
id: 'facebook-id',
},
},
});
});
it('returns an error if confirmation phrase is wrong', async () => {
await expect(user.del('/user', {
password: 'just-do-it',
})).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('incorrectDeletePhrase', { magicWord: 'DELETE' }),
});
});
it('returns an error if confirmation phrase is not supplied', async () => {
await expect(user.del('/user', {
password: '',
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('missingPassword'),
});
});
it('deletes a Facebook user', async () => {
await user.del('/user', {
password: DELETE_CONFIRMATION,
});
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
});
});
context('user with Google auth', async () => {
beforeEach(async () => {
user = await generateUser({

View File

@@ -145,19 +145,16 @@ describe('POST /user/class/cast/:spellId', () => {
text: 'todo group',
type: 'todo',
});
await groupLeader.post(`/tasks/${groupTask._id}/assign/${groupLeader._id}`);
const memberTasks = await groupLeader.get('/tasks/user');
const syncedGroupTask = find(memberTasks, memberTask => memberTask.group.id === group._id);
await groupLeader.post(`/tasks/${groupTask._id}/assign`, [groupLeader._id]);
await groupLeader.update({ 'stats.class': 'rogue', 'stats.lvl': 11 });
await sleep(0.5);
await groupLeader.sync();
await expect(groupLeader.post(`/user/class/cast/pickPocket?targetId=${syncedGroupTask._id}`))
await expect(groupLeader.post(`/user/class/cast/pickPocket?targetId=${groupTask._id}`))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('groupTasksNoCast'),
code: 404,
error: 'NotFound',
message: t('messageTaskNotFound'),
});
});
@@ -279,7 +276,10 @@ describe('POST /user/class/cast/:spellId', () => {
type: 'todo',
});
await user.update({ 'stats.class': 'healer', 'stats.mp': 200, 'stats.lvl': 15 });
await user.post(`/tasks/${groupTask._id}/assign/${user._id}`);
await user.post(`/tasks/${groupTask._id}/assign`, [user._id]);
await user.put('/user', {
'preferences.tasks.mirrorGroupTasks': [guild._id],
});
await user.post('/user/class/cast/brightness');
await user.sync();

View File

@@ -100,11 +100,14 @@ describe('POST /user/reset', () => {
text: 'todo group',
type: 'todo',
});
await user.post(`/tasks/${groupTask._id}/assign/${user._id}`);
await user.post(`/tasks/${groupTask._id}/assign`, [user._id]);
await user.post('/user/reset');
await user.sync();
await user.put('/user', {
'preferences.tasks.mirrorGroupTasks': [guild._id],
});
const memberTasks = await user.get('/tasks/user');
const syncedGroupTask = find(memberTasks, memberTask => memberTask.group.id === guild._id);

View File

@@ -35,7 +35,7 @@ describe('PUT /user', () => {
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: 'mustBeArray',
message: 'Tag list must be an array.',
});
});

View File

@@ -20,44 +20,6 @@ describe('DELETE social registration', () => {
});
});
context('Facebook', () => {
it('fails if user does not have an alternative registration method', async () => {
await user.update({
'auth.facebook.id': 'some-fb-id',
'auth.local': { ok: true },
});
await expect(user.del('/user/auth/social/facebook')).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('cantDetachSocial'),
});
});
it('succeeds if user has a local registration', async () => {
await user.update({
'auth.facebook.id': 'some-fb-id',
});
const response = await user.del('/user/auth/social/facebook');
expect(response).to.eql({});
await user.sync();
expect(user.auth.facebook).to.be.undefined;
});
it('succeeds if user has a google registration', async () => {
await user.update({
'auth.facebook.id': 'some-fb-id',
'auth.google.id': 'some-google-id',
'auth.local': { ok: true },
});
const response = await user.del('/user/auth/social/facebook');
expect(response).to.eql({});
await user.sync();
expect(user.auth.facebook).to.be.undefined;
});
});
context('Google', () => {
it('fails if user does not have an alternative registration method', async () => {
await user.update({
@@ -81,19 +43,6 @@ describe('DELETE social registration', () => {
await user.sync();
expect(user.auth.google).to.be.undefined;
});
it('succeeds if user has a facebook registration', async () => {
await user.update({
'auth.google.id': 'some-google-id',
'auth.facebook.id': 'some-facebook-id',
'auth.local': { ok: true },
});
const response = await user.del('/user/auth/social/google');
expect(response).to.eql({});
await user.sync();
expect(user.auth.goodl).to.be.undefined;
});
});
context('Apple', () => {
@@ -119,18 +68,5 @@ describe('DELETE social registration', () => {
await user.sync();
expect(user.auth.apple).to.be.undefined;
});
it('succeeds if user has a facebook registration', async () => {
await user.update({
'auth.apple.id': 'some-apple-id',
'auth.facebook.id': 'some-facebook-id',
'auth.local': { ok: true },
});
const response = await user.del('/user/auth/social/apple');
expect(response).to.eql({});
await user.sync();
expect(user.auth.goodl).to.be.undefined;
});
});
});

View File

@@ -344,6 +344,24 @@ describe('POST /user/auth/local/register', () => {
});
});
it('enforces maximum length for the password', async () => {
const username = generateRandomUserName();
const email = `${username}@example.com`;
const password = '12345678910111213141516171819202122232425262728293031323334353637383940';
const confirmPassword = '12345678910111213141516171819202122232425262728293031323334353637383940';
await expect(api.post('/user/auth/local/register', {
username,
email,
password,
confirmPassword,
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('invalidReqParams'),
});
});
it('requires a username', async () => {
const email = `${generateRandomUserName()}@example.com`;
const password = 'password';

View File

@@ -12,7 +12,6 @@ describe('POST /user/auth/social', () => {
let user;
const endpoint = '/user/auth/social';
let randomAccessToken = '123456';
let randomFacebookId = 'facebookId';
let randomGoogleId = 'googleId';
let network = 'NoNetwork';
@@ -33,146 +32,6 @@ describe('POST /user/auth/social', () => {
});
});
describe('facebook', () => {
beforeEach(async () => {
randomFacebookId = generateUUID();
const expectedResult = {
id: randomFacebookId,
displayName: 'a facebook user',
emails: [
{ value: `${user.auth.local.username}+facebook@example.com` },
],
};
sandbox.stub(passport._strategies.facebook, 'userProfile').yields(null, expectedResult);
network = 'facebook';
});
afterEach(async () => {
passport._strategies.facebook.userProfile.restore();
});
it('registers a new user', async () => {
const response = await api.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
});
expect(response.apiToken).to.exist;
expect(response.id).to.exist;
expect(response.newUser).to.be.true;
expect(response.username).to.exist;
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a facebook user');
await expect(getProperty('users', response.id, 'auth.local.lowerCaseUsername')).to.exist;
await expect(getProperty('users', response.id, 'auth.local.email')).to.eventually.equal(`${user.auth.local.username}+facebook@example.com`);
await expect(getProperty('users', response.id, 'auth.facebook.id')).to.eventually.equal(randomFacebookId);
});
it('logs an existing user in', async () => {
const registerResponse = await api.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
});
const response = await api.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
});
expect(response.apiToken).to.eql(registerResponse.apiToken);
expect(response.id).to.eql(registerResponse.id);
expect(response.newUser).to.be.false;
expect(registerResponse.newUser).to.be.true;
});
it('logs an existing user in if they have local auth with matching email', async () => {
passport._strategies.facebook.userProfile.restore();
const expectedResult = {
id: randomFacebookId,
displayName: 'a facebook user',
emails: [
{ value: user.auth.local.email },
],
};
sandbox.stub(passport._strategies.facebook, 'userProfile').yields(null, expectedResult);
const response = await api.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
});
expect(response.apiToken).to.eql(user.apiToken);
expect(response.id).to.eql(user._id);
expect(response.newUser).to.be.false;
});
it('logs an existing user into their social account if they have local auth with matching email', async () => {
const registerResponse = await api.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
});
expect(registerResponse.newUser).to.be.true;
// This is important for existing accounts before the new social handling
passport._strategies.facebook.userProfile.restore();
const expectedResult = {
id: randomFacebookId,
displayName: 'a facebook user',
emails: [
{ value: user.auth.local.email },
],
};
sandbox.stub(passport._strategies.facebook, 'userProfile').yields(null, expectedResult);
const response = await api.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
});
expect(response.apiToken).to.eql(registerResponse.apiToken);
expect(response.id).to.eql(registerResponse.id);
expect(response.apiToken).not.to.eql(user.apiToken);
expect(response.id).not.to.eql(user._id);
expect(response.newUser).to.be.false;
});
it('add social auth to an existing user', async () => {
const response = await user.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
});
expect(response.apiToken).to.eql(user.apiToken);
expect(response.id).to.eql(user._id);
expect(response.newUser).to.be.false;
});
it('does not log into other account if social auth already exists', async () => {
const registerResponse = await api.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
});
expect(registerResponse.newUser).to.be.true;
await expect(user.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
})).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('socialAlreadyExists'),
});
});
xit('enrolls a new user in an A/B test', async () => {
await api.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
});
await expect(getProperty('users', user._id, '_ABtests')).to.eventually.be.a('object');
});
});
describe('google', () => {
beforeEach(async () => {
randomGoogleId = generateUUID();

View File

@@ -25,6 +25,19 @@ describe('POST /user/reset-password', async () => {
expect(user.auth.local.hashed_password).to.not.eql(previousPassword);
});
it('resets password for social users', async () => {
const email = `${user.auth.local.username}+google@example.com`;
await user.update({ 'auth.google.emails': [{ value: email }] });
await user.sync();
const previousPassword = user.auth.local.passwordResetCode;
const response = await user.post(endpoint, {
email,
});
expect(response).to.eql({ data: {}, message: t('passwordReset') });
await user.sync();
expect(user.auth.local.passwordResetCode).to.not.eql(previousPassword);
});
it('same message on error as on success', async () => {
const response = await user.post(endpoint, {
email: 'nonExistent@email.com',

View File

@@ -130,19 +130,16 @@ describe('POST /user/class/cast/:spellId', () => {
text: 'todo group',
type: 'todo',
});
await groupLeader.post(`/tasks/${groupTask._id}/assign/${groupLeader._id}`);
const memberTasks = await groupLeader.get('/tasks/user');
const syncedGroupTask = find(memberTasks, memberTask => memberTask.group.id === group._id);
await groupLeader.post(`/tasks/${groupTask._id}/assign`, [groupLeader._id]);
await groupLeader.update({ 'stats.class': 'rogue', 'stats.lvl': 11 });
await sleep(0.5);
await groupLeader.sync();
await expect(groupLeader.post(`/user/class/cast/pickPocket?targetId=${syncedGroupTask._id}`))
await expect(groupLeader.post(`/user/class/cast/pickPocket?targetId=${groupTask._id}`))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('groupTasksNoCast'),
code: 404,
error: 'NotFound',
message: t('messageTaskNotFound'),
});
});
@@ -247,7 +244,10 @@ describe('POST /user/class/cast/:spellId', () => {
type: 'todo',
});
await user.update({ 'stats.class': 'healer', 'stats.mp': 200, 'stats.lvl': 15 });
await user.post(`/tasks/${groupTask._id}/assign/${user._id}`);
await user.post(`/tasks/${groupTask._id}/assign`, [user._id]);
await user.put('/user', {
'preferences.tasks.mirrorGroupTasks': [guild._id],
});
await user.post('/user/class/cast/brightness');
await user.sync();

View File

@@ -100,11 +100,14 @@ describe('POST /user/reset', () => {
text: 'todo group',
type: 'todo',
});
await user.post(`/tasks/${groupTask._id}/assign/${user._id}`);
await user.post(`/tasks/${groupTask._id}/assign`, [user._id]);
await user.post('/user/reset');
await user.sync();
await user.put('/user', {
'preferences.tasks.mirrorGroupTasks': [guild._id],
});
const memberTasks = await user.get('/tasks/user');
const syncedGroupTask = find(memberTasks, memberTask => memberTask.group.id === guild._id);

View File

@@ -34,7 +34,7 @@ describe('PUT /user', () => {
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: 'mustBeArray',
message: 'Tag list must be an array.',
});
});

View File

@@ -213,7 +213,7 @@ describe('cron utility functions', () => {
};
}
it('offset 0, next date in 3 months', () => {
it('monthly plan, next date in 3 months', () => {
const user = baseUserData(60, 0, 'group_plan_auto');
const planContext = getPlanContext(user, now);
@@ -222,8 +222,8 @@ describe('cron utility functions', () => {
.to.be.sameMoment('2022-08-10T02:00:00.144Z');
});
it('offset 1, next date in 1 months', () => {
const user = baseUserData(60, 1, 'group_plan_auto');
it('monthly plan, next date in 1 month', () => {
const user = baseUserData(62, 0, 'group_plan_auto');
const planContext = getPlanContext(user, now);
@@ -231,8 +231,17 @@ describe('cron utility functions', () => {
.to.be.sameMoment('2022-06-10T02:00:00.144Z');
});
it('offset 2, next date in 2 months - with any plan', () => {
const user = baseUserData(60, 2, 'basic_3mo');
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);

View File

@@ -249,18 +249,6 @@ describe('shared.ops.scoreTask', () => {
expect(ref.afterUser._tmp.quest.progressDelta).to.eql(secondTaskDelta);
});
it('does not modify stats when task need approval', () => {
todo.group.approval.required = true;
options = {
user: ref.afterUser, task: todo, direction: 'up', times: 5, cron: false,
};
scoreTask(options);
expect(ref.afterUser.stats.hp).to.eql(50);
expect(ref.afterUser.stats.exp).to.equal(ref.beforeUser.stats.exp);
expect(ref.afterUser.stats.gp).to.equal(ref.beforeUser.stats.gp);
});
context('habits', () => {
it('up', () => {
options = {

File diff suppressed because it is too large Load Diff

View File

@@ -21,19 +21,19 @@
"@storybook/vue": "6.3.13",
"@vue/cli-plugin-babel": "^4.5.15",
"@vue/cli-plugin-eslint": "^4.5.19",
"@vue/cli-plugin-router": "^4.5.15",
"@vue/cli-plugin-router": "^5.0.8",
"@vue/cli-plugin-unit-mocha": "^4.5.15",
"@vue/cli-service": "^4.5.15",
"@vue/test-utils": "1.0.0-beta.29",
"amplitude-js": "^8.18.5",
"axios": "^0.25.0",
"amplitude-js": "^8.21.1",
"axios": "^0.27.2",
"axios-progress-bar": "^1.2.0",
"babel-eslint": "^10.1.0",
"bootstrap": "^4.6.0",
"bootstrap-vue": "^2.22.0",
"chai": "^4.3.6",
"core-js": "^3.23.5",
"dompurify": "^2.3.8",
"core-js": "^3.26.0",
"dompurify": "^2.4.1",
"eslint": "^6.8.0",
"eslint-config-habitrpg": "^6.2.0",
"eslint-plugin-mocha": "^5.3.0",
@@ -41,30 +41,31 @@
"habitica-markdown": "^3.0.0",
"hellojs": "^1.19.5",
"inspectpack": "^4.7.1",
"intro.js": "^5.1.0",
"jquery": "^3.6.0",
"intro.js": "^6.0.0",
"jquery": "^3.6.1",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"nconf": "^0.12.0",
"sass": "^1.34.0",
"sass-loader": "^8.0.2",
"smartbanner.js": "^1.19.0",
"smartbanner.js": "^1.19.1",
"stopword": "^2.0.5",
"svg-inline-loader": "^0.8.2",
"svg-url-loader": "^7.1.1",
"svgo": "^1.3.2",
"svgo-loader": "^2.2.1",
"uuid": "^8.3.2",
"validator": "^13.7.0",
"vue": "^2.6.14",
"vue": "^2.7.10",
"vue-cli-plugin-storybook": "2.1.0",
"vue-mugen-scroll": "^0.2.6",
"vue-router": "^3.5.4",
"vue-template-compiler": "^2.6.14",
"vue-router": "^3.6.5",
"vue-template-compiler": "^2.7.10",
"vuedraggable": "^2.24.3",
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#153d339e4dbebb73733658aeda1d5b7fcc55b0a0",
"webpack": "^4.46.0"
},
"devDependencies": {
"@babel/plugin-proposal-optional-chaining": "^7.18.6"
"@babel/plugin-proposal-optional-chaining": "^7.18.9"
}
}

View File

@@ -7,7 +7,7 @@ if (process.env.NODE_ENV === 'production') {
stdio: 'inherit',
});
execSync('npm run storybook:build', {
/* execSync('npm run storybook:build', {
stdio: 'inherit',
});
}); */
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 979 B

View File

@@ -1,9 +1,56 @@
.create-task-area {
position: absolute;
right: 24px;
right: 12px;
top: -24px;
z-index: 998;
align-items: center;
.dropdown {
width: 140px;
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);
background-color: $white;
margin-top: 2px;
position: absolute;
right: 0px;
.task-icon {
width: 30px;
}
.task-label {
margin-top: 1px;
}
.dropdown-item:hover {
.svg-icon {
color: $purple-300;
}
}
.svg-icon {
color: $gray-200;
&.icon-habit {
width: 30px;
height: 20px;
}
&.icon-daily {
width: 24px;
height: 20px;
}
&.icon-todo {
width: 20px;
height: 20px;
}
&.icon-reward {
width: 26px;
height: 20px;
}
}
}
}
.slide-tasks-btns-leave-active, .slide-tasks-btns-enter-active {
@@ -17,70 +64,14 @@
overflow-x: hidden;
}
.diamond-btn {
margin-left: 24px;
background: $white;
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 4px;
box-shadow: 0 2px 2px 0 rgba($black, 0.16), 0 1px 4px 0 rgba($black, 0.12);
cursor: pointer;
color: $gray-200;
transform: rotate(45deg);
&:hover:not(.create-btn) {
color: $purple-400;
box-shadow: 0 1px 8px 0 rgba($black, 0.12), 0 4px 4px 0 rgba($black, 0.16);
}
.svg-icon {
width: 20px;
height: 20px;
transform: rotate(-45deg);
&.icon-habit {
width: 24px;
height: 16px;
}
&.icon-daily {
width: 21.6px;
height: 18px;
}
&.icon-todo {
width: 18px;
height: 18px;
}
&.icon-reward {
width: 23.4px;
height: 18px;
}
}
}
.create-btn {
color: $white;
background-color: $green-100;
height: 48px;
width: 48px;
background-color: $purple-200;
height: 32px;
.svg-icon {
width: 16px;
height: 16px;
transform: rotate(-45deg);
transition: transform 0.3s cubic-bezier(0, 1, 0.5, 1);
}
&.open {
background: $gray-200 !important;
.svg-icon {
transform: rotate(-90deg);
}
color: $purple-500;
width: 10px;
height: 10px;
}
}

View File

@@ -180,8 +180,8 @@ input, textarea, input.form-control, textarea.form-control {
}
// used in checkboxes and radios
$bg-focused-active-control: #4f2993;
$bg-disabled-control: #34303a;
$bg-focused-active-control: $purple-200;
$bg-disabled-control: $gray-10;
// custom control
.custom-control {
@@ -231,17 +231,21 @@ $bg-disabled-control: #34303a;
background-color: inherit;
}
&:focus:not(:checked):not(:disabled)~.custom-control-label::before, &:active:not(:checked):not(:disabled)~.custom-control-label::before {
box-shadow: 0 0 0 6px rgba($bg-focused-active-control, 0.1);
&:focus:not(:checked):not(:disabled)~.custom-control-label::before,
&:active:not(:checked):not(:disabled)~.custom-control-label::before {
border: 2px solid $gray-300;
box-shadow: 0 0 0 2px rgba(146, 92, 243, 0.5);
}
&:focus:checked:not(:disabled)~.custom-control-label::before, &:active:checked:not(:disabled)~.custom-control-label::before {
box-shadow: 0 0 0 6px rgba($bg-focused-active-control, 0.1);
border-color: $purple-400;
&:focus:checked:not(:disabled)~.custom-control-label::before,
&:active:checked:not(:disabled)~.custom-control-label::before {
box-shadow: 0 0 0 2px rgba(146, 92, 243, 0.5);
border-color: 2 px solid $purple-400;
background-color: $purple-400;
}
&:focus:disabled~.custom-control-label::before, &:active:disabled~.custom-control-label::before {
&:focus:disabled~.custom-control-label::before,
&:active:disabled~.custom-control-label::before {
box-shadow: 0 0 0 6px rgba($bg-disabled-control, 0.1);
}

View File

@@ -30,7 +30,7 @@
&-bg {
background: $maroon-100 !important;
.habit-control:not(.task-not-scoreable):hover { background: rgba($black, 0.5) !important; }
.daily-todo-control:hover { background: rgba($white, 0.75) !important; }
.daily-todo-control:not(.task-not-scoreable):hover { background: rgba($white, 0.75) !important; }
}
&-bg-noninteractive { background: $maroon-100 !important; }
&-inner-habit { background: rgba($black, 0.25) !important; }
@@ -41,7 +41,7 @@
&-modal {
&-bg { background: $maroon-100 !important; }
&-headings { color: $white; }
&-headings { color: $white !important; }
&-icon { color: $maroon-100 !important; }
&-text { color: $red-1 !important; }
&-content {
@@ -61,7 +61,7 @@
&-bg {
background: $red-100 !important;
.habit-control:not(.task-not-scoreable):hover { background: rgba($black, 0.5) !important; }
.daily-todo-control:hover { background: rgba($white, 0.75) !important; }
.daily-todo-control:not(.task-not-scoreable):hover { background: rgba($white, 0.75) !important; }
}
&-bg-noninteractive { background: $red-100 !important; }
&-inner-habit { background: rgba($black, 0.25) !important; }
@@ -92,7 +92,7 @@
&-bg {
background: $orange-100 !important;
.habit-control:not(.task-not-scoreable):hover { background: rgba($orange-1, 0.5) !important; }
.daily-todo-control:hover { background: rgba($white, 0.75) !important; }
.daily-todo-control:not(.task-not-scoreable):hover { background: rgba($white, 0.75) !important; }
}
&-bg-noninteractive { background: $orange-100 !important; }
&-inner-habit { background: rgba($orange-1, 0.25) !important; }
@@ -123,7 +123,7 @@
&-bg {
background: $yellow-100 !important;
.habit-control:not(.task-not-scoreable):hover { background: rgba($yellow-1, 0.5) !important; }
.daily-todo-control:hover { background: rgba($white, 0.75) !important; }
.daily-todo-control:not(.task-not-scoreable):hover { background: rgba($white, 0.75) !important; }
}
&-bg-noninteractive { background: $yellow-100 !important; }
&-inner-habit { background: rgba($yellow-1, 0.25) !important; }
@@ -154,7 +154,7 @@
&-bg {
background: $green-100 !important;
.habit-control:not(.task-not-scoreable):hover { background: rgba($black, 0.5) !important; }
.daily-todo-control:hover { background: rgba($white, 0.75) !important; }
.daily-todo-control:not(.task-not-scoreable):hover { background: rgba($white, 0.75) !important; }
}
&-bg-noninteractive { background: $green-100 !important; }
&-inner-habit { background: rgba($black, 0.25) !important; }
@@ -186,7 +186,7 @@
&-bg {
background: $teal-100 !important;
.habit-control:not(.task-not-scoreable):hover { background: rgba($black, 0.5) !important; }
.daily-todo-control:hover { background: rgba($white, 0.75) !important; }
.daily-todo-control:not(.task-not-scoreable):hover { background: rgba($white, 0.75) !important; }
}
&-bg-noninteractive { background: $teal-100 !important; }
&-inner-habit { background: rgba($black, 0.25) !important; }
@@ -217,7 +217,7 @@
&-bg {
background: $blue-100 !important;
.habit-control:not(.task-not-scoreable):hover { background: rgba($black, 0.5) !important; }
.daily-todo-control:hover { background: rgba($white, 0.75) !important; }
.daily-todo-control:not(.task-not-scoreable):hover { background: rgba($white, 0.75) !important; }
}
&-bg-noninteractive { background: $blue-100 !important; }
&-inner-habit { background: rgba($black, 0.25) !important; }
@@ -249,7 +249,7 @@
&-bg {
background: $purple-task !important;
.habit-control:not(.task-not-scoreable):hover { background: rgba($black, 0.5) !important; }
.daily-todo-control:hover { background: rgba($white, 0.75) !important; }
.daily-todo-control:not(.task-not-scoreable):hover { background: rgba($white, 0.75) !important; }
}
&-inner-habit { background: rgba($black, 0.25) !important; }
&-inner-daily-todo { background: rgba($white, 0.5) !important; }
@@ -258,7 +258,7 @@
&-modal {
&-bg { background: $purple-300 !important; }
&-headings { color: $white; }
&-headings { color: $white !important; }
&-icon { color: $purple-300 !important; }
&-text { color: $black !important; }
&-content {
@@ -281,11 +281,11 @@
background: rgba($yellow-100, 0.15) !important;
.small-text { color: $yellow-1 !important; }
&:hover { background: rgba($yellow-100, 0.25) !important; }
&:hover:not(.task-not-scoreable) { background: rgba($yellow-100, 0.25) !important; }
}
&-bg-noninteractive {
background: rgba($yellow-100, 0.15) !important;
.small-text { color: $yellow-1 !important; }
background-color: $gray-500 !important;
.small-text { color: $gray-100 !important; }
}
}
}
@@ -312,7 +312,7 @@
&-control {
&-bg {
background: $gray-200 !important;
.daily-todo-control:hover { background: rgba($white, 0.75) !important; }
.daily-todo-control:not(.task-not-scoreable):hover { background: rgba($white, 0.75) !important; }
}
&-bg-noninteractive {
background: $gray-200 !important;
@@ -323,7 +323,7 @@
background: $gray-600;
.task-title, .task-notes {
color: $gray-300 !important;
opacity: 0.75;
}
}
}

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="10" viewBox="0 0 13 10">
<path fill-rule="evenodd" d="M4.662 9.832c-.312 0-.61-.123-.831-.344L0 5.657l1.662-1.662 2.934 2.934L10.534 0l1.785 1.529-6.764 7.893a1.182 1.182 0 0 1-.848.409l-.045.001"/>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path d="M6.54,13c-.3,0-.59-.13-.81-.35l-3.73-3.9,1.62-1.69,2.86,2.98L12.26,3l1.74,1.56L7.41,12.58c-.21,.25-.51,.4-.83,.42-.01,0-.03,0-.04,0Z" fill-rule="evenodd"/>
</svg>

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 236 B

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="16" viewBox="0 0 8 16">
<path fill-rule="evenodd" d="M7.145 8.006H4.903V16H1.58V8.006H0V5.182h1.58V3.354C1.58 2.045 2.202 0 4.933 0l2.461.01v2.742H5.608c-.291 0-.705.145-.705.77v1.66h2.533l-.291 2.824z"/>
<svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M20,0H4C1.79,0,0,1.79,0,4V20c0,2.21,1.79,4,4,4H20c2.21,0,4-1.79,4-4V4c0-2.21-1.79-4-4-4Zm-3.72,6.66h-1.26c-1.24,0-1.63,.77-1.63,1.56v1.88h2.78l-.44,2.9h-2.33v7h-3.13v-7h-2.54v-2.9h2.54v-2.21c0-2.51,1.5-3.9,3.78-3.9,1.1,0,2.24,.2,2.24,.2v2.47Z" fill-rule="evenodd"/>
</svg>

Before

Width:  |  Height:  |  Size: 274 B

After

Width:  |  Height:  |  Size: 354 B

View File

@@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<path fill="#DE3F3F" d="M0 5.667 3.333 1h9.334L16 5.667l-8 8.666z"/>
<path fill="#FFF" opacity=".25" d="M4.667 5.533 4 2.333h4zM11.333 5.533l.667-3.2H8z"/>
<path fill="#FFF" opacity=".5" d="M4.667 5.533 8 2.333l3.333 3.2zM1.733 5.533 4 2.333l.667 3.2z"/>
<path fill="#34313A" opacity=".11" d="M14.267 5.533 12 2.333l-.667 3.2zM1.733 5.533h2.934L8 12.4z"/>
<path fill="#FFF" opacity=".5" d="M14.267 5.533h-2.934L8 12.4z"/>
<path fill="#FFF" opacity=".25" d="M4.667 5.533h6.666L8 12.4z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 675 B

View File

@@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<path d="M3 12.606v1.778c0 .208.093.408.262.53 1.842 1.347 6.923 1.347 8.766 0a.655.655 0 0 0 .26-.53v-1.778c0-1.621-.831-3.177-2.091-4.104a.666.666 0 0 1 0-1.08c1.26-.927 2.092-2.483 2.092-4.105V1.54a.652.652 0 0 0-.261-.53c-1.843-1.346-6.924-1.346-8.766 0A.65.65 0 0 0 3 1.54v1.777c0 1.622.832 3.178 2.092 4.105.368.27.368.81 0 1.08C3.832 9.429 3 10.985 3 12.606" fill="#F19595"/>
<path d="M7.644 1.327c1.51 0 2.684.274 3.318.587v1.403c0 1.169-.594 2.332-1.551 3.036a2.006 2.006 0 0 0-.818 1.609c0 .63.305 1.232.817 1.608.958.705 1.552 1.868 1.552 3.036v1.404c-.634.313-1.809.587-3.318.587-1.508 0-2.683-.274-3.317-.587v-1.404c0-1.168.594-2.331 1.551-3.035.513-.377.817-.978.817-1.609 0-.63-.304-1.232-.816-1.609-.958-.704-1.552-1.867-1.552-3.036V1.914c.634-.313 1.809-.587 3.317-.587" fill-opacity=".9" fill="#FFF"/>
<path d="M7.797 2.324c-1.132 0-2.331.105-2.343.385-.01.226-.005.664.914 1.13.893.453 1.06 1.282 1.546 1.282.564 0 .596-.477 1.284-.95.71-.488.823-1.148.815-1.408-.011-.363-1.084-.439-2.216-.439" fill="#DE3F3F"/>
<path d="M9.198 4.17c.71-.487.823-1.146.815-1.407-.009-.288-.684-.395-1.526-.427.236.12.543.377.467.88-.078.525-.904 1.105-.77 1.568.025.09.069.162.124.221.247-.17.408-.502.89-.835" fill="#B01515"/>
<path d="M7.644 9.17c-.344 0-.433.628-.933 1.018-.613.478-1.196 1.067-1.356 1.914-.131.698-.012.785.148.834.16.049 1.386.257 2.588 0 1.203-.258 1.87-.737 1.755-1.227-.111-.466-.448-.865-1.068-1.325-.593-.44-.79-1.214-1.134-1.214" fill="#DE3F3F"/>
<path d="M5.503 12.936c.16.05 1.386.257 2.588 0 .956-.205 1.574-.55 1.729-.929a.096.096 0 0 0-.005-.023c-.067-.256-1.073-.41-2.325-.207-1.192.192-2.158.586-2.153 1.03.037.08.097.108.166.129" fill="#B01515"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 0c2.173 0 2.445.01 3.298.048.852.04 1.433.174 1.942.372.526.205.973.478 1.418.922.444.445.717.892.922 1.418.198.509.333 1.09.372 1.942C15.99 5.555 16 5.827 16 8s-.01 2.445-.048 3.298c-.04.852-.174 1.433-.372 1.942a3.924 3.924 0 0 1-.922 1.418 3.924 3.924 0 0 1-1.418.922c-.509.198-1.09.333-1.942.372-.853.04-1.125.048-3.298.048s-2.445-.009-3.298-.048c-.852-.04-1.433-.174-1.942-.372a3.924 3.924 0 0 1-1.418-.922A3.924 3.924 0 0 1 .42 13.24c-.198-.509-.333-1.09-.372-1.942C.01 10.445 0 10.173 0 8s.01-2.445.048-3.298C.088 3.85.222 3.269.42 2.76c.205-.526.478-.973.922-1.418A3.924 3.924 0 0 1 2.76.42C3.269.222 3.85.087 4.702.048 5.555.01 5.827 0 8 0zm0 3.892a4.108 4.108 0 1 0 0 8.216 4.108 4.108 0 0 0 0-8.216zm5.23-.162a.96.96 0 1 0-1.92 0 .96.96 0 0 0 1.92 0zM8 10.667a2.666 2.666 0 1 1 0-5.333 2.666 2.666 0 0 1 0 5.333z"/>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M14.67,12A2.67,2.67,0,1,1,12,9.33,2.67,2.67,0,0,1,14.67,12Zm3.89,0c0,1.16.1,3.66-.32,4.71a2.7,2.7,0,0,1-1.52,1.52c-1.06.42-3.55.33-4.72.33s-3.66.09-4.71-.33a2.65,2.65,0,0,1-1.52-1.52c-.42-1.05-.33-3.55-.33-4.71s-.09-3.67.33-4.72A2.65,2.65,0,0,1,7.29,5.76c1-.42,3.55-.32,4.71-.32a18,18,0,0,1,4.72.32,2.7,2.7,0,0,1,1.52,1.52C18.66,8.34,18.56,10.83,18.56,12ZM16.1,12A4.1,4.1,0,1,0,12,16.1,4.09,4.09,0,0,0,16.1,12Zm1.13-4.27a1,1,0,1,0-1,1A1,1,0,0,0,17.23,7.73ZM24,4V20a4,4,0,0,1-4,4H4a4,4,0,0,1-4-4V4A4,4,0,0,1,4,0H20A4,4,0,0,1,24,4ZM19.94,8.7a4.71,4.71,0,0,0-1.29-3.35A4.71,4.71,0,0,0,15.3,4.06C14,4,10,4,8.7,4.06A4.76,4.76,0,0,0,5.35,5.34,4.75,4.75,0,0,0,4.06,8.7C4,10,4,14,4.06,15.3a4.73,4.73,0,0,0,1.29,3.35A4.77,4.77,0,0,0,8.7,19.94c1.32.08,5.28.08,6.6,0a4.71,4.71,0,0,0,3.35-1.29,4.73,4.73,0,0,0,1.29-3.35C20,14,20,10,19.94,8.7Z" />
</svg>

Before

Width:  |  Height:  |  Size: 954 B

After

Width:  |  Height:  |  Size: 914 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="14" viewBox="0 0 16 14" xmlns="http://www.w3.org/2000/svg">
<path d="m12.707 5.707-1.414-1.414L8 7.586 6.707 6.293 5.293 7.707 8 10.414l4.707-4.707zM16 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-2h2v2h10V2H4v2.5h2l-3 3-3-3h2V2a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" fill="#878190" fill-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 325 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M20,0H4A4,4,0,0,0,0,4V20a4,4,0,0,0,4,4H20a4,4,0,0,0,4-4V4A4,4,0,0,0,20,0ZM16.68,19a4.47,4.47,0,0,1-3,1C9.86,20,9,17.22,9,15.61v-4.5H7.56a.31.31,0,0,1-.31-.32V8.67a.52.52,0,0,1,.35-.5,4,4,0,0,0,2.63-3.66c0-.34.21-.51.51-.51H13a.31.31,0,0,1,.32.31v3.6h2.59a.31.31,0,0,1,.31.31v2.55a.31.31,0,0,1-.31.32H13.25v4.16c0,1.07.74,1.67,2.13,1.12a.67.67,0,0,1,.4-.07.36.36,0,0,1,.23.25l.68,2C16.75,18.71,16.8,18.89,16.68,19Z" fill-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 516 B

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="13" viewBox="0 0 16 13">
<path fill-rule="evenodd" d="M14.362 3.238c.007.141.01.281.01.424 0 4.338-3.302 9.34-9.34 9.34A9.284 9.284 0 0 1 0 11.527c.257.029.518.045.783.045a6.576 6.576 0 0 0 4.076-1.404 3.288 3.288 0 0 1-3.065-2.28 3.312 3.312 0 0 0 1.481-.056A3.288 3.288 0 0 1 .642 4.613v-.041c.444.246.949.393 1.488.41A3.28 3.28 0 0 1 .67 2.25c0-.602.162-1.166.444-1.651a9.315 9.315 0 0 0 6.766 3.43A3.28 3.28 0 0 1 11.078 0c.943 0 1.797.398 2.395 1.035a6.565 6.565 0 0 0 2.085-.797 3.289 3.289 0 0 1-1.443 1.816A6.543 6.543 0 0 0 16 1.539a6.665 6.665 0 0 1-1.638 1.699"/>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M20,0H4A4,4,0,0,0,0,4V20a4,4,0,0,0,4,4H20a4,4,0,0,0,4-4V4A4,4,0,0,0,20,0ZM18.36,8.74c0,.14,0,.29,0,.43A9.34,9.34,0,0,1,4,17a6.85,6.85,0,0,0,.79,0,6.57,6.57,0,0,0,4.07-1.4A3.29,3.29,0,0,1,5.8,13.39a4.1,4.1,0,0,0,.62,0,3.49,3.49,0,0,0,.86-.11,3.28,3.28,0,0,1-2.63-3.22v0a3.35,3.35,0,0,0,1.48.42A3.29,3.29,0,0,1,4.67,7.76,3.22,3.22,0,0,1,5.12,6.1a9.3,9.3,0,0,0,6.76,3.43,3.67,3.67,0,0,1-.08-.75,3.28,3.28,0,0,1,5.67-2.24,6.54,6.54,0,0,0,2.08-.79,3.22,3.22,0,0,1-1.44,1.8A6.67,6.67,0,0,0,20,7.05,7.31,7.31,0,0,1,18.36,8.74Z" fill-rule="evenodd"/>
</svg>

Before

Width:  |  Height:  |  Size: 645 B

After

Width:  |  Height:  |  Size: 622 B

View File

@@ -1,20 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
<defs>
<path id="rt3mruthma" d="M11.2 13.2c0-1.231-.467-2.35-1.23-3.2h1.23c1.765 0 3.2 1.435 3.2 3.2h-3.2zm-9.6 0c0-1.765 1.435-3.2 3.2-3.2h1.6c1.765 0 3.2 1.435 3.2 3.2h-8zm4-9.6C6.926 3.6 8 4.674 8 6c0 1.326-1.074 2.4-2.4 2.4-1.326 0-2.4-1.074-2.4-2.4 0-1.326 1.074-2.4 2.4-2.4zm3.452.415C9.436 3.754 9.9 3.6 10.4 3.6c1.326 0 2.4 1.074 2.4 2.4 0 1.326-1.074 2.4-2.4 2.4-.5 0-.964-.154-1.348-.415.34-.587.548-1.26.548-1.985 0-.726-.209-1.398-.548-1.985zm4.154 4.829C13.942 8.118 14.4 7.112 14.4 6c0-2.206-1.794-4-4-4-.904 0-1.73.313-2.4.82C7.33 2.314 6.504 2 5.6 2c-2.206 0-4 1.794-4 4 0 1.112.458 2.118 1.194 2.844C1.146 9.604 0 11.266 0 13.2c0 .884.716 1.6 1.6 1.6h12.8c.884 0 1.6-.716 1.6-1.6 0-1.934-1.146-3.596-2.794-4.356z"/>
</defs>
<g fill="none" fill-rule="evenodd">
<g>
<g>
<g transform="translate(-1228 -1456) translate(1216 1416) translate(12 40)">
<mask id="6szqyv7o1b" fill="#fff">
<use xlink:href="#rt3mruthma"/>
</mask>
<use fill="#878190" xlink:href="#rt3mruthma"/>
<g fill="#878190" mask="url(#6szqyv7o1b)">
<path d="M0 0H16V16H0z"/>
</g>
</g>
</g>
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 12.8"><path d="M11.2,12.8A4.79,4.79,0,0,0,10,9.6H11.2a3.21,3.21,0,0,1,3.2,3.2Zm-9.6,0A3.21,3.21,0,0,1,4.8,9.6H6.4a3.21,3.21,0,0,1,3.2,3.2Zm4-9.6A2.4,2.4,0,1,1,3.2,5.6,2.39,2.39,0,0,1,5.6,3.2Zm3.45.42A2.35,2.35,0,0,1,10.4,3.2a2.4,2.4,0,1,1,0,4.8,2.35,2.35,0,0,1-1.35-.42,3.84,3.84,0,0,0,0-4Zm4.16,4.82A4,4,0,0,0,8,2.42,4,4,0,0,0,5.6,1.6,4,4,0,0,0,2.79,8.44,4.82,4.82,0,0,0,0,12.8a1.6,1.6,0,0,0,1.6,1.6H14.4A1.6,1.6,0,0,0,16,12.8a4.82,4.82,0,0,0-2.79-4.36Z" transform="translate(0 -1.6)" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 569 B

View File

@@ -22,11 +22,6 @@
Account created:
<strong>{{ hero.auth.timestamps.created | formatDate }}</strong>
</div>
<div>
Most recent cron:
<strong>{{ hero.auth.timestamps.loggedin | formatDate }}</strong>
("auth.timestamps.loggedin")
</div>
<div v-if="cronError">
"lastCron" value:
<strong>{{ hero.lastCron | formatDate }}</strong>
@@ -36,6 +31,19 @@
("auth.timestamps.loggedin" and "lastCron" dates are different).
</span>
</div>
<div class="form-inline">
<div>
Most recent cron:
<strong>{{ hero.auth.timestamps.loggedin | formatDate }}</strong>
("auth.timestamps.loggedin")
</div>
<button
class="btn btn-primary ml-2"
@click="resetCron()"
>
Reset Cron to Yesterday
</button>
</div>
<div class="subsection-start">
Time zone:
<strong>{{ hero.preferences.timezoneOffset | formatTimeZone }}</strong>
@@ -218,6 +226,10 @@ export default {
await this.saveHero({ hero: this.hero, msg: 'API Token' });
this.tokenModified = true;
},
resetCron () {
this.hero.resetCron = true;
this.saveHero({ hero: this.hero, msg: 'Last Cron', clearData: true });
},
},
};
</script>

View File

@@ -17,6 +17,10 @@
:reset-counter="resetCounter"
/>
<subscription-and-perks
:hero="hero"
/>
<cron-and-auth
:hero="hero"
:reset-counter="resetCounter"
@@ -45,6 +49,7 @@
<transactions
:hero="hero"
:reset-counter="resetCounter"
/>
<contributor-details
@@ -97,6 +102,7 @@ import AvatarAndDrops from './avatarAndDrops';
import PrivilegesAndGems from './privilegesAndGems';
import ContributorDetails from './contributorDetails';
import Transactions from './transactions';
import SubscriptionAndPerks from './subscriptionAndPerks';
import { userStateMixin } from '../../../mixins/userState';
@@ -110,6 +116,7 @@ export default {
PrivilegesAndGems,
ContributorDetails,
Transactions,
SubscriptionAndPerks,
},
mixins: [userStateMixin],
data () {

View File

@@ -40,6 +40,11 @@
v-model="hero.auth.blocked"
type="checkbox"
> Ban / Block
<p>
<small>
Banning a user also auto-hides all their guild posts.
</small>
</p>
</label>
</div>
<div class="form-inline">

View File

@@ -0,0 +1,155 @@
<template>
<div class="accordion-group">
<h3
class="expand-toggle"
:class="{'open': expand}"
@click="expand = !expand"
>
Subscription, Monthly Perks
</h3>
<div v-if="expand">
<form @submit.prevent="saveHero({ hero, msg: 'Subscription Perks' })">
<div v-if="hero.purchased.plan.paymentMethod">
Payment method:
<strong>{{ hero.purchased.plan.paymentMethod }}</strong>
</div>
<div v-if="hero.purchased.plan.planId">
Payment schedule ("basic-earned" is monthly):
<strong>{{ hero.purchased.plan.planId }}</strong>
</div>
<div v-if="hero.purchased.plan.dateCreated">
Creation date:
<strong>{{ dateFormat(hero.purchased.plan.dateCreated) }}</strong>
</div>
<div>
Termination date:
<strong
v-if="hero.purchased.plan.dateTerminated"
>
{{ dateFormat(hero.purchased.plan.dateTerminated) }}
</strong>
<strong v-else> None </strong>
</div>
<div class="form-inline">
<label>
Consecutive months:
<input
v-model="hero.purchased.plan.consecutive.count"
class="form-control"
type="number"
min="0"
step="1"
>
</label>
</div>
<div>
Months until renewal:
<strong>{{ hero.purchased.plan.consecutive.offset }}</strong>
</div>
<div>
Next Mystic Hourglass:
<strong>{{ nextHourglassDate }}</strong>
</div>
<div class="form-inline">
<label>
Mystic Hourglasses:
<input
v-model="hero.purchased.plan.consecutive.trinkets"
class="form-control"
type="number"
min="0"
step="1"
>
</label>
</div>
<div>
Gem cap:
<strong>{{ hero.purchased.plan.consecutive.gemCapExtra + 25 }}</strong>
</div>
<div class="form-inline">
<label>
Gems bought this month:
<input
v-model="hero.purchased.plan.gemsBought"
class="form-control"
type="number"
min="0"
:max="hero.purchased.plan.consecutive.gemCapExtra + 25"
step="1"
>
</label>
</div>
<div
v-if="hero.purchased.plan.extraMonths > 0"
>
Additional credit (applied upon cancellation):
<strong>{{ hero.purchased.plan.extraMonths }}</strong>
</div>
<div>
Mystery Items:
<span
v-if="hero.purchased.plan.mysteryItems.length > 0"
>
<span
v-for="(item, index) in hero.purchased.plan.mysteryItems"
:key="index"
>
<strong v-if="index < hero.purchased.plan.mysteryItems.length - 1">
{{ item }},
</strong>
<strong v-else> {{ item }} </strong>
</span>
</span>
<span v-else>
<strong>None</strong>
</span>
</div>
<input
type="submit"
value="Save"
class="btn btn-primary mt-1"
>
</form>
</div>
</div>
</template>
<script>
import moment from 'moment';
import saveHero from '../mixins/saveHero';
import { getPlanContext } from '@/../../common/script/cron';
export default {
mixins: [saveHero],
props: {
hero: {
type: Object,
required: true,
},
},
data () {
return {
expand: false,
};
},
computed: {
nextHourglassDate () {
const currentPlanContext = getPlanContext(this.hero, new Date());
return currentPlanContext.nextHourglassDate.format('MMMM');
},
},
watch: {
'hero.purchased.plan.consecutive.count' () { // eslint-disable-line object-shorthand
this.hero.purchased.plan.consecutive.gemCapExtra = Math.min(
Math.floor(this.hero.purchased.plan.consecutive.count / 3) * 5, 25,
);
},
},
methods: {
dateFormat (date) {
return moment(date).format('YYYY/MM/DD');
},
},
};
</script>

View File

@@ -30,6 +30,10 @@ export default {
type: Object,
required: true,
},
resetCounter: {
type: Number,
required: true,
},
},
data () {
return {
@@ -38,6 +42,14 @@ export default {
hourglassTransactions: [],
};
},
watch: {
resetCounter () {
if (this.expand) {
this.expand = !this.expand;
this.toggleTransactionsOpen();
}
},
},
methods: {
async toggleTransactionsOpen () {
this.expand = !this.expand;

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,5 @@
<template>
<div class="form">
<div class="form-group row text-center">
<div class="col-12">
<div
class="btn btn-secondary social-button"
@click="socialAuth('facebook')"
>
<div
class="svg-icon social-icon"
v-html="icons.facebookIcon"
></div>
<span>{{ registering
? $t('signUpWithSocial', {social: 'Facebook'})
: $t('loginWithSocial', {social: 'Facebook'}) }}</span>
</div>
</div>
</div>
<div class="form-group row text-center">
<div class="col-12">
<div
@@ -243,7 +227,6 @@ import debounce from 'lodash/debounce';
import isEmail from 'validator/lib/isEmail';
import { setUpAxios, buildAppleAuthUrl } from '@/libs/auth';
import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants';
import facebookSquareIcon from '@/assets/svg/facebook-square.svg';
import googleIcon from '@/assets/svg/google.svg';
import appleIcon from '@/assets/svg/apple_black.svg';
@@ -260,7 +243,6 @@ export default {
};
data.icons = Object.freeze({
facebookIcon: facebookSquareIcon,
googleIcon,
appleIcon,
});
@@ -308,8 +290,6 @@ export default {
},
mounted () {
hello.init({
facebook: process.env.FACEBOOK_KEY, // eslint-disable-line
// windows: WINDOWS_CLIENT_ID,
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
});
},

View File

@@ -621,7 +621,6 @@ import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants';
import exclamation from '@/assets/svg/exclamation.svg';
import gryphon from '@/assets/svg/gryphon.svg';
import habiticaIcon from '@/assets/svg/habitica-logo.svg';
import facebookSquareIcon from '@/assets/svg/facebook-square.svg';
import googleIcon from '@/assets/svg/google.svg';
import appleIcon from '@/assets/svg/apple_black.svg';
@@ -644,7 +643,6 @@ export default {
exclamation,
gryphon,
habiticaIcon,
facebookIcon: facebookSquareIcon,
googleIcon,
appleIcon,
});
@@ -734,8 +732,6 @@ export default {
},
mounted () {
hello.init({
facebook: process.env.FACEBOOK_KEY, // eslint-disable-line
// windows: WINDOWS_CLIENT_ID,
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
});
},
@@ -757,8 +753,7 @@ export default {
}, 500),
sanitizeRedirect (redirect) {
if (!redirect) return '/';
let sanitizedString = DOMPurify.sanitize(redirect).replace(/\\|\/\/|\./g, '');
sanitizedString = `/${sanitizedString}`;
const sanitizedString = DOMPurify.sanitize(redirect).replace(/\\|\/\/|\./g, '');
return sanitizedString;
},
async register () {

View File

@@ -27,7 +27,7 @@
<customize-options
:items="set.options"
:current-value="user.preferences.hair.color"
:full-set="!hideSet(set) && !userOwnsSet('hair', set.keys, 'color')"
:full-set="!hideSet(set.key) && !userOwnsSet('hair', set.keys, 'color')"
@unlock="unlock(`hair.color.${set.keys.join(',hair.color.')}`)"
/>
</div>

View File

@@ -23,7 +23,7 @@
<customize-options
:items="set.options"
:current-value="user.preferences.skin"
:full-set="!hideSet(set) && !userOwnsSet('skin', set.keys)"
:full-set="!hideSet(set.key) && !userOwnsSet('skin', set.keys)"
@unlock="unlock(`skin.${set.keys.join(',skin.')}`)"
/>
</div>

View File

@@ -280,6 +280,7 @@ import markdownDirective from '@/directives/markdown';
import { userStateMixin } from '../../mixins/userState';
import { TAVERN_ID, MIN_SHORTNAME_SIZE_FOR_CHALLENGES, MAX_SUMMARY_SIZE_FOR_CHALLENGES } from '@/../../common/script/constants';
import CategoryOptions from '@/../../common/script/content/categoryOptions';
export default {
directives: {
@@ -288,64 +289,7 @@ export default {
mixins: [userStateMixin],
props: ['groupId'],
data () {
const categoryOptions = [
{
label: 'habitica_official',
key: 'habitica_official',
},
{
label: 'academics',
key: 'academics',
},
{
label: 'advocacy_causes',
key: 'advocacy_causes',
},
{
label: 'creativity',
key: 'creativity',
},
{
label: 'entertainment',
key: 'entertainment',
},
{
label: 'finance',
key: 'finance',
},
{
label: 'health_fitness',
key: 'health_fitness',
},
{
label: 'hobbies_occupations',
key: 'hobbies_occupations',
},
{
label: 'location_based',
key: 'location_based',
},
{
label: 'mental_health',
key: 'mental_health',
},
{
label: 'getting_organized',
key: 'getting_organized',
},
{
label: 'self_improvement',
key: 'self_improvement',
},
{
label: 'spirituality',
key: 'spirituality',
},
{
label: 'time_management',
key: 'time_management',
},
];
const categoryOptions = CategoryOptions;
const hashedCategories = {};
categoryOptions.forEach(category => {
hashedCategories[category.key] = category.label;

View File

@@ -112,10 +112,8 @@ export default {
},
},
watch: {
'group._id': {
async groupId () {
this.loadChallenges();
},
'group._id': function groupId () {
this.loadChallenges();
},
},
mounted () {

View File

@@ -89,70 +89,14 @@
import throttle from 'lodash/throttle';
import FilterSidebar from '@/components/ui/filterSidebar';
import FilterGroup from '@/components/ui/filterGroup';
import CategoryOptions from '@/../../common/script/content/categoryOptions';
export default {
components: { FilterGroup, FilterSidebar },
data () {
return {
categoryFilters: [],
categoryOptions: [
{
label: 'habitica_official',
key: 'habitica_official',
},
{
label: 'academics',
key: 'academics',
},
{
label: 'advocacy_causes',
key: 'advocacy_causes',
},
{
label: 'creativity',
key: 'creativity',
},
{
label: 'entertainment',
key: 'entertainment',
},
{
label: 'finance',
key: 'finance',
},
{
label: 'health_fitness',
key: 'health_fitness',
},
{
label: 'hobbies_occupations',
key: 'hobbies_occupations',
},
{
label: 'location_based',
key: 'location_based',
},
{
label: 'mental_health',
key: 'mental_health',
},
{
label: 'getting_organized',
key: 'getting_organized',
},
{
label: 'self_improvement',
key: 'self_improvement',
},
{
label: 'spirituality',
key: 'spirituality',
},
{
label: 'time_management',
key: 'time_management',
},
],
categoryOptions: CategoryOptions,
membershipFilters: [],
membershipOptions: [
{

View File

@@ -1,3 +1,4 @@
<!-- THIS IS A VERY OLD FILE DO NOT USE -->
<template>
<div class="create-group-modal-pages">
<div

View File

@@ -0,0 +1,356 @@
<template>
<b-modal
id="create-group"
:title="activePage === PAGES.CREATE_GROUP ? 'Create your Group' : 'Select Payment'"
:hide-footer="true"
:hide-header="true"
size="md"
@hide="onHide()"
>
<div
v-if="activePage === PAGES.CREATE_GROUP"
class="col-12"
>
<!-- HEADER -->
<div
class="modal-close"
>
<span
class="cancel-text"
@click="close()"
>
{{ $t('cancel') }}
</span>
<button
class="btn btn-primary next-button"
:value="$t('next')"
:disabled="!newGroupIsReady"
@click="createGroup()"
>
{{ $t('next') }}
</button>
</div>
<h2>{{ $t('createGroup') }}</h2>
<!-- FORM -->
<div class="form-group">
<lockable-label
:text="$t('nameStar')"
/>
<input
id="new-group-name"
v-model="newGroup.name"
class="form-control input-medium option-content name-input"
required="required"
type="text"
:placeholder="$t('nameStarText')"
>
</div>
<div class="form-group">
<lockable-label
for="new-group-description"
:text="$t('descriptionOptional')"
class="description-label"
/>
<div class="characters-remaining">
{{ $t('charactersRemaining', {characters: charactersRemaining}) }}
</div>
<textarea
id="new-group-description"
v-model="newGroup.description"
class="form-control option-content description-input"
cols="3"
:placeholder="$t('descriptionOptionalText')"
maxlength="250"
></textarea>
</div>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input
id="create-group-leaderOnlyChallenges-checkbox"
v-model="newGroup.leaderOnly.challenges"
class="custom-control-input"
type="checkbox"
>
<label
class="custom-control-label"
for="create-group-leaderOnlyChallenges-checkbox"
>{{ $t('leaderOnlyChallenges') }}</label>
</div>
</div>
<div class="form-group">
<lockable-label
:text="$t('groupUse')"
/>
<select-translated-array
:items="[
'groupParentChildren',
'groupCouple',
'groupFriends',
'groupCoworkers',
'groupManager',
'groupTeacher'
]"
class="group-input"
:placeholder="'groupUseDefault'"
:value="newGroup.demographics"
@select="newGroup.demographics = $event"
/>
</div>
<div class="form-group">
<button
class="btn btn-primary btn-lg btn-block btn-payment"
:disabled="!newGroupIsReady"
@click="createGroup()"
>
{{ $t('nextPaymentMethod') }}
</button>
</div>
</div>
<!-- PAYMENT -->
<!-- @TODO: Separate payment into a separate modal -->
<div
v-if="activePage === PAGES.PAY"
class="col-12 payments"
>
<div class="text-center">
<payments-buttons
:stripe-fn="() => pay(PAYMENTS.STRIPE)"
:amazon-data="pay(PAYMENTS.AMAZON)"
/>
</div>
</div>
</b-modal>
</template>
<style lang="scss" scoped>
@import '~@/assets/scss/colors.scss';
h2 {
color: $purple-300;
font-size: 1.25rem;
height: 28px;
width: 120px;
margin-top: 24px;
align-self: center;
}
.cancel-text {
color: $blue-10;
font-size: 0.875rem;
margin-right: 16px;
text-align: center;
cursor: pointer;
}
.next-button {
align-self: center;
}
.form-control::placeholder {
color: $gray-50;
height: 32px;
}
.description-label {
margin-bottom: -24px;
}
.name-input, .description-input, .group-input {
margin-top: -4px;
}
.characters-remaining {
color: $gray-100;
font-size: 0.75rem;
line-height: 1.33;
text-align: right;
margin-bottom: 12px;
}
.description-input {
height: 56px;
}
.spacer {
margin-bottom: 16px !important;
}
.btn-payment {
margin: 24px 112px 24px 112px;
width: 177px;
}
.payments {
padding: 24px;
}
.payment-options {
margin-bottom: 4em;
.purple-box {
background-color: #4f2a93;
color: #fff;
padding: .5em;
border-radius: 8px;
width: 200px;
height: 215px;
.dollar {
}
.number {
font-size: 60px;
}
.name {
width: 100px;
margin-left: .3em;
}
div {
display: inline-block;
}
}
.box, .purple-box {
display: inline-block;
vertical-align: bottom;
}
}
</style>
<style lang="scss">
@import '~@/assets/scss/mixins.scss';
#create-group {
.modal-dialog {
max-width: 448px;
}
.modal-content {
width: 448px;
max-height: 436px;
border-radius: 8px;
box-shadow: 0 14px 28px 0 rgba(26, 24, 29, 0.24), 0 10px 10px 0 rgba(26, 24, 29, 0.28);
}
.modal-body{
padding: 0px;
margin-left: 12px;
margin-right: 12px;
}
.modal-close {
position: absolute;
right: 16px;
cursor: pointer;
top: 0px;
}
}
</style>
<script>
import paymentsMixin from '../../mixins/payments';
import { mapState } from '@/libs/store';
import paymentsButtons from '@/components/payments/buttons/list';
import selectTranslatedArray from '@/components/tasks/modal-controls/selectTranslatedArray';
import lockableLabel from '@/components/tasks/modal-controls/lockableLabel';
import * as Analytics from '@/libs/analytics';
export default {
components: {
paymentsButtons,
selectTranslatedArray,
lockableLabel,
},
mixins: [paymentsMixin],
data () {
return {
amazonPayments: {},
PAGES: {
CREATE_GROUP: 'create-group',
// UPGRADE_GROUP: 'upgrade-group',
PAY: 'pay',
},
PAYMENTS: {
AMAZON: 'amazon',
STRIPE: 'stripe',
},
paymentMethod: '',
newGroup: {
type: 'guild',
privacy: 'private',
name: '',
description: '',
leaderOnly: {
challenges: false,
},
demographics: null,
user: '',
},
activePage: 'create-group',
type: 'guild',
};
},
computed: {
...mapState({ user: 'user.data' }),
newGroupIsReady () {
return Boolean(this.newGroup.name) && Boolean(this.newGroup.demographics);
},
charactersRemaining () {
const currentLength = this.newGroup.description ? this.newGroup.description.length : 0;
return 250 - currentLength;
},
},
methods: {
close () {
this.$root.$emit('bv::hide::modal', 'create-group');
},
changePage (page) {
this.activePage = page;
},
createGroup () {
this.changePage(this.PAGES.PAY);
},
pay (paymentMethod) {
const subscriptionKey = 'group_monthly'; // @TODO: Get from content API?
const demographicsKey = this.newGroup.demographics;
const paymentData = {
subscription: subscriptionKey,
coupon: null,
demographics: demographicsKey,
};
Analytics.track({
hitType: 'event',
eventName: 'group plan create',
eventAction: 'group plan create',
eventCategory: 'behavior',
demographics: this.newGroup.demographics,
type: this.newGroup.type,
}, { trackOnClient: true });
if (this.upgradingGroup && this.upgradingGroup._id) {
paymentData.groupId = this.upgradingGroup._id;
paymentData.group = this.upgradingGroup;
} else {
paymentData.groupToCreate = this.newGroup;
}
this.paymentMethod = paymentMethod;
if (this.paymentMethod === this.PAYMENTS.AMAZON) {
paymentData.type = 'subscription';
return paymentData;
}
if (this.paymentMethod === this.PAYMENTS.STRIPE) {
this.redirectToStripe(paymentData);
}
return null;
},
onHide () {
this.sendingInProgress = false;
},
},
};
</script>

View File

@@ -0,0 +1,204 @@
<template>
<b-modal
id="group-plans-update"
title="New Shared Task Board"
size="lg"
hide-footer="hide-footer"
:no-close-on-backdrop="true"
:no-close-on-esc="true"
:centered="true"
>
<div
slot="modal-header"
class="w-100 d-flex pt-2 justify-content-center"
>
<h2
class="mx-auto mt-4"
v-once
>
{{ $t('newGroupsWelcome') }}
</h2>
<div
class="svg-icon color close-x ml-auto my-auto"
aria-hidden="true"
tabindex="0"
@click="close()"
@keypress.enter="close()"
v-html="icons.close"
></div>
<img
class="task-columns"
src="~@/assets/images/group-plans/task-columns.png"
srcset="~@/assets/images/group-plans/task-columns@2x.png 2x,
~@/assets/images/group-plans/task-columns@3x.png 3x"
>
</div>
<div
class="d-flex flex-column justify-content-center p-4"
>
<div class="d-flex align-items-center justify-content-center mb-3">
<div
class="svg-icon sparkles my-auto mr-3"
v-html="icons.sparkles"
>
</div>
<h3
class="my-auto"
v-once
>
{{ $t('newGroupsWhatsNew') }}
</h3>
<div
class="svg-icon sparkles my-auto ml-3 flip-x"
v-html="icons.sparkles"
>
</div>
</div>
<ul
class="mb-4 px-4"
>
<li>{{ $t('newGroupsBullet01') }}</li>
<li>{{ $t('newGroupsBullet02') }}</li>
<li>{{ $t('newGroupsBullet03') }}</li>
<li>{{ $t('newGroupsBullet04') }}</li>
<li>{{ $t('newGroupsBullet05') }}</li>
<li>{{ $t('newGroupsBullet06') }}</li>
<li>{{ $t('newGroupsBullet07') }}</li>
<li>{{ $t('newGroupsBullet08') }}</li>
<li>{{ $t('newGroupsBullet09') }}</li>
<li>{{ $t('newGroupsBullet10') }}
<ul class="p-0">
<li v-html="$t('newGroupsBullet10a')"></li>
<li v-html="$t('newGroupsBullet10b')"></li>
<li v-html="$t('newGroupsBullet10c')"></li>
</ul>
</li>
</ul>
<div
class="mx-auto"
v-html="$t('newGroupsVisitFAQ')"
></div>
<div
class="mx-auto"
>
{{ $t('newGroupsEnjoy') }}
</div>
<button
class="btn btn-primary mt-4 mb-1 mx-auto"
@click="close()"
@keypress.enter="close()"
>
{{ $t('getStarted') }}
</button>
</div>
</b-modal>
</template>
<style lang="scss">
@import '~@/assets/scss/colors.scss';
#group-plans-update {
.modal-content {
border-top-left-radius: 10px;
border-top-right-radius: 10px;
border: none;
box-shadow: 0 14px 28px 0 rgba($black, 0.24), 0 10px 10px 0 rgba($black, 0.28);
}
.modal-dialog {
max-width: 566px;
margin-top: 0px;
}
.modal-header {
background-image: linear-gradient(64deg, $purple-200, $purple-300 55%);
height: 136px;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
}
.modal-header, .modal-body, .modal-footer {
padding: 0px;
border: none;
}
}
</style>
<style lang="scss" scoped>
@import '~@/assets/scss/colors.scss';
h2 {
color: $white;
line-height: 28px;
}
li {
padding-left: 0.75rem;
}
ul {
line-height: 24px;
list-style-type: square;
li ul {
list-style-type: none;
li::before {
margin-right: 1rem;
font-size: 16px;
content: '▫';
}
}
}
.btn-primary {
width: max-content;
}
.close-x {
color: $purple-400;
height: 16px;
width: 16px;
position: absolute;
opacity: 0.75;
cursor: pointer;
right: 18px;
top: 18px;
&:hover, &:focus {
opacity: 1;
}
}
.sparkles {
width: 32px;
height: 17px;
&.flip-x {
transform: scaleX(-1);
}
}
.task-columns {
position: absolute;
top: 84px;
}
</style>
<script>
import closeIcon from '@/assets/svg/close.svg';
import sparkles from '@/assets/svg/sparkles-left.svg';
export default {
data () {
return {
icons: Object.freeze({
close: closeIcon,
sparkles,
}),
};
},
methods: {
close () {
this.$store.dispatch('user:set', { 'flags.tour.groupPlans': -2 });
this.$root.$emit('bv::hide::modal', 'group-plans-update');
},
},
};
</script>

View File

@@ -28,7 +28,7 @@
{{ $t('groupBilling') }}
</router-link>
</secondary-menu>
<div class="col-12">
<div class="col-12 px-0">
<router-view />
</div>
</div>

View File

@@ -1,5 +1,8 @@
<template>
<div class="standard-page">
<div
class="standard-page"
@click="openCreateBtn ? openCreateBtn = false : null"
>
<group-plan-overview-modal />
<task-modal
ref="taskModal"
@@ -7,65 +10,80 @@
:purpose="taskFormPurpose"
:group-id="groupId"
@cancel="cancelTaskModal()"
@taskCreated="taskCreated"
@taskEdited="taskEdited"
@taskCreated="loadTasks"
@taskEdited="loadTasks"
@taskDestroyed="taskDestroyed"
/>
<div class="row tasks-navigation">
<div class="col-12 col-md-4">
<h1>{{ $t('groupTasksTitle') }}</h1>
<task-summary
ref="taskSummary"
:task="editingTask"
@cancel="cancelTaskModal()"
/>
<div class="d-flex flex-wrap align-items-center mb-4">
<div class="mr-auto">
<h1>{{ group.name }}</h1>
</div>
<!-- @TODO: Abstract to component!-->
<div class="col-12 col-md-4">
<input
v-model="searchText"
class="form-control input-search"
type="text"
:placeholder="$t('search')"
>
</div>
<div
v-if="canCreateTasks"
class="create-task-area d-flex"
<input
v-model="searchText"
class="form-control input-search"
type="text"
:placeholder="$t('search')"
>
<transition name="slide-tasks-btns">
<div
class="d-flex flex-wrap align-items-center justify-content-end ml-auto"
>
<toggle-switch
id="taskMirrorToggle"
class="mr-3 mb-1 ml-auto"
:label="'Copy tasks'"
:checked="user.preferences.tasks.mirrorGroupTasks.indexOf(group._id) !== -1"
:hover-text="'Show assigned and open tasks on your personal task board'"
@change="changeMirrorPreference"
/>
<div
class="day-start d-flex align-items-center"
v-html="$t('dayStart', { startTime: groupStartTime } )"
>
</div>
<div class="create-task-area ml-2">
<button
id="create-task-btn"
v-if="canCreateTasks"
class="btn btn-primary create-btn d-flex align-items-center"
:class="{open: openCreateBtn}"
@click.stop.prevent="openCreateBtn = !openCreateBtn"
@keypress.enter="openCreateBtn = !openCreateBtn"
tabindex="0"
>
<div
class="svg-icon icon-10 color"
v-html="icons.positive"
></div>
<div class="ml-75 mr-1"> {{ $t('addTask') }} </div>
</button>
<div
v-if="openCreateBtn"
class="d-flex"
class="dropdown"
>
<div
v-for="type in columns"
:key="type"
v-b-tooltip.hover.bottom="$t(type)"
class="create-task-btn diamond-btn"
@click="createTask(type)"
class="dropdown-item d-flex px-2 py-1"
>
<div
class="svg-icon"
:class="`icon-${type}`"
v-html="icons[type]"
></div>
<div class="d-flex align-items-center justify-content-center task-icon">
<div
class="svg-icon m-auto"
:class="`icon-${type}`"
v-html="icons[type]"
></div>
</div>
<div class="task-label ml-2">
{{ $t(type) }}
</div>
</div>
</div>
</transition>
<div
id="create-task-btn"
class="create-btn diamond-btn btn btn-success"
:class="{open: openCreateBtn}"
@click="openCreateBtn = !openCreateBtn"
>
<div
class="svg-icon"
v-html="icons.positive"
></div>
</div>
<b-tooltip
v-if="!openCreateBtn"
target="create-task-btn"
placement="bottom"
>
{{ $t('create') }}
</b-tooltip>
</div>
</div>
<div class="row">
@@ -79,6 +97,7 @@
:search-text="searchText"
:draggable-override="canCreateTasks"
@editTask="editTask"
@taskSummary="taskSummary"
@loadGroupCompletedTodos="loadGroupCompletedTodos"
@taskDestroyed="taskDestroyed"
/>
@@ -86,12 +105,66 @@
</div>
</template>
<style lang="scss">
#taskMirrorToggle {
font-weight: bold;
.svg-icon {
margin: 3px 6px 0px 4px;
}
.toggle-switch {
margin-left: 0px;
}
.toggle-switch-description {
margin-top: 3px;
}
}
</style>
<style lang="scss" scoped>
@import '~@/assets/scss/colors.scss';
@import '~@/assets/scss/create-task.scss';
.tasks-navigation {
margin-bottom: 40px;
h1 {
color: $purple-300;
margin-bottom: 0px;
}
.create-task-area {
position: inherit;
.dropdown {
right: 24px;
}
}
.day-start {
height: 2rem;
padding: 0.25rem 0.75rem;
border-radius: 2px;
color: $gray-100;
background-color: $gray-600;
}
@media screen and (min-width: 1200px) {
.input-search {
margin-left: 12.5rem;
width: 25%;
}
}
@media screen and (max-width: 1200px) {
.input-search {
width: 50%;
}
}
@media screen and (max-width: 500px) {
#create-task-btn {
margin-top: 4px;
}
}
.positive {
@@ -107,11 +180,15 @@
import Vue from 'vue';
import cloneDeep from 'lodash/cloneDeep';
import findIndex from 'lodash/findIndex';
import groupBy from 'lodash/groupBy';
import moment from 'moment';
import taskDefaults from '@/../../common/script/libs/taskDefaults';
import TaskColumn from '../tasks/column';
import TaskModal from '../tasks/taskModal';
import TaskSummary from '../tasks/taskSummary';
import GroupPlanOverviewModal from './groupPlanOverviewModal';
import toggleSwitch from '@/components/ui/toggleSwitch';
import sync from '../../mixins/sync';
import positiveIcon from '@/assets/svg/positive.svg';
import filterIcon from '@/assets/svg/filter.svg';
@@ -121,14 +198,18 @@ import dailyIcon from '@/assets/svg/daily.svg';
import todoIcon from '@/assets/svg/todo.svg';
import rewardIcon from '@/assets/svg/reward.svg';
import * as Analytics from '@/libs/analytics';
import { mapState } from '@/libs/store';
export default {
components: {
TaskColumn,
TaskModal,
TaskSummary,
GroupPlanOverviewModal,
toggleSwitch,
},
mixins: [sync],
props: ['groupId'],
data () {
return {
@@ -199,6 +280,16 @@ export default {
return (this.group.leader && this.group.leader._id === this.user._id)
|| (this.group.managers && Boolean(this.group.managers[this.user._id]));
},
groupStartTime () {
if (!this.group || !this.group.cron) return null;
const { dayStart, timezoneOffset } = this.group.cron;
const timezoneDiff = this.user.preferences.timezoneOffset - timezoneOffset;
return moment()
.hour(dayStart)
.minute(0)
.subtract(timezoneDiff, 'minutes')
.format('h:mm A');
},
},
watch: {
// call again the method if the route changes (when this route is already active)
@@ -208,6 +299,10 @@ export default {
this.$set(this, 'searchId', to.params.groupId);
next();
},
async beforeRouteLeave (to, from, next) {
await this.sync();
next();
},
mounted () {
if (!this.searchId) this.searchId = this.groupId;
this.load();
@@ -218,6 +313,7 @@ export default {
this.$root.$on('habitica:team-sync', () => {
this.loadTasks();
this.loadGroupCompletedTodos();
});
},
methods: {
@@ -238,6 +334,9 @@ export default {
this.group.members = members;
this.loadTasks();
if (this.user.flags.tour.groupPlans !== -2) {
this.$root.$emit('bv::show::modal', 'group-plans-update');
}
},
async loadTasks () {
this.tasksByType = {
@@ -251,22 +350,13 @@ export default {
groupId: this.searchId,
});
const groupedApprovals = await this.loadApprovals();
tasks.forEach(task => {
if (
groupedApprovals[task._id]
&& groupedApprovals[task._id].length > 0
) task.approvals = groupedApprovals[task._id];
this.tasksByType[task.type].push(task);
});
},
async loadApprovals () {
const approvalRequests = await this.$store.dispatch('tasks:getGroupApprovals', {
groupId: this.searchId,
});
return groupBy(approvalRequests, 'group.taskId');
if (this.editingTask && this.editingTask.completed) {
this.loadGroupCompletedTodos();
}
},
editTask (task) {
this.taskFormPurpose = 'edit';
@@ -277,6 +367,12 @@ export default {
this.$root.$emit('bv::show::modal', 'task-modal');
});
},
taskSummary (task) {
this.editingTask = cloneDeep(task);
Vue.nextTick(() => {
this.$root.$emit('bv::show::modal', 'task-summary');
});
},
async loadGroupCompletedTodos () {
const completedTodos = await this.$store.dispatch('tasks:getCompletedGroupTasks', {
groupId: this.searchId,
@@ -299,14 +395,6 @@ export default {
this.$root.$emit('bv::show::modal', 'task-modal');
});
},
taskCreated (task) {
task.group.id = this.group._id;
this.tasksByType[task.type].unshift(task);
},
taskEdited (task) {
const index = findIndex(this.tasksByType[task.type], taskItem => taskItem._id === task._id);
this.tasksByType[task.type].splice(index, 1, task);
},
taskDestroyed (task) {
const index = findIndex(this.tasksByType[task.type], taskItem => taskItem._id === task._id);
this.tasksByType[task.type].splice(index, 1);
@@ -354,6 +442,25 @@ export default {
if (this.temporarilySelectedTags.indexOf(tagId) !== -1) return true;
return false;
},
changeMirrorPreference (newVal) {
Analytics.track({
eventName: 'mirror tasks',
eventAction: 'mirror tasks',
eventCategory: 'behavior',
hitType: 'event',
mirror: newVal,
group: this.group._id,
}, { trackOnClient: true });
const groupsToMirror = this.user.preferences.tasks.mirrorGroupTasks || [];
if (newVal) { // we're turning copy ON for this group
groupsToMirror.push(this.group._id);
} else { // we're turning copy OFF for this group
groupsToMirror.splice(groupsToMirror.indexOf(this.group._id), 1);
}
this.$store.dispatch('user:set', {
'preferences.tasks.mirrorGroupTasks': groupsToMirror,
});
},
},
};
</script>

View File

@@ -379,6 +379,7 @@ import informationIcon from '@/assets/svg/information.svg';
import { MAX_SUMMARY_SIZE_FOR_GUILDS } from '@/../../common/script/constants';
import { userStateMixin } from '../../mixins/userState';
import CategoryOptions from '@/../../common/script/content/categoryOptions';
// @TODO: Not sure the best way to pass party creating status
// Since we need the modal in the header, passing props doesn't work
@@ -410,64 +411,7 @@ export default {
allowGuildInvitationsFromNonMembers: true,
bannedWordsAllowed: null,
},
categoryOptions: [
{
label: 'habitica_official',
key: 'habitica_official',
},
{
label: 'academics',
key: 'academics',
},
{
label: 'advocacy_causes',
key: 'advocacy_causes',
},
{
label: 'creativity',
key: 'creativity',
},
{
label: 'entertainment',
key: 'entertainment',
},
{
label: 'finance',
key: 'finance',
},
{
label: 'health_fitness',
key: 'health_fitness',
},
{
label: 'hobbies_occupations',
key: 'hobbies_occupations',
},
{
label: 'location_based',
key: 'location_based',
},
{
label: 'mental_health',
key: 'mental_health',
},
{
label: 'getting_organized',
key: 'getting_organized',
},
{
label: 'recovery_support_groups',
key: 'recovery_support_groups',
},
{
label: 'spirituality',
key: 'spirituality',
},
{
label: 'time_management',
key: 'time_management',
},
],
categoryOptions: CategoryOptions,
showCategorySelect: false,
members: [],
inviteMembers: false,

View File

@@ -1,6 +1,7 @@
<template>
<!-- @TODO: Move to group plans folder-->
<div>
<group-plan-creation-modal />
<div>
<div class="header">
<h1 class="text-center">
@@ -51,6 +52,7 @@
</div>
</div>
</div>
<!-- Upgrading an existing group -->
<div
v-if="upgradingGroup._id"
id="upgrading-group"
@@ -92,7 +94,6 @@
</div>
</div>
<div class="box payment-providers">
<h3>Choose your payment method</h3>
<payments-buttons
:stripe-fn="() => pay(PAYMENTS.STRIPE)"
:amazon-data="pay(PAYMENTS.AMAZON)"
@@ -101,154 +102,53 @@
</div>
</div>
</div>
<!-- Create a new group -->
<div
v-if="!upgradingGroup._id"
class="container col-6 offset-3 create-option"
>
<div class="row">
<h1 class="col-12 text-center purple-header">
Create your Group today!
Create Your Group Today!
</h1>
</div>
<div class="row">
<div class="col-12 text-center">
<button
class="btn btn-primary create-group"
@click="launchModal('create')"
@click="launchModal('create-page')"
>
Create Your New Group
Create Your New Group!
</button>
</div>
</div>
<div class="row pricing">
<div class="col-5">
<div class="dollar">
$
</div>
<div class="number">
9
</div>
<div class="name">
<div>Group Owner</div>
<div>Subscription</div>
</div>
<div class="row pricing justify-content-center align-items-center">
<div class="dollar">
$
</div>
<div class="col-1">
<div class="plus">
+
</div>
<div class="number">
9
</div>
<div class="col-6">
<div class="dollar">
$
</div>
<div class="number">
3
</div>
<div class="name">
<div>Each Additional</div>
<div>Member</div>
</div>
<div class="name">
<div>Group Owner</div>
<div>Subscription</div>
</div>
<div class="plus">
+
</div>
<div class="dollar">
$
</div>
<div class="number">
3
</div>
<div class="name">
<div>Each Additional</div>
<div>Member</div>
</div>
</div>
</div>
</div>
<b-modal
id="group-plan-modal"
:title="activePage === PAGES.CREATE_GROUP ? 'Create your Group' : 'Select Payment'"
size="md"
hide-footer="hide-footer"
>
<div
v-if="activePage === PAGES.CREATE_GROUP"
class="col-12"
>
<div class="form-group">
<label
class="control-label"
for="new-group-name"
>Name</label>
<input
id="new-group-name"
v-model="newGroup.name"
class="form-control input-medium option-content"
required="required"
type="text"
placeholder="Name"
>
</div>
<div class="form-group">
<label for="new-group-description">{{ $t('description') }}</label>
<textarea
id="new-group-description"
v-model="newGroup.description"
class="form-control option-content"
cols="3"
:placeholder="$t('description')"
></textarea>
</div>
<div
v-if="type === 'guild'"
class="form-group"
>
<div class="custom-control custom-radio">
<input
v-model="newGroup.privacy"
class="custom-control-input"
type="radio"
name="new-group-privacy"
value="private"
>
<label class="custom-control-label">{{ $t('inviteOnly') }}</label>
</div>
</div>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input
id="create-group-leaderOnlyChallenges-checkbox"
v-model="newGroup.leaderOnly.challenges"
class="custom-control-input"
type="checkbox"
>
<label
class="custom-control-label"
for="create-group-leaderOnlyChallenges-checkbox"
>{{ $t('leaderOnlyChallenges') }}</label>
</div>
</div>
<div
v-if="type === 'party'"
class="form-group"
>
<button
class="btn btn-secondary form-control"
:value="$t('create')"
@click="createGroup()"
></button>
</div>
<div class="form-group">
<button
class="btn btn-primary btn-lg btn-block"
:disabled="!newGroupIsReady"
@click="createGroup()"
>
{{ $t('create') }}
</button>
</div>
</div>
<div
v-if="activePage === PAGES.PAY"
class="col-12"
>
<div class="text-center">
<h3>Choose your payment method</h3>
<payments-buttons
:stripe-fn="() => pay(PAYMENTS.STRIPE)"
:amazon-data="pay(PAYMENTS.AMAZON)"
/>
</div>
</div>
</b-modal>
</div>
</template>
@@ -260,12 +160,17 @@
.dollar {
position: absolute;
left: -1em;
top: 1em;
left: -16px;
top: 16px;
}
.purple-box {
color: #bda8ff;
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
}
.number {
@@ -279,6 +184,8 @@
.payment-providers {
width: 350px;
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
}
}
@@ -286,9 +193,9 @@
background: #432874;
background: linear-gradient(180deg, #4F2A93 0%, #432874 100%);
color: #fff;
padding: 2em;
padding: 32px;
height: 340px;
margin-bottom: 2em;
margin-bottom: 32px;
margin-left: -12px;
margin-right: -12px;
@@ -312,6 +219,7 @@
.box {
height: 416px;
border-radius: 8px;
}
h2 {
@@ -354,17 +262,19 @@
button.create-group {
width: 330px;
height: 96px;
border-radius: 8px;
font-size: 1.5rem;
}
.purple-header {
color: #6133b4;
font-size: 48px;
margin-top: 1em;
margin-top: 16px;
}
.pricing {
margin-top: 2em;
margin-bottom: 4em;
margin-top: 32px;
margin-bottom: 64px;
.dollar, .number, .name {
display: inline-block;
@@ -373,35 +283,41 @@
}
.plus {
font-size: 34px;
font-size: 2.125rem;
color: #a5a1ac;
margin-left: 16px;
margin-right: 16px;
}
.dollar {
margin-bottom: 1.5em;
font-size: 32px;
margin-bottom: 24px;
font-size: 2rem;
font-weight: bold;
}
.name {
font-size: 24px;
margin-bottom: .8em;
margin-left: .5em;
font-size: 1.5rem;
margin-left: 8px;
margin-right: 8px;
}
.number {
font-size: 72px;
font-size: 4.5rem;
font-weight: bolder;
}
}
.payment-options {
margin-bottom: 4em;
margin-bottom: 64px;
h4 {
color: #34313a;
}
.purple-box {
background-color: #4f2a93;
color: #fff;
padding: .5em;
padding: 8px;
border-radius: 8px;
width: 200px;
height: 215px;
@@ -415,7 +331,7 @@
.name {
width: 100px;
margin-left: .3em;
margin-left: 4.8px;
}
.plus {
@@ -440,10 +356,12 @@ import paymentsMixin from '../../mixins/payments';
import { mapState } from '@/libs/store';
import positiveIcon from '@/assets/svg/positive.svg';
import paymentsButtons from '@/components/payments/buttons/list';
import groupPlanCreationModal from '../group-plans/groupPlanCreationModal';
export default {
components: {
paymentsButtons,
groupPlanCreationModal,
},
mixins: [paymentsMixin],
data () {
@@ -492,12 +410,7 @@ export default {
},
methods: {
launchModal () {
this.changePage(this.PAGES.CREATE_GROUP);
this.$root.$emit('bv::show::modal', 'group-plan-modal');
},
changePage (page) {
this.activePage = page;
window.scrollTo(0, 0);
this.$root.$emit('bv::show::modal', 'create-group');
},
createGroup () {
this.changePage(this.PAGES.PAY);

View File

@@ -86,6 +86,7 @@
import throttle from 'lodash/throttle';
import FilterSidebar from '@/components/ui/filterSidebar';
import FilterGroup from '@/components/ui/filterGroup';
import CategoryOptions from '@/../../common/script/content/categoryOptions';
// TODO use checkbox-component to add/remove entries to *Filters, but without the v-model binding
@@ -94,64 +95,7 @@ export default {
data () {
return {
categoryFilters: [],
categoryOptions: [
{
label: 'habitica_official',
key: 'habitica_official',
},
{
label: 'academics',
key: 'academics',
},
{
label: 'advocacy_causes',
key: 'advocacy_causes',
},
{
label: 'creativity',
key: 'creativity',
},
{
label: 'entertainment',
key: 'entertainment',
},
{
label: 'finance',
key: 'finance',
},
{
label: 'health_fitness',
key: 'health_fitness',
},
{
label: 'hobbies_occupations',
key: 'hobbies_occupations',
},
{
label: 'location_based',
key: 'location_based',
},
{
label: 'mental_health',
key: 'mental_health',
},
{
label: 'getting_organized',
key: 'getting_organized',
},
{
label: 'recovery_support_groups',
key: 'recovery_support_groups',
},
{
label: 'spirituality',
key: 'spirituality',
},
{
label: 'time_management',
key: 'time_management',
},
],
categoryOptions: CategoryOptions,
roleFilters: [],
roleOptions: [
{

View File

@@ -247,9 +247,6 @@
<li v-once>
{{ $t('sleepBullet3') }}
</li>
<li v-once>
{{ $t('sleepBullet4') }}
</li>
</ul>
<button
v-if="!user.preferences.sleep"
@@ -270,7 +267,7 @@
</div>
</div>
<div class="px-4">
<sidebar-section :title="$t('staffAndModerators')">
<sidebar-section :title="$t('staff')">
<div class="row">
<div
v-for="user in staff"
@@ -292,19 +289,6 @@
class="svg-icon staff-icon"
v-html="icons.tierStaff"
></div>
<div
v-if="user.type === 'Moderator' && user.name !== 'It\'s Bailey'"
class="svg-icon mod-icon"
v-html="icons.tierMod"
></div>
<div
v-if="user.name === 'It\'s Bailey'"
class="svg-icon npc-icon"
v-html="icons.tierNPC"
></div>
</div>
<div class="type">
{{ user.type }}
</div>
</div>
</div>
@@ -342,6 +326,7 @@
<li>
<a
href
:style="glossary-link"
v-html="$t('glossary')"
></a>
</li>
@@ -536,6 +521,21 @@
margin-left: .5em;
}
// formats the report a bug link to match the others
a:not([href]) {
&:not([role=button]) {
color: #007bff;
text-decoration: none;
}
}
a:not([href]):hover {
&:not([role=button]) {
color: #0056b3;
text-decoration: underline;
}
}
.tier1-icon, .tier2-icon {
width: 11px;
}

View File

@@ -23,7 +23,14 @@ export default {
props: ['notification', 'canRemove'],
methods: {
action () {
this.$router.push({ name: 'tasks' });
if (this.notification.data.groupId) {
this.$router.push({
name: 'groupPlanDetailTaskInformation',
params: { groupId: this.notification.data.groupId },
});
} else {
this.$router.push({ name: 'tasks' });
}
},
},
};

View File

@@ -129,8 +129,6 @@ import GUILD_INVITATION from './notifications/guildInvitation';
import PARTY_INVITATION from './notifications/partyInvitation';
import CHALLENGE_INVITATION from './notifications/challengeInvitation';
import QUEST_INVITATION from './notifications/questInvitation';
import GROUP_TASK_APPROVAL from './notifications/groupTaskApproval';
import GROUP_TASK_APPROVED from './notifications/groupTaskApproved';
import GROUP_TASK_ASSIGNED from './notifications/groupTaskAssigned';
import GROUP_TASK_CLAIMED from './notifications/groupTaskClaimed';
import UNALLOCATED_STATS_POINTS from './notifications/unallocatedStatsPoints';
@@ -155,8 +153,6 @@ export default {
PARTY_INVITATION,
CHALLENGE_INVITATION,
QUEST_INVITATION,
GROUP_TASK_APPROVAL,
GROUP_TASK_APPROVED,
GROUP_TASK_ASSIGNED,
GROUP_TASK_CLAIMED,
UNALLOCATED_STATS_POINTS,
@@ -182,7 +178,7 @@ export default {
openStatus: undefined,
actionableNotifications: [
'GUILD_INVITATION', 'PARTY_INVITATION', 'CHALLENGE_INVITATION',
'QUEST_INVITATION', 'GROUP_TASK_APPROVED',
'QUEST_INVITATION',
],
// A list of notifications handled by this component,
// listed in the order they should appear in the notifications panel.
@@ -196,8 +192,6 @@ export default {
'CHALLENGE_INVITATION',
'QUEST_INVITATION',
'GROUP_TASK_ASSIGNED',
'GROUP_TASK_APPROVAL',
'GROUP_TASK_APPROVED',
'GROUP_TASK_CLAIMED',
'NEW_MYSTERY_ITEMS',
'CARD_RECEIVED',

View File

@@ -787,18 +787,15 @@ export default {
if (sortBy === 'sortByColor') {
groupKey = 'potionKey';
} else if (sortBy === 'AZ') {
groupKey = '';
groupKey = i => i.eggName[0];
} else if (sortBy === 'sortByHatchable') {
groupKey = i => (i.isHatchable() ? 0 : 1);
}
const groupedPets = groupBy(pets, groupKey);
// Pets are rendered as grouped "rows". Count helps decide if show more button is necessary.
if (sortBy === 'AZ') {
this.petRowCount[animalGroup.key] = 1;
} else {
this.petRowCount[animalGroup.key] = Object.keys(groupedPets).length;
}
this.petRowCount[animalGroup.key] = Object.keys(groupedPets).length;
return groupedPets;
},
mounts (animalGroup, hideMissing, sortBy, searchText) {
@@ -814,14 +811,12 @@ export default {
if (sortBy === 'sortByColor') {
groupKey = 'potionKey';
} else if (sortBy === 'AZ') {
groupKey = '';
groupKey = i => i.eggName[0];
}
const groupedMounts = groupBy(mounts, groupKey);
if (sortBy === 'AZ') {
this.mountRowCount[animalGroup.key] = 1;
} else {
this.mountRowCount[animalGroup.key] = Object.keys(groupedMounts).length;
}
this.mountRowCount[animalGroup.key] = Object.keys(groupedMounts).length;
return groupedMounts;
},
// Actions

View File

@@ -32,11 +32,12 @@
/>
<onboarding-complete />
<first-drops />
<group-plans-update />
</div>
</template>
<style lang='scss'>
.introjs-helperNumberLayer, .introjs-bullets {
.introjs-helperNumberLayer, .introjs-bullets, .introjs-skipbutton {
display: none;
}
@@ -62,9 +63,9 @@
.npc_justin_textbox {
position: absolute;
right: 1em;
top: -3.6em;
top: -55px;
width: 48px;
height: 52px;
height: 51px;
background-image: url('~@/assets/images/justin_textbox.png');
}
}
@@ -95,7 +96,7 @@
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12) !important;
}
.introjs-skipbutton.btn-primary, .introjs-donebutton.btn-primary {
.introjs-donebutton.btn-primary {
color: #fff;
}
</style>
@@ -142,6 +143,7 @@ import loginIncentives from './achievements/login-incentives';
import onboardingComplete from './achievements/onboardingComplete';
import verifyUsername from './settings/verifyUsername';
import firstDrops from './achievements/firstDrops';
import groupPlansUpdate from './group-plans/groupPlansUpdateModal';
const NOTIFICATIONS = {
// general notifications
@@ -239,6 +241,14 @@ const NOTIFICATIONS = {
achievement: 'mountColorAchievs',
},
},
ACHIEVEMENT_PET_SET_COMPLETE: {
achievement: true,
label: $t => `${$t('achievement')}: ${$t('achievementPetSetComplete')}`,
modalId: 'generic-achievement',
data: {
achievement: 'petSetCompleteAchievs',
},
},
};
export default {
@@ -268,6 +278,7 @@ export default {
genericAchievement,
onboardingComplete,
firstDrops,
groupPlansUpdate,
},
mixins: [notifications, guide],
data () {
@@ -310,6 +321,7 @@ export default {
'ACHIEVEMENT_ANIMAL_SET',
'ACHIEVEMENT_PET_COLOR',
'ACHIEVEMENT_MOUNT_COLOR',
'ACHIEVEMENT_PET_SET_COMPLETE',
].forEach(type => {
handledNotifications[type] = true;
});
@@ -612,7 +624,7 @@ export default {
const yesterUtcOffset = yesterDay.utcOffset();
dailys.forEach(task => {
if (task && task.group.approval && task.group.approval.requested) return;
if (task.group && task.group.id) return;
if (task.completed) return;
const due = shouldDo(yesterDay, task, { timezoneUtcOffset: yesterUtcOffset });
if (task.yesterDaily && due) this.yesterDailies.push(task);
@@ -769,6 +781,15 @@ export default {
Vue.set(this.user.achievements, achievement, true);
break;
}
case 'ACHIEVEMENT_PET_SET_COMPLETE': {
const { achievement } = notification.data;
const upperCaseAchievement = achievement.charAt(0).toUpperCase() + achievement.slice(1);
const achievementTitleKey = `achievement${upperCaseAchievement}`;
NOTIFICATIONS.ACHIEVEMENT_PET_SET_COMPLETE.label = $t => `${$t('achievement')}: ${$t(achievementTitleKey)}`;
this.showNotificationWithModal(notification);
Vue.set(this.user.achievements, achievement, true);
break;
}
case 'ACHIEVEMENT': { // generic achievement
const { achievement } = notification.data;
const upperCaseAchievement = achievement.charAt(0).toUpperCase() + achievement.slice(1);

View File

@@ -196,7 +196,7 @@ export default {
appState.group = pick(this.amazonPayments.groupToCreate, ['_id', 'memberCount', 'name']);
} else {
appState.newGroup = false;
appState.group = pick(this.amazonPayments.group, ['_id', 'memberCount', 'name']);
appState.group = pick(this.amazonPayments.group, ['_id', 'memberCount', 'name', 'type']);
}
} else if (paymentType && paymentType.indexOf('gift-') === 0) {
appState.gift = this.amazonPayments.gift;

View File

@@ -153,14 +153,6 @@
</span>
</div>
</div>
<div class="row text-center">
<h4
v-once
class="col-12 text-payment mb-3"
>
{{ $t('choosePaymentMethod') }}
</h4>
</div>
<payments-buttons
:disabled="!selectedGemsBlock"
:stripe-fn="() => redirectToStripe({ gemsBlock: selectedGemsBlock })"
@@ -169,6 +161,19 @@
})"
:amazon-data="{type: 'single', gemsBlock: selectedGemsBlock}"
/>
<div
v-if="eventName === 'fall_extra_gems' || eventName === 'spooky_extra_gems'"
class="d-flex flex-column justify-content-center"
>
<h4 class="mt-3 mx-auto"> {{ $t('howItWorks') }}</h4>
<small class="text-center">
{{ $t('gemSaleHow', { eventStartMonth, eventStartOrdinal, eventEndOrdinal }) }}
</small>
<h4 class="mt-3 mx-auto"> {{ $t('limitations') }}</h4>
<small class="text-center">
{{ $t('gemSaleLimitations', { eventStartMonth, eventStartOrdinal, eventEndOrdinal }) }}
</small>
</div>
</div>
</b-modal>
</div>
@@ -178,6 +183,13 @@
@import '~@/assets/scss/colors.scss';
#buy-gems {
small {
color: $gray-100;
font-size: 12px;
margin-left: 20px;
margin-right: 20px;
}
.close-icon svg path {
stroke: $purple-400;
}
@@ -347,8 +359,8 @@
.svg-icon.check {
color: $purple-400;
width: 0.77rem;
height: 0.615rem;
width: 16px;
height: 16px;
}
.text-leadin {
@@ -423,6 +435,15 @@ export default {
}
return '';
},
eventStartMonth () {
return moment(this.currentEvent.start).format('MMMM');
},
eventStartOrdinal () {
return moment(this.currentEvent.start).format('Do');
},
eventEndOrdinal () {
return moment(this.currentEvent.end).format('Do');
},
isGemsPromoActive () {
const currEvt = this.currentEvent;
if (currEvt && currEvt.gemsPromo && moment().isBefore(currEvt.end)) {

View File

@@ -75,6 +75,7 @@
v-show="selectedPage === 'subscription'"
class="subscribe-option"
:userReceivingGift="userReceivingGift"
:receiverName="receiverName"
/>
<!-- gem block -->
@@ -648,6 +649,7 @@ export default {
},
},
giftReceiver: this.receiverName,
toUserId: this.userReceivingGift._id,
});
}, 500);
},

View File

@@ -1,11 +1,22 @@
<template>
<b-modal
id="payments-success-modal"
:title="$t('accountSuspendedTitle')"
:hide-footer="isFromBalance"
:modal-class="isFromBalance ? ['modal-hidden-footer'] : []"
:hide-footer="isNewGroup || isGems || isSubscription"
:modal-class="isNewGroup || isGems || isSubscription
? ['modal-hidden-footer'] : []"
>
<!-- HEADER -->
<div slot="modal-header">
<div
class="modal-close"
@click="close()"
>
<div
class="icon-close"
v-html="icons.close"
>
</div>
</div>
<div class="check-container d-flex align-items-center justify-content-center">
<div
v-once
@@ -13,19 +24,13 @@
v-html="icons.check"
></div>
</div>
<h2>{{ $t(isFromBalance ? 'success' : 'paymentSuccessful') }}</h2>
</div>
<div slot="modal-footer">
<div
v-once
class="small-text"
>
{{ $t('giftSubscriptionText4') }}
</div>
<h2>{{ $t(isGemsBalance ? 'success' : 'paymentSuccessful') }}</h2>
</div>
<!-- BODY -->
<div class="row">
<div class="col-12 modal-body-col">
<template v-if="paymentData.paymentType === 'gems'">
<!-- buy gems for self -->
<template v-if="isGems">
<strong v-once>{{ $t('paymentYouReceived') }}</strong>
<div class="details-block gems">
<div
@@ -36,9 +41,9 @@
<span>{{ paymentData.gemsBlock.gems }}</span>
</div>
</template>
<!-- buy gems to someone else OR send gems from balance-->
<template
v-if="paymentData.paymentType === 'gift-gems'
|| paymentData.paymentType === 'gift-gems-balance'"
v-if="isGiftGems || isGemsBalance"
>
<span v-html="$t('paymentYouSentGems', {name: paymentData.giftReceiver})"></span>
<div class="details-block gems">
@@ -50,13 +55,17 @@
<span>{{ paymentData.gift.gems.amount }}</span>
</div>
</template>
<!-- give gift subscription (non-recurring)-->
<template v-if="paymentData.paymentType === 'gift-subscription'">
<span
v-html="$t('paymentYouSentSubscription', {
name: paymentData.giftReceiver, months: paymentData.subscription.months})"
></span>
<div>
<span
v-html="$t('paymentYouSentSubscription', {
name: paymentData.giftReceiver, months: paymentData.subscription.months})"
></span>
</div>
</template>
<template v-if="paymentData.paymentType === 'subscription'">
<!-- buy self subscription (recurring) -->
<template v-if="isSubscription">
<strong v-once>{{ $t('nowSubscribed') }}</strong>
<div class="details-block">
<span
@@ -65,48 +74,238 @@
></span>
</div>
</template>
<template v-if="paymentData.paymentType === 'groupPlan'">
<!-- group plan new or upgraded -->
<template v-if="isGroupPlan">
<span
v-html="$t(paymentData.newGroup
v-html="$t(isNewGroup
? 'groupPlanCreated' : 'groupPlanUpgraded', {groupName: paymentData.group.name})"
></span>
<div class="details-block">
<span
v-html="$t('paymentSubBilling', {
amount: groupPlanCost, months: paymentData.subscription.months})"
></span>
<div
v-if="isGroupPlan"
class=""
>
<div class="details-block group-billing-date">
<span
v-html="$t('groupsPaymentSubBilling', { renewalDate })"
>
</span>
</div>
<div class="small-text group-auto-renew">
<span
v-once
>{{ $t('groupsPaymentAutoRenew') }}
</span>
</div>
</div>
</template>
<!-- buy self subscription auto renew -->
<template
v-if="paymentData.paymentType === 'groupPlan'
|| paymentData.paymentType === 'subscription'"
v-if="isSubscription"
>
<span
v-once
class="small-text auto-renew"
>{{ $t('paymentAutoRenew') }}</span>
</template>
<!-- buttons for subscriptions / new Group / buy Gems for self -->
<button
v-if="isNewGroup || isGems || isSubscription"
v-once
class="btn btn-primary"
@click="close()"
@click="submit()"
>
{{ $t('onwards') }}
</button>
</div>
</div>
<!-- FOOTER -->
<div slot="modal-footer">
<!-- gift gems balance & buy, gift subscription -->
<div
v-if="isGemsBalance || isGiftGems || isGiftSubscription"
class="message mx-auto"
>
<lockable-label
:text="$t('sendGiftLabel')"
class="mx-auto label-text"
/>
<textarea
v-model="gift.message"
class="form-control mx-auto"
:placeholder="$t('sendGiftMessagePlaceholder')"
></textarea>
<button
:disabled="!gift.message || sendingInProgress"
class="btn btn-primary mx-auto"
@click="sendMessage()"
>
{{ $t('sendMessage') }}
</button>
</div>
<!-- upgradedGroup -->
<div
v-else-if="isUpgradedGroup"
class="demographics d-flex flex-column justify-content-center"
>
<lockable-label
:text="$t('groupUse')"
class="mx-auto label-text"
/>
<select-translated-array
:items="[
'groupParentChildren',
'groupCouple',
'groupFriends',
'groupCoworkers',
'groupManager',
'groupTeacher'
]"
class="group-input"
:placeholder="'groupUseDefault'"
:value="upgradedGroup.demographics"
@select="upgradedGroup.demographics = $event"
/>
<button
v-if="!paymentData.newGroup"
class="btn btn-primary mx-auto"
:disabled="!upgradedGroup.demographics"
@click="submit()"
>
{{ $t('submit') }}
</button>
</div>
</div>
</b-modal>
</template>
<style lang="scss">
@import '~@/assets/scss/colors.scss';
#payments-success-modal .modal-md {
max-width: 20.5rem;
}
#payments-success-modal {
.modal-md {
max-width: 448px;
min-width: 330px;
#payments-success-modal .modal-content {
background: transparent;
.modal-close {
position: absolute;
right: 16px;
top: 16px;
cursor: pointer;
.icon-close {
width: 18px;
height: 18px;
vertical-align: middle;
& svg path {
fill: $green-1;
}
& :hover {
fill: $green-1;
}
}
}
.modal-content {
background: transparent;
}
.modal-header {
justify-content: center;
padding-top: 24px;
padding-bottom: 0px;
background: $green-100;
border-top-right-radius: 8px;
border-top-left-radius: 8px;
border-bottom: none;
h2 {
color: $green-1;
}
.check-container {
width: 64px;
height: 64px;
border-radius: 50%;
background: $green-1;
margin: 0 auto;
margin-bottom: 16px;
}
.check {
width: 35.1px;
height: 28px;
color: $white;
}
}
.modal-body {
padding: 16px 32px 24px 32px;
background: $white;
.modal-body-col {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
.btn.btn-primary {
margin-top: 24px;
}
}
.details-block {
background: $gray-700;
border-radius: 4px;
padding: 8px 16px;
margin-top: 16px;
display: inline-flex;
flex-direction: row;
text-align: center;
&.gems {
padding: 12px 16px 12px 20px;
color: $green-10;
font-size: 24px;
font-weight: bold;
line-height: 1.33;
.svg-icon {
margin-right: 8px;
width: 32px;
height: 32px;
}
}
}
.auto-renew {
margin-top: 16px;
color: $orange-10;
font-style: normal;
}
.group-auto-renew {
margin: 12px 20px -8px 20px;
color: $yellow-5;
font-style: normal;
}
.group-billing-date {
width: 269px;
}
}
.modal-footer {
background: $gray-700;
border-bottom-right-radius: 8px;
border-bottom-left-radius: 8px;
justify-content: center;
border-top: none;
.small-text {
font-style: normal;
}
}
}
}
#payments-success-modal.modal-hidden-footer .modal-body {
@@ -114,119 +313,123 @@
border-bottom-left-radius: 8px;
}
#payments-success-modal .modal-header {
justify-content: center;
padding-top: 24px;
padding-bottom: 0px;
background: $green-100;
border-top-right-radius: 8px;
border-top-left-radius: 8px;
border-bottom: none;
.demographics {
background-color: $gray-700;
h2 {
color: white;
.label-text {
margin-bottom: 20px;
}
.check-container {
width: 64px;
height: 64px;
border-radius: 50%;
background: #1CA372;
margin: 0 auto;
margin-bottom: 16px;
.group-input {
width: 400px !important;
margin-top: -24px !important;
}
.check {
width: 35.1px;
height: 28px;
color: white;
.btn {
margin-top: 0px;
width: 77px;
margin-bottom: 20px;
}
}
.message {
margin-bottom: 8px;
width: 378px;
display: flex;
flex-direction: column;
#payments-success-modal .modal-body {
padding-top: 16px;
padding-bottom: 24px;
background: white;
.modal-body-col {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
.btn.btn-primary {
margin-top: 24px;
}
}
.details-block {
background: $gray-700;
border-radius: 4px;
padding: 8px 24px;
margin-top: 16px;
display: flex;
flex-direction: row;
text-align: center;
&.gems {
padding: 12px 16px 12px 20px;
color: $green-10;
font-size: 24px;
font-weight: bold;
line-height: 1.33;
.svg-icon {
margin-right: 8px;
width: 32px;
height: 32px;
}
}
}
.auto-renew {
margin-top: 16px;
color: $orange-10;
font-style: normal;
}
}
#payments-success-modal .modal-footer {
background: $gray-700;
border-bottom-right-radius: 8px;
border-bottom-left-radius: 8px;
justify-content: center;
border-top: none;
.small-text {
font-style: normal;
textarea.form-control {
height: 56px;
margin: 0 24px 24px 24px;
font-size: 0.875rem;
}
}
</style>
<style lang="scss">
@import '~@/assets/scss/mixins.scss';
</style>
<script>
// icons
import checkIcon from '@/assets/svg/check.svg';
import gemIcon from '@/assets/svg/gem.svg';
import closeIcon from '@/assets/svg/close.svg';
// components
import { mapState } from '@/libs/store';
import subscriptionBlocks from '@/../../common/script/content/subscriptionBlocks';
import selectTranslatedArray from '@/components/tasks/modal-controls/selectTranslatedArray';
import lockableLabel from '@/components/tasks/modal-controls/lockableLabel';
// mixins
import notificationsMixin from '@/mixins/notifications';
import paymentsMixin from '@/mixins/payments';
// analytics
import * as Analytics from '@/libs/analytics';
export default {
components: {
selectTranslatedArray,
lockableLabel,
},
mixins: [
paymentsMixin,
notificationsMixin,
],
data () {
return {
icons: Object.freeze({
check: checkIcon,
gem: gemIcon,
close: closeIcon,
}),
paymentData: {},
upgradedGroup: {
name: '',
demographics: null,
},
sendingInProgress: false,
gift: {
message: '',
},
receiverName: {
name: null,
uuid: null,
},
};
},
computed: {
...mapState({ user: 'user.data', group: 'group.data' }),
groupPlanCost () {
const sub = this.paymentData.subscription;
const memberCount = this.paymentData.group.memberCount || 1;
return sub.price + 3 * (memberCount - 1);
},
isFromBalance () {
isGemsBalance () {
return this.paymentData.paymentType === 'gift-gems-balance';
},
isGems () {
return this.paymentData.paymentType === 'gems';
},
isGiftGems () {
return this.paymentData.paymentType === 'gift-gems';
},
isGiftSubscription () {
return this.paymentData.paymentType === 'gift-subscription';
},
isSubscription () {
return this.paymentData.paymentType === 'subscription';
},
isGroupPlan () {
return this.paymentData.paymentType === 'groupPlan';
},
isUpgradedGroup () {
return this.paymentData.paymentType === 'groupPlan' && !this.paymentData.newGroup;
},
isNewGroup () {
return this.paymentData.paymentType === 'groupPlan' && this.paymentData.newGroup;
},
},
mounted () {
this.$root.$on('habitica:payment-success', data => {
@@ -242,7 +445,30 @@ export default {
this.$root.$off('habitica:payments-success');
},
methods: {
async sendMessage () {
this.sendingInProgress = true;
await this.$store.dispatch('members:sendPrivateMessage', {
message: this.gift.message,
toUserId: this.paymentData.gift.uuid || this.paymentData.toUserId,
});
this.close();
},
close () {
this.gift.message = '';
this.sendingInProgress = false;
this.$root.$emit('bv::hide::modal', 'payments-success-modal');
},
submit () {
if (this.paymentData.group && !this.paymentData.newGroup) {
Analytics.track({
hitType: 'event',
eventName: 'group plan upgrade',
eventAction: 'group plan upgrade',
eventCategory: 'behavior',
demographics: this.upgradedGroup.demographics,
type: this.paymentData.group.type,
}, { trackOnClient: true });
}
this.paymentData = {};
this.$root.$emit('bv::hide::modal', 'payments-success-modal');
},

View File

@@ -387,9 +387,7 @@
{{ $t('saveAndConfirm') }}
</button>
</div>
<h5
v-if="user.auth.local.email"
>
<h5 v-if="user.auth.local.has_password">
{{ $t('changeEmail') }}
</h5>
<div

View File

@@ -125,6 +125,10 @@ export default {
type: Object,
default () {},
},
receiverName: {
type: String,
default: '',
},
},
data () {
return {
@@ -135,7 +139,6 @@ export default {
type: 'subscription',
subscription: { key: 'basic_earned' },
},
receiverName: '',
};
},
computed: {

View File

@@ -385,7 +385,6 @@ import EquipmentAttributesGrid from '../inventory/equipment/attributesGrid.vue';
import Item from '@/components/inventory/item';
import Avatar from '@/components/avatar';
import seasonalShopConfig from '@/../../common/script/libs/shops-seasonal.config';
import { drops as dropEggs } from '@/../../common/script/content/eggs';
import { drops as dropPotions } from '@/../../common/script/content/hatching-potions';
@@ -438,7 +437,6 @@ export default {
selectedAmountToBuy: 1,
isPinned: false,
endDate: seasonalShopConfig.dateRange.end,
};
},
computed: {
@@ -489,6 +487,9 @@ export default {
nonSubscriberHourglasses () {
return (!this.user.purchased.plan.customerId && !this.user.purchased.plan.consecutive.trinkets && this.getPriceClass() === 'hourglasses');
},
endDate () {
return moment(this.item.event.end);
},
},
watch: {
item: function itemChanged () {

View File

@@ -8,16 +8,6 @@
:popover-position="'top'"
@click="itemSelected(item)"
>
<span slot="popoverContent">
<strong v-if="item.key === 'gem' && gemsLeft === 0">{{ $t('maxBuyGems') }}</strong>
<h4 class="popover-content-title">{{ item.text }}</h4>
<div
v-if="item.event"
class="mt-2"
>
{{ limitedString }}
</div>
</span>
<template
slot="itemBadge"
slot-scope="ctx"
@@ -32,11 +22,9 @@
import _filter from 'lodash/filter';
import _sortBy from 'lodash/sortBy';
import _map from 'lodash/map';
import moment from 'moment';
import { mapState } from '@/libs/store';
import pinUtils from '@/mixins/pinUtils';
import planGemLimits from '@/../../common/script/libs/planGemLimits';
import seasonalShopConfig from '@/../../common/script/libs/shops-seasonal.config';
import ShopItem from '../shopItem';
import CategoryItem from './categoryItem';
@@ -48,12 +36,6 @@ export default {
},
mixins: [pinUtils],
props: ['hideLocked', 'hidePinned', 'searchBy', 'sortBy', 'category'],
data () {
return {
timer: '',
limitedString: '',
};
},
computed: {
...mapState({
content: 'content',
@@ -106,43 +88,10 @@ export default {
return result;
},
},
mounted () {
this.countdownString();
this.timer = setInterval(this.countdownString, 1000);
},
beforeDestroy () {
this.cancelAutoUpdate();
},
methods: {
itemSelected (item) {
this.$root.$emit('buyModal::showItem', item);
},
countdownString () {
const diffDuration = moment.duration(moment(seasonalShopConfig.dateRange.end).diff(moment()));
if (diffDuration.asSeconds() <= 0) {
this.limitedString = this.$t('noLongerAvailable');
} else if (diffDuration.days() > 0 || diffDuration.months() > 0) {
this.limitedString = this.$t('limitedAvailabilityDays', {
days: moment(seasonalShopConfig.dateRange.end).diff(moment(), 'days'),
hours: diffDuration.hours(),
minutes: diffDuration.minutes(),
});
} else if (diffDuration.asMinutes() > 2) {
this.limitedString = this.$t('limitedAvailabilityHours', {
hours: diffDuration.hours(),
minutes: diffDuration.minutes(),
});
} else {
this.limitedString = this.$t('limitedAvailabilityMinutes', {
minutes: diffDuration.minutes(),
seconds: diffDuration.seconds(),
});
}
},
cancelAutoUpdate () {
clearInterval(this.timer);
},
},
};
</script>

View File

@@ -263,8 +263,8 @@
</style>
<script>
import moment from 'moment';
import { mapState } from '@/libs/store';
import seasonalShopConfig from '@/../../common/script/libs/shops-seasonal.config';
import svgClock from '@/assets/svg/clock.svg';
import svgClose from '@/assets/svg/close.svg';
@@ -319,7 +319,6 @@ export default {
isPinned: false,
selectedAmountToBuy: 1,
endDate: seasonalShopConfig.dateRange.end,
};
},
computed: {
@@ -343,6 +342,9 @@ export default {
if (this.priceType === 'hourglasses') return this.icons.hourglass;
return this.icons.gem;
},
endDate () {
return moment(this.item.event.end);
},
},
watch: {
item: function itemChanged () {

View File

@@ -402,6 +402,8 @@ import _sortBy from 'lodash/sortBy';
import _throttle from 'lodash/throttle';
import _groupBy from 'lodash/groupBy';
import _map from 'lodash/map';
import _each from 'lodash/each';
import * as stopword from 'stopword/dist/stopword.esm.mjs';
import { mapState } from '@/libs/store';
import ShopItem from '../shopItem';
@@ -426,6 +428,51 @@ import SelectTranslatedArray from '@/components/tasks/modal-controls/selectTrans
import QuestPopover from './questPopover';
import { worldStateMixin } from '@/mixins/worldState';
function splitMultipleDelims (text, delims) {
const omniDelim = 'θνι';
let workingText = text;
for (const delim of delims) {
workingText = workingText.replace(new RegExp(delim, 'g'), omniDelim);
}
return workingText.split(omniDelim);
}
function removeStopwordsFromText (text, language) {
// list of supported languages https://www.npmjs.com/package/stopword
const langs = {
bg: stopword.bul,
cs: stopword.ces,
da: stopword.dan,
de: stopword.deu,
en: stopword.eng,
en_GB: stopword.eng,
'en@pirate': stopword.eng.concat(["th'"]),
es: stopword.spa,
es_419: stopword.spa,
fr: stopword.fra,
he: stopword.heb,
hu: stopword.hun,
id: stopword.ind,
it: stopword.ita,
ja: stopword.jpn,
nl: stopword.nld,
pl: stopword.pol,
pt: stopword.por,
pt_BR: stopword.porBr,
ro: stopword.ron,
ru: stopword.rus,
sk: stopword.slv,
// sr: stopword.,
sv: stopword.swe,
tr: stopword.tur,
uk: stopword.ukr,
zh: stopword.zho,
zh_TW: stopword.zho,
};
const splitText = splitMultipleDelims(text, [' ', "'"]);
return stopword.removeStopwords(splitText, langs[language] || stopword.eng).join(' ').toLowerCase();
}
export default {
components: {
QuestPopover,
@@ -539,7 +586,14 @@ export default {
switch (sortBy) { // eslint-disable-line default-case
case 'AZ': {
result = _sortBy(result, ['text']);
if (category.identifier === 'pet' || category.identifier === 'hatchingPotion') {
_each(result, item => {
item.sortText = removeStopwordsFromText(item.text, this.user.preferences.language);
});
result = _sortBy(result, ['sortText']);
} else {
result = _sortBy(result, ['text']);
}
break;
}

View File

@@ -31,6 +31,11 @@
class="svg-icon inline lock"
v-html="icons.lock"
></span>
<span
v-if="item.completed"
class="svg-icon inline check"
v-html="icons.check"
></span>
<span
v-if="item.isSuggested"
class="suggestedDot"
@@ -200,6 +205,15 @@
color: $gray-200;
}
span.svg-icon.inline.check {
height: 16px;
width: 16px;
position: absolute;
left: 4px;
top: 4px;
color: $gray-200;
}
span.badge.badge-round.badge-item.badge-clock {
height: 24px;
width: 24px;
@@ -246,6 +260,7 @@ import svgGem from '@/assets/svg/gem.svg';
import svgGold from '@/assets/svg/gold.svg';
import svgHourglasses from '@/assets/svg/hourglass.svg';
import svgLock from '@/assets/svg/lock.svg';
import svgCheck from '@/assets/svg/check.svg';
import svgClock from '@/assets/svg/clock.svg';
import EquipmentAttributesPopover from '@/components/inventory/equipment/attributesPopover';
@@ -297,6 +312,7 @@ export default {
gems: svgGem,
gold: svgGold,
lock: svgLock,
check: svgCheck,
hourglasses: svgHourglasses,
clock: svgClock,
}),
@@ -350,6 +366,7 @@ export default {
'highlight-border': this.highlightBorder,
suggested: this.item.isSuggested,
locked: this.item.locked,
completed: this.item.completed,
};
},
countdownString () {

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