Compare commits

..

161 Commits

Author SHA1 Message Date
Hafiz
872182ce19 Responsive Layout for Equipment Containers
- Added responsive CSS for mobile (<768px) and tablet (769px-1024px)
- Implemented flex-wrap layout that automatically stacks items in rows of 4 on smaller
2025-08-13 12:16:57 -05:00
Hafiz
9a1fb18959 Merge remote-tracking branch 'origin/develop' into qa/bat 2025-08-12 09:46:07 -05:00
Kalista Payne
876d5a67d6 5.38.2 2025-08-08 14:04:19 -05:00
Kalista Payne
3078af8f2a fix(apple): don't run auth middleware during redirect 2025-08-08 14:04:13 -05:00
Weblate
dad1440138 Translated using Weblate (German)
Currently translated at 99.4% (185 of 186 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Dutch)

Currently translated at 78.0% (2643 of 3385 strings)

Translated using Weblate (Dutch)

Currently translated at 40.8% (100 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 89.9% (233 of 259 strings)

Translated using Weblate (Dutch)

Currently translated at 67.5% (175 of 259 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 20.8% (51 of 245 strings)

Translated using Weblate (Turkish)

Currently translated at 65.9% (60 of 91 strings)

Translated using Weblate (Turkish)

Currently translated at 65.9% (60 of 91 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 17.9% (44 of 245 strings)

Co-authored-by: FingerTiao <787170918@qq.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Karmelkowy <kicimeow.karmelio@gmail.com>
Co-authored-by: Linsey Dunya Pastoor <sekai.creations@gmail.com>
Co-authored-by: Mete Olmez <metezori27@gmail.com>
Co-authored-by: Sefa Uğurlu <ugurlusefa2@gmail.com>
Co-authored-by: Summer_GUI <heyang94@163.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: innnko <ayakabooker@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/death/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/nl/
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/es/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/pl/
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Communityguidelines
Translation: Habitica/Contrib
Translation: Habitica/Death
Translation: Habitica/Defaulttasks
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Settings
2025-08-08 10:12:25 +02:00
Phillip Thelen
12773d539e Add interface to block ip-addresses or clients due to abuse (#15484)
* Read IP blocks from database

* begin building general blocking solution

* add new frontend files

* Add UI for managing blockers

* correctly reset local data after creating blocker

* Tweak wording

* Add UI for managing blockers

* restructure admin pages

* improve test coverage

* Improve blocker UI

* add blocker to block emails from registration

* lint fix

* fix

* lint fixes

* fix import

* add new permission for managing blockers

* improve permission check

* fix managing permissions from admin

* improve navbar display for non fullAccess admin

* update block error strings

* lint fix

* add option to errorHandler to skip logging

* validate blocker value during input

* improve blocker form display

* chore(subproj): reconcile habitica-images

* fix(scripts): use same Mongo version for dev/test

* fix(whitespace): eof

* documentation improvements

* remove nconf import

* remove old test

---------

Co-authored-by: Kalista Payne <kalista@habitica.com>
Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-08-06 15:08:07 -05:00
Fiz
ae4130b108 Add backend support for Hydra mount (#15482)
* chore: update time travelers shop to display seasonal backgrounds

* chore: update time travelers banner (note CSS borken rn)

* chore: fix borken CSS and update logic in shop

* chore: added isSubscribed function, not working

* chore: isSubscribed working but no bg for subscribers

* chore: logic and css updates

* chore: update habitica-images

* chore: add check for trinket

* chore: more time traveler shop logicking

* Add backend support for Hydra mount

- Add Dragon-Hydra to special mounts in stable.js
  - Configure as contributor level 7 reward with canFind: true
  - Add GIF format support for mount sprites
  - Enable admin panel granting capability

* Fix Vue template errors in timeTravelers component

* Fix duplicate template block in timeTravelers component

* add CSS for Hydra mount GIF sprites

Added CSS rules for Mount_Head_Dragon-Hydra and Mount_Body_Dragon-Hydra GIF sprites

* Remove the separate Hydra mount dimension declaration

---------

Co-authored-by: CuriousMagpie <eilatan@gmail.com>
2025-08-05 15:12:44 -05:00
Kalista Payne
ad0614282e 5.38.1 2025-08-05 14:31:05 -05:00
Phillip Thelen
2ea0b64603 improve blocker form display 2025-08-05 14:57:25 +02:00
Phillip Thelen
bd1aa1e417 validate blocker value during input 2025-08-05 14:45:23 +02:00
Weblate
5a7704aed7 Translated using Weblate (Chinese (Traditional))
Currently translated at 17.1% (42 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 16.7% (41 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 16.3% (40 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Japanese)

Currently translated at 98.8% (425 of 430 strings)

Translated using Weblate (French)

Currently translated at 99.4% (184 of 185 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 15.9% (39 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (3385 of 3385 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (185 of 185 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.8% (3379 of 3385 strings)

Translated using Weblate (Polish)

Currently translated at 95.5% (128 of 134 strings)

Translated using Weblate (Japanese)

Currently translated at 94.7% (254 of 268 strings)

Translated using Weblate (Polish)

Currently translated at 94.0% (126 of 134 strings)

Translated using Weblate (Japanese)

Currently translated at 98.6% (424 of 430 strings)

Translated using Weblate (Japanese)

Currently translated at 98.3% (423 of 430 strings)

Translated using Weblate (Japanese)

Currently translated at 92.5% (798 of 862 strings)

Translated using Weblate (Japanese)

Currently translated at 92.4% (797 of 862 strings)

Translated using Weblate (Japanese)

Currently translated at 90.6% (781 of 862 strings)

Translated using Weblate (Japanese)

Currently translated at 91.9% (3112 of 3385 strings)

Translated using Weblate (Japanese)

Currently translated at 91.9% (3111 of 3385 strings)

Translated using Weblate (Japanese)

Currently translated at 94.0% (174 of 185 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 15.5% (38 of 245 strings)

Translated using Weblate (Japanese)

Currently translated at 91.6% (3104 of 3385 strings)

Translated using Weblate (Japanese)

Currently translated at 93.5% (173 of 185 strings)

Translated using Weblate (Japanese)

Currently translated at 99.6% (279 of 280 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Japanese)

Currently translated at 89.2% (769 of 862 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Japanese)

Currently translated at 94.4% (253 of 268 strings)

Translated using Weblate (Japanese)

Currently translated at 91.8% (170 of 185 strings)

Translated using Weblate (Japanese)

Currently translated at 97.9% (421 of 430 strings)

Translated using Weblate (Japanese)

Currently translated at 91.6% (3104 of 3385 strings)

Translated using Weblate (Japanese)

Currently translated at 93.6% (251 of 268 strings)

Translated using Weblate (Japanese)

Currently translated at 90.8% (168 of 185 strings)

Translated using Weblate (Japanese)

Currently translated at 82.4% (202 of 245 strings)

Translated using Weblate (French)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3385 of 3385 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 15.1% (37 of 245 strings)

Translated using Weblate (French)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Japanese)

Currently translated at 91.3% (3092 of 3385 strings)

Translated using Weblate (Japanese)

Currently translated at 92.5% (248 of 268 strings)

Translated using Weblate (Japanese)

Currently translated at 92.5% (248 of 268 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Korean)

Currently translated at 22.8% (56 of 245 strings)

Translated using Weblate (Korean)

Currently translated at 47.7% (128 of 268 strings)

Translated using Weblate (Croatian)

Currently translated at 45.1% (121 of 268 strings)

Translated using Weblate (Korean)

Currently translated at 71.9% (620 of 862 strings)

Translated using Weblate (Croatian)

Currently translated at 70.6% (609 of 862 strings)

Translated using Weblate (Croatian)

Currently translated at 75.0% (6 of 8 strings)

Translated using Weblate (Korean)

Currently translated at 67.6% (291 of 430 strings)

Translated using Weblate (Korean)

Currently translated at 52.8% (1788 of 3385 strings)

Translated using Weblate (Croatian)

Currently translated at 50.3% (1706 of 3385 strings)

Translated using Weblate (Croatian)

Currently translated at 51.7% (134 of 259 strings)

Translated using Weblate (Czech)

Currently translated at 92.8% (130 of 140 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 86.9% (233 of 268 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Danish)

Currently translated at 92.1% (105 of 114 strings)

Translated using Weblate (Czech)

Currently translated at 89.4% (102 of 114 strings)

Translated using Weblate (Czech)

Currently translated at 83.5% (112 of 134 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 71.6% (308 of 430 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Serbian)

Currently translated at 84.4% (49 of 58 strings)

Translated using Weblate (Bulgarian)

Currently translated at 51.4% (144 of 280 strings)

Translated using Weblate (Swedish)

Currently translated at 66.5% (286 of 430 strings)

Translated using Weblate (Serbian)

Currently translated at 65.5% (282 of 430 strings)

Translated using Weblate (Slovak)

Currently translated at 65.5% (282 of 430 strings)

Translated using Weblate (Romanian)

Currently translated at 66.7% (287 of 430 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Danish)

Currently translated at 66.0% (284 of 430 strings)

Translated using Weblate (Czech)

Currently translated at 69.7% (300 of 430 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (3377 of 3385 strings)

Translated using Weblate (Swedish)

Currently translated at 54.1% (1834 of 3385 strings)

Translated using Weblate (Serbian)

Currently translated at 50.6% (1714 of 3385 strings)

Translated using Weblate (Slovak)

Currently translated at 50.0% (1695 of 3385 strings)

Translated using Weblate (Romanian)

Currently translated at 60.5% (2050 of 3385 strings)

Translated using Weblate (Hebrew)

Currently translated at 38.4% (1301 of 3385 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3385 of 3385 strings)

Translated using Weblate (Danish)

Currently translated at 54.0% (1829 of 3385 strings)

Translated using Weblate (Czech)

Currently translated at 59.6% (2020 of 3385 strings)

Translated using Weblate (Swedish)

Currently translated at 75.6% (140 of 185 strings)

Translated using Weblate (Serbian)

Currently translated at 73.5% (136 of 185 strings)

Translated using Weblate (Slovak)

Currently translated at 84.8% (157 of 185 strings)

Translated using Weblate (Romanian)

Currently translated at 78.9% (146 of 185 strings)

Translated using Weblate (Portuguese)

Currently translated at 82.1% (152 of 185 strings)

Translated using Weblate (Italian)

Currently translated at 91.8% (170 of 185 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (185 of 185 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (185 of 185 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (185 of 185 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (Danish)

Currently translated at 77.2% (143 of 185 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 98.7% (242 of 245 strings)

Translated using Weblate (Czech)

Currently translated at 75.1% (139 of 185 strings)

Translated using Weblate (Bulgarian)

Currently translated at 74.5% (138 of 185 strings)

Translated using Weblate (Czech)

Currently translated at 8.1% (20 of 245 strings)

Translated using Weblate (Swedish)

Currently translated at 72.0% (621 of 862 strings)

Translated using Weblate (Serbian)

Currently translated at 65.1% (562 of 862 strings)

Translated using Weblate (Slovak)

Currently translated at 66.9% (577 of 862 strings)

Translated using Weblate (Romanian)

Currently translated at 77.7% (670 of 862 strings)

Translated using Weblate (Portuguese)

Currently translated at 70.0% (604 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 67.1% (579 of 862 strings)

Translated using Weblate (Italian)

Currently translated at 86.8% (749 of 862 strings)

Translated using Weblate (Indonesian)

Currently translated at 86.0% (742 of 862 strings)

Translated using Weblate (Hebrew)

Currently translated at 66.1% (570 of 862 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 98.0% (845 of 862 strings)

Translated using Weblate (Danish)

Currently translated at 69.9% (603 of 862 strings)

Translated using Weblate (Czech)

Currently translated at 69.7% (601 of 862 strings)

Translated using Weblate (Bulgarian)

Currently translated at 66.3% (572 of 862 strings)

Translated using Weblate (Serbian)

Currently translated at 74.0% (305 of 412 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Danish)

Currently translated at 90.0% (371 of 412 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Swedish)

Currently translated at 53.6% (139 of 259 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Danish)

Currently translated at 62.1% (161 of 259 strings)

Translated using Weblate (Bulgarian)

Currently translated at 54.0% (140 of 259 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 82.8% (222 of 268 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 99.4% (184 of 185 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 98.3% (241 of 245 strings)

Translated using Weblate (Japanese)

Currently translated at 91.3% (3092 of 3385 strings)

Translated using Weblate (Japanese)

Currently translated at 88.4% (237 of 268 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Japanese)

Currently translated at 82.4% (202 of 245 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Japanese)

Currently translated at 87.3% (234 of 268 strings)

Translated using Weblate (Japanese)

Currently translated at 86.4% (160 of 185 strings)

Translated using Weblate (Japanese)

Currently translated at 99.8% (913 of 914 strings)

Translated using Weblate (German)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (German)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (German)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (German)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (German)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3385 of 3385 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (Russian)

Currently translated at 88.5% (248 of 280 strings)

Translated using Weblate (Spanish)

Currently translated at 99.8% (3379 of 3385 strings)

Translated using Weblate (German)

Currently translated at 100.0% (862 of 862 strings)

Co-authored-by: Ayaka Booker <ayakabooker@gmail.com>
Co-authored-by: Chaotic Lawful <habitica@eusebius.fr>
Co-authored-by: FingerTiao <787170918@qq.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Jan Freihöfer <jan.stauch.is@gmail.com>
Co-authored-by: Karmelkowy <kicimeow.karmelio@gmail.com>
Co-authored-by: Lio Zam <zerofux@web.de>
Co-authored-by: Mika <isekai.chr@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Summer_GUI <heyang94@163.com>
Co-authored-by: Vera <verasmolinap@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Zhi Hao Li <zhihaoli000@gmail.com>
Co-authored-by: Zuz Q <zuzannakunik@gmail.com>
Co-authored-by: innnko <ayakabooker@gmail.com>
Co-authored-by: 吳昀錡 <J1120241@gm.fdhs.tyc.edu.tw>
Co-authored-by: 潘致翰 <happyq0908@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/es/
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/ja/
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/character/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/character/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/da/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/death/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/
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/de/
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/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/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/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/da/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/he/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/hr/
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/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/sv/
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/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/da/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/es_419/
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/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/da/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/es/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/da/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/he/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/id/
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/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/da/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/de/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/es/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/en_GB/
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/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/cs/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Death
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2025-08-05 13:02:45 +02:00
Phillip Thelen
7c49b845d6 add option to errorHandler to skip logging 2025-08-04 17:40:26 +02:00
Phillip Thelen
1ee172139d lint fix 2025-08-04 16:32:40 +02:00
Phillip Thelen
6447b9ab4b update block error strings 2025-08-04 16:03:55 +02:00
Phillip Thelen
5c414099d9 improve navbar display for non fullAccess admin 2025-08-04 14:46:05 +02:00
Phillip Thelen
5e8e1179aa fix managing permissions from admin 2025-08-04 14:45:47 +02:00
Phillip Thelen
7e86a62624 improve permission check 2025-08-04 14:33:09 +02:00
Phillip Thelen
1ba9dda0ed add new permission for managing blockers 2025-08-04 14:21:36 +02:00
Fiz
2feadd6125 Merge pull request #15483 from HabitRPG/kalista/category-content
Add Challenge categories to content API
2025-07-31 12:38:01 -05:00
Phillip Thelen
227e5ceaa8 fix import 2025-07-30 11:26:55 +02:00
Phillip Thelen
f77ab5a3ab lint fixes 2025-07-30 11:26:55 +02:00
Phillip Thelen
1916faf647 fix 2025-07-30 11:26:55 +02:00
Phillip Thelen
80ecb5cef1 lint fix 2025-07-30 11:26:55 +02:00
Phillip Thelen
75c36e6622 add blocker to block emails from registration 2025-07-30 11:26:55 +02:00
Phillip Thelen
78330c975a Improve blocker UI 2025-07-30 11:26:55 +02:00
Phillip Thelen
95266f6cb3 improve test coverage 2025-07-30 11:26:55 +02:00
Phillip Thelen
e9b2c1b51a restructure admin pages 2025-07-30 11:26:54 +02:00
Phillip Thelen
2a2bea07ab Add UI for managing blockers 2025-07-30 11:26:54 +02:00
Phillip Thelen
ea60ddbf4c Tweak wording 2025-07-30 11:25:51 +02:00
Phillip Thelen
1c2ca0e478 correctly reset local data after creating blocker 2025-07-30 11:25:51 +02:00
Phillip Thelen
ef2b7eb928 Add UI for managing blockers 2025-07-30 11:25:51 +02:00
Phillip Thelen
3d16387a61 add new frontend files 2025-07-30 11:25:41 +02:00
Phillip Thelen
93b7770eaa begin building general blocking solution 2025-07-30 11:25:41 +02:00
Phillip Thelen
a9f84d3307 Read IP blocks from database 2025-07-30 11:25:41 +02:00
Kalista Payne
efe0b3cd9e 5.38.0 2025-07-28 14:15:00 -05:00
Kalista Payne
96731da380 5.37.3 2025-07-28 14:06:45 -05:00
Weblate
0c5dd5d8b5 Merge branch 'origin/develop' into Weblate. 2025-07-28 21:03:29 +02:00
Weblate
2f943a22e6 Translated using Weblate (German)
Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (German)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (German)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (German)

Currently translated at 98.6% (850 of 862 strings)

Translated using Weblate (German)

Currently translated at 99.8% (3373 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.8% (3373 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.8% (3373 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.5% (3361 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.5% (3361 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.5% (3361 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.4% (3360 of 3377 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (185 of 185 strings)

Translated using Weblate (Polish)

Currently translated at 67.1% (579 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 67.1% (579 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (German)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (German)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (German)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 14.2% (35 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 13.8% (34 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 13.0% (32 of 245 strings)

Translated using Weblate (Hebrew)

Currently translated at 2.0% (5 of 245 strings)

Translated using Weblate (Hebrew)

Currently translated at 66.1% (570 of 862 strings)

Translated using Weblate (Portuguese)

Currently translated at 54.1% (1830 of 3377 strings)

Co-authored-by: FingerTiao <787170918@qq.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Jan Freihöfer <jan.stauch.is@gmail.com>
Co-authored-by: Jonathan Niessen <37.friedrich@gmail.com>
Co-authored-by: Karmelkowy <kicimeow.karmelio@gmail.com>
Co-authored-by: Katharina <katharinaanna.wilding@gmail.com>
Co-authored-by: Laura Fleckenstein <fleckenstein_laura@web.de>
Co-authored-by: Omer I.S <omeritzicschwartz@gmail.com>
Co-authored-by: Remigiusz Haziak <haziakremigiusz@gmail.com>
Co-authored-by: Uwe B <hbtca@tunixgut.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Wellinton Cardoso <wmcardoso1@hotmail.com>
Co-authored-by: cloudzzy <truskawka412@gmail.com>
Co-authored-by: 吳昀錡 <J1120241@gm.fdhs.tyc.edu.tw>
Translate-URL: https://translate.habitica.com/projects/habitica/character/de/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/de/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/de/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/he/
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/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/de/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/he/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pl/
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Npc
Translation: Habitica/Questscontent
2025-07-28 21:03:17 +02:00
Phillip Thelen
666184d7e4 Fix 500 when deleting a very old group plan account (#15481) 2025-07-28 09:50:38 -05:00
Phillip Thelen
17d22dda3f enforce x-client header (#15476) 2025-07-22 14:00:51 -05:00
Natalie
d1a18c121d August 2025 Content Build (#15460)
* chore: 2025-08 content build

* chore: fix typos

* fix(string): degender the news

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-07-21 17:59:40 -05:00
Kalista Payne
836d7f3991 5.37.2 2025-07-21 09:01:59 -05:00
Weblate
ace9c3c46a Translated using Weblate (English (United Kingdom))
Currently translated at 82.7% (221 of 267 strings)

Translated using Weblate (Polish)

Currently translated at 67.0% (578 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 67.0% (578 of 862 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 62.6% (57 of 91 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 71.4% (185 of 259 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 69.1% (179 of 259 strings)

Translated using Weblate (German)

Currently translated at 98.3% (181 of 184 strings)

Translated using Weblate (German)

Currently translated at 98.9% (191 of 193 strings)

Translated using Weblate (German)

Currently translated at 99.3% (3354 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.3% (3354 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Japanese)

Currently translated at 89.0% (768 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 12.6% (31 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 12.6% (31 of 245 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 98.3% (181 of 184 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 78.7% (193 of 245 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 96.7% (237 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 86.8% (749 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 99.3% (905 of 911 strings)

Translated using Weblate (German)

Currently translated at 99.2% (3352 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 12.2% (30 of 245 strings)

Translated using Weblate (Japanese)

Currently translated at 82.0% (201 of 245 strings)

Translated using Weblate (German)

Currently translated at 99.2% (3350 of 3377 strings)

Translated using Weblate (Russian)

Currently translated at 88.9% (2994 of 3367 strings)

Translated using Weblate (Russian)

Currently translated at 88.8% (2991 of 3367 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 67.5% (175 of 259 strings)

Translated using Weblate (German)

Currently translated at 99.1% (3348 of 3377 strings)

Translated using Weblate (Japanese)

Currently translated at 85.8% (158 of 184 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Japanese)

Currently translated at 81.6% (200 of 245 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 86.3% (787 of 911 strings)

Translated using Weblate (Japanese)

Currently translated at 79.1% (194 of 245 strings)

Translated using Weblate (Japanese)

Currently translated at 88.9% (767 of 862 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3346 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3346 of 3377 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 94.6% (390 of 412 strings)

Translated using Weblate (Japanese)

Currently translated at 91.5% (3090 of 3377 strings)

Translated using Weblate (Japanese)

Currently translated at 85.3% (228 of 267 strings)

Translated using Weblate (Japanese)

Currently translated at 88.8% (766 of 862 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Japanese)

Currently translated at 77.5% (190 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 87.6% (234 of 267 strings)

Translated using Weblate (Ukrainian)

Currently translated at 57.6% (1946 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3345 of 3377 strings)

Translated using Weblate (German)

Currently translated at 98.3% (181 of 184 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3345 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3345 of 3377 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.3% (427 of 430 strings)

Translated using Weblate (Polish)

Currently translated at 32.6% (80 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 87.6% (234 of 267 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.6% (266 of 267 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.6% (266 of 267 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Ukrainian)

Currently translated at 98.8% (425 of 430 strings)

Translated using Weblate (Ukrainian)

Currently translated at 57.6% (1946 of 3377 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 93.8% (107 of 114 strings)

Translated using Weblate (Ukrainian)

Currently translated at 57.5% (1944 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 66.8% (576 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 59.3% (54 of 91 strings)

Translated using Weblate (Polish)

Currently translated at 89.8% (240 of 267 strings)

Translated using Weblate (Polish)

Currently translated at 87.4% (376 of 430 strings)

Translated using Weblate (Polish)

Currently translated at 52.3% (1769 of 3377 strings)

Translated using Weblate (Ukrainian)

Currently translated at 55.9% (137 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 28.5% (70 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 66.8% (576 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 59.3% (54 of 91 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Polish)

Currently translated at 96.4% (110 of 114 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.6% (258 of 259 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 91.8% (395 of 430 strings)

Translated using Weblate (German)

Currently translated at 98.9% (3343 of 3377 strings)

Translated using Weblate (Ukrainian)

Currently translated at 55.5% (136 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 58.2% (53 of 91 strings)

Translated using Weblate (Korean)

Currently translated at 80.1% (730 of 911 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Polish)

Currently translated at 88.4% (229 of 259 strings)

Translated using Weblate (Polish)

Currently translated at 97.3% (401 of 412 strings)

Translated using Weblate (Polish)

Currently translated at 85.3% (221 of 259 strings)

Translated using Weblate (German)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (German)

Currently translated at 99.7% (909 of 911 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 87.5% (798 of 911 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Swedish)

Currently translated at 4.8% (12 of 245 strings)

Translated using Weblate (Swedish)

Currently translated at 1.2% (3 of 245 strings)

Translated using Weblate (Swedish)

Currently translated at 94.6% (53 of 56 strings)

Translated using Weblate (Swedish)

Currently translated at 77.6% (104 of 134 strings)

Translated using Weblate (Swedish)

Currently translated at 86.2% (50 of 58 strings)

Translated using Weblate (Swedish)

Currently translated at 66.5% (286 of 430 strings)

Translated using Weblate (Swedish)

Currently translated at 81.8% (199 of 243 strings)

Translated using Weblate (Swedish)

Currently translated at 51.6% (47 of 91 strings)

Translated using Weblate (Swedish)

Currently translated at 91.4% (43 of 47 strings)

Translated using Weblate (Swedish)

Currently translated at 66.2% (285 of 430 strings)

Translated using Weblate (Swedish)

Currently translated at 76.0% (140 of 184 strings)

Translated using Weblate (Swedish)

Currently translated at 53.6% (139 of 259 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Hebrew)

Currently translated at 78.9% (90 of 114 strings)

Translated using Weblate (Hebrew)

Currently translated at 66.4% (89 of 134 strings)

Translated using Weblate (Hebrew)

Currently translated at 56.2% (242 of 430 strings)

Translated using Weblate (Hebrew)

Currently translated at 38.5% (1301 of 3377 strings)

Translated using Weblate (Hebrew)

Currently translated at 90.5% (220 of 243 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Hebrew)

Currently translated at 66.1% (570 of 862 strings)

Translated using Weblate (Hebrew)

Currently translated at 98.4% (190 of 193 strings)

Translated using Weblate (Hebrew)

Currently translated at 58.6% (152 of 259 strings)

Translated using Weblate (Dutch)

Currently translated at 37.5% (92 of 245 strings)

Translated using Weblate (French)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Dutch)

Currently translated at 37.1% (91 of 245 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Hebrew)

Currently translated at 75.0% (6 of 8 strings)

Translated using Weblate (Hebrew)

Currently translated at 87.2% (212 of 243 strings)

Translated using Weblate (Hebrew)

Currently translated at 75.0% (138 of 184 strings)

Translated using Weblate (Hebrew)

Currently translated at 88.8% (48 of 54 strings)

Translated using Weblate (Hebrew)

Currently translated at 66.1% (570 of 862 strings)

Translated using Weblate (Hebrew)

Currently translated at 57.1% (52 of 91 strings)

Translated using Weblate (Hebrew)

Currently translated at 98.4% (190 of 193 strings)

Translated using Weblate (Hebrew)

Currently translated at 96.3% (106 of 110 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (3377 of 3377 strings)

Co-authored-by: Adrielle Marques <adrielle.marques3030@gmail.com>
Co-authored-by: Aleksander Mieciek <alex.mieciek@gmail.com>
Co-authored-by: AlexFad <2077505931@qq.com>
Co-authored-by: Alexandre Le Mercier <alexandre.le.mercier@ulb.be>
Co-authored-by: Ana <taranaana75@gmail.com>
Co-authored-by: Andrea <goffopaguro@gmail.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Eddy Nottingham <habitica.com.scone566@simplelogin.com>
Co-authored-by: FingerTiao <787170918@qq.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Jezz <lorraly@gmail.com>
Co-authored-by: Jezzica Israelsson <lorraly@gmail.com>
Co-authored-by: Jonathan Niessen <37.friedrich@gmail.com>
Co-authored-by: Karmelkowy <kicimeow.karmelio@gmail.com>
Co-authored-by: Laura Fleckenstein <fleckenstein_laura@web.de>
Co-authored-by: Maximiliano Guerra <guguloco2006@gmail.com>
Co-authored-by: Omer I.S <omeritzicschwartz@gmail.com>
Co-authored-by: Pasquale Bosso <protagora87@gmail.com>
Co-authored-by: Remigiusz Haziak <haziakremigiusz@gmail.com>
Co-authored-by: Ri Vargas <goldenhaitang@gmail.com>
Co-authored-by: Sara Olson <sara@habitica.com>
Co-authored-by: Sonia <sophishport@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Summer_GUI <heyang94@163.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Uwe B <hbtca@tunixgut.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: gongxueyan <1264752231@qq.com>
Co-authored-by: haru kake <hebey27020@bulmp3.com>
Co-authored-by: luckycccc <806009164@qq.com>
Co-authored-by: Ірина <ira.chipsa@gmail.com>
Co-authored-by: 吳昀錡 <J1120241@gm.fdhs.tyc.edu.tw>
Co-authored-by: 污染源 <polluter979@qq.com>
Co-authored-by: 김수연 <dus28232@naver.com>
Co-authored-by: ? <importantdata78@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/he/
Translate-URL: https://translate.habitica.com/projects/habitica/character/de/
Translate-URL: https://translate.habitica.com/projects/habitica/character/he/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/he/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/death/he/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/he/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/sv/
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/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/front/he/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/he/
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/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/he/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/he/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/he/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/he/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/he/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/he/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/he/
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/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/es/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/he/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/sv/
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/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pl/
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/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/Loginincentives
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Spells
Translation: Habitica/Subscriber
2025-07-21 11:58:35 +02:00
Fiz
068640311e Reload window after changing password (#15474)
* Reload window after changing password

* Shows password change success message

Displays a success snackbar after a user successfully changes their password. This provides visual confirmation to the user that the password update was successful.
The success message is displayed only once after the page reloads.

* lint fix trailing spaces
2025-07-16 13:07:28 -05:00
Fiz
f26d2a59ae add InvalidCredentialsError with language-agnostic code (#15472)
* add InvalidCredentialsError with language-agnostic code and update backend & web logout logic

* error.code in API error responses

Updated the error handler to serialize responseErr.code as the JSON error field, falling back to responseErr.name when no code is set.

* fix(lint): whitespace and missing def

* fix(lint): missed one

* add InvalidCredentialsError case for bad token

Add test verifying that auth middleware throws InvalidCredentialsError with code "invalid_credentials" and correct translated message when the API token is invalid.

* fix(test): user fields implicitly required

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-07-15 09:49:11 -05:00
Phillip Thelen
03c7e9172e fix double submit for pw reset (#15473) 2025-07-14 16:14:20 -05:00
negue
6fdc072ec3 reset the ApiToken on password changes/resets (#15433)
* reset the ApiToken on password changes/resets

* fix/add tests

* fix(typo): test grammar

* update new API Token Strings, removed unused one

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-07-01 12:30:34 -05:00
Kalista Payne
e68661c04b 5.37.1 2025-07-01 11:54:42 -05:00
Weblate
4f567592ea Translated using Weblate (Italian)
Currently translated at 84.6% (237 of 280 strings)

Translated using Weblate (Swedish)

Currently translated at 52.0% (139 of 267 strings)

Translated using Weblate (German)

Currently translated at 99.6% (266 of 267 strings)

Translated using Weblate (Swedish)

Currently translated at 94.6% (53 of 56 strings)

Translated using Weblate (Swedish)

Currently translated at 92.3% (12 of 13 strings)

Translated using Weblate (Swedish)

Currently translated at 86.8% (99 of 114 strings)

Translated using Weblate (Swedish)

Currently translated at 74.6% (100 of 134 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Swedish)

Currently translated at 66.0% (284 of 430 strings)

Translated using Weblate (Swedish)

Currently translated at 75.5% (139 of 184 strings)

Translated using Weblate (Swedish)

Currently translated at 72.0% (621 of 862 strings)

Translated using Weblate (Swedish)

Currently translated at 47.2% (43 of 91 strings)

Translated using Weblate (Swedish)

Currently translated at 87.2% (41 of 47 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (Spanish)

Currently translated at 99.9% (3375 of 3377 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 10.2% (25 of 245 strings)

Translated using Weblate (Spanish)

Currently translated at 99.8% (3372 of 3377 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 7.7% (19 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 94.6% (390 of 412 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 93.2% (125 of 134 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (French)

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Spanish)

Currently translated at 99.7% (3369 of 3377 strings)

Translated using Weblate (German)

Currently translated at 98.9% (3341 of 3377 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 5.7% (14 of 245 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 83.8% (223 of 266 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 92.5% (124 of 134 strings)

Translated using Weblate (Portuguese)

Currently translated at 54.1% (1829 of 3377 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese)

Currently translated at 53.5% (1808 of 3377 strings)

Translated using Weblate (Italian)

Currently translated at 28.1% (69 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Polish)

Currently translated at 52.3% (1768 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 52.3% (1768 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 52.3% (1768 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 61.4% (172 of 280 strings)

Translated using Weblate (Polish)

Currently translated at 52.3% (1768 of 3377 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Polish)

Currently translated at 32.2% (79 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 94.4% (389 of 412 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (French)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Italian)

Currently translated at 92.3% (170 of 184 strings)

Translated using Weblate (Italian)

Currently translated at 27.3% (67 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Polish)

Currently translated at 99.1% (241 of 243 strings)

Translated using Weblate (Polish)

Currently translated at 96.2% (234 of 243 strings)

Translated using Weblate (Polish)

Currently translated at 96.2% (234 of 243 strings)

Translated using Weblate (Polish)

Currently translated at 86.5% (231 of 267 strings)

Translated using Weblate (Polish)

Currently translated at 94.6% (89 of 94 strings)

Translated using Weblate (Polish)

Currently translated at 91.3% (222 of 243 strings)

Translated using Weblate (Polish)

Currently translated at 30.6% (75 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 30.6% (75 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Polish)

Currently translated at 84.2% (225 of 267 strings)

Translated using Weblate (Polish)

Currently translated at 84.2% (225 of 267 strings)

Translated using Weblate (Polish)

Currently translated at 84.2% (225 of 267 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 99.1% (241 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 99.1% (241 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 96.2% (234 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 96.2% (234 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 93.4% (227 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 93.4% (227 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (Russian)

Currently translated at 88.8% (2992 of 3367 strings)

Translated using Weblate (Russian)

Currently translated at 88.8% (2992 of 3367 strings)

Translated using Weblate (Russian)

Currently translated at 88.8% (2992 of 3367 strings)

Translated using Weblate (Italian)

Currently translated at 86.3% (2907 of 3367 strings)

Translated using Weblate (Russian)

Currently translated at 71.8% (176 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 70.2% (172 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Russian)

Currently translated at 66.9% (164 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Italian)

Currently translated at 22.0% (54 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 22.0% (54 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (908 of 908 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (908 of 908 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (167 of 167 strings)

Co-authored-by: Bartosz Babik <kotka-wali0h@icloud.com>
Co-authored-by: Goldy <mariesipova314@gmail.com>
Co-authored-by: Jackal <qwerty70244@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Jan P <jankrzpow@gmail.com>
Co-authored-by: Jezzica Israelsson <lorraly@gmail.com>
Co-authored-by: Jonathan Niessen <37.friedrich@gmail.com>
Co-authored-by: Karmelkowy <kicimeow.karmelio@gmail.com>
Co-authored-by: Kernis <kerhsing.wang@gmail.com>
Co-authored-by: LaiYi <lysinexxin@163.com>
Co-authored-by: Nicolas Samuel Reuter <nicolasreuter1@gmail.com>
Co-authored-by: Nik <doni.della.morte5619@gmail.com>
Co-authored-by: Pasquale Bosso <protagora87@gmail.com>
Co-authored-by: R. J <ricardo@pinho.org>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Sugo Gangotti <giacomo@ergonomia.it>
Co-authored-by: Summer_GUI <heyang94@163.com>
Co-authored-by: Svetlana <shkulepo@rambler.ru>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Val <3qes0hnzh@mozmail.com>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Zuz Q <zuzannakunik@gmail.com>
Co-authored-by: fitria nabila <fitria3nabila@gmail.com>
Co-authored-by: konhi <hello.konhi@gmail.com>
Co-authored-by: razil <boss.razmarin@gmail.com>
Co-authored-by: 吳昀錡 <J1120241@gm.fdhs.tyc.edu.tw>
Co-authored-by: ? <importantdata78@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/it/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/id/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/it/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pl/
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/it/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/character/it/
Translate-URL: https://translate.habitica.com/projects/habitica/character/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/it/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/content/it/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/it/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/front/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/front/it/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sv/
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/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/it/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt/
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/generic/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/it/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/it/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/it/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/sv/
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/Challenge
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Spells
Translation: Habitica/Subscriber
2025-07-01 05:18:55 +02:00
Kalista Payne
63c9b7a894 fix(groups): add missing markModified 2025-06-26 18:08:57 -05:00
Kalista Payne
eaec39188e chore(subproj): update habitica-images 2025-06-26 17:55:24 -05:00
Natalie
ba6940eb81 chore: update time travelers shop to display seasonal backgrounds (#15445)
* chore: update time travelers shop to display seasonal backgrounds

* chore: update time travelers banner (note CSS borken rn)

* chore: fix borken CSS and update logic in shop

* chore: added isSubscribed function, not working

* chore: isSubscribed working but no bg for subscribers

* chore: logic and css updates

* chore: add check for trinket

* chore: more time traveler shop logicking

* chore(git): heckin habitica-images

* refactor(style): indents/readability

* refactor(style): one more line break

* refactor(style): still more indents

* refactor(style): I wonder if lint can help with this stuff

* refactor(style): tighten up

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-06-25 15:12:23 -05:00
Kalista Payne
f8a3e4d673 perf(groups): use simplest possible query for cron script (#15457) 2025-06-25 14:56:50 -05:00
Phillip Thelen
2727da6f6c Correctly define components (#15458) 2025-06-25 14:56:29 -05:00
Kalista Payne
fa97852e38 5.37.0 2025-06-21 08:57:40 -05:00
Weblate
2c7da25a25 Translated using Weblate (Dutch)
Currently translated at 35.1% (86 of 245 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (French)

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 79.6% (212 of 266 strings)

Translated using Weblate (French)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.5% (406 of 412 strings)

Translated using Weblate (French)

Currently translated at 96.1% (177 of 184 strings)

Co-authored-by: Alexandre Le Mercier <alexandre.le.mercier@ulb.be>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: LaiYi <lysinexxin@163.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/front/fr/
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/zh_Hant/
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Subscriber
2025-06-21 15:07:30 +02:00
Natalie
9a072e3e76 chore: update grand gala start dates (#15435)
* chore: update grand gala start date

* chore: update test cases for new gala dates

* chore(sales): adjust promo dates

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-06-19 18:05:38 -05:00
Natalie
823b339d27 chore: July 2025 content build (#15449)
* chore: July 2025 content build

* chore: add release dates to armoire

* fix(sprites): correct Cryptid typo

* fix(grammar): missing comma

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-06-17 13:34:57 -05:00
Kalista Payne
fe98d9485d 5.36.6 2025-06-17 13:06:00 -05:00
Weblate
407e1bb560 Translated using Weblate (Italian)
Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Italian)

Currently translated at 98.8% (898 of 908 strings)

Translated using Weblate (Italian)

Currently translated at 90.1% (82 of 91 strings)

Translated using Weblate (Italian)

Currently translated at 86.3% (2907 of 3367 strings)

Translated using Weblate (German)

Currently translated at 99.1% (3340 of 3367 strings)

Translated using Weblate (Italian)

Currently translated at 84.6% (77 of 91 strings)

Translated using Weblate (Italian)

Currently translated at 20.4% (50 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 72.5% (66 of 91 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 86.5% (786 of 908 strings)

Translated using Weblate (French)

Currently translated at 88.5% (163 of 184 strings)

Translated using Weblate (German)

Currently translated at 99.1% (3338 of 3367 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 86.4% (159 of 184 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Spanish)

Currently translated at 95.6% (176 of 184 strings)

Translated using Weblate (Italian)

Currently translated at 18.3% (45 of 245 strings)

Translated using Weblate (Spanish)

Currently translated at 86.4% (159 of 184 strings)

Translated using Weblate (German)

Currently translated at 86.4% (159 of 184 strings)

Translated using Weblate (Polish)

Currently translated at 29.7% (73 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 28.9% (71 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Polish)

Currently translated at 28.9% (71 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 96.3% (106 of 110 strings)

Translated using Weblate (Dutch)

Currently translated at 30.6% (75 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3336 of 3367 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (3367 of 3367 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.8% (3361 of 3367 strings)

Translated using Weblate (Italian)

Currently translated at 17.9% (44 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.5% (3353 of 3367 strings)

Translated using Weblate (Italian)

Currently translated at 17.5% (43 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.2% (3343 of 3367 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.7% (429 of 430 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.5% (406 of 412 strings)

Translated using Weblate (Korean)

Currently translated at 96.4% (110 of 114 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Korean)

Currently translated at 22.8% (56 of 245 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3334 of 3367 strings)

Translated using Weblate (Korean)

Currently translated at 13.0% (32 of 245 strings)

Translated using Weblate (Korean)

Currently translated at 9.7% (24 of 245 strings)

Translated using Weblate (Korean)

Currently translated at 7.3% (18 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.2% (3341 of 3367 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.0% (3334 of 3367 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.0% (3334 of 3367 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 92.7% (382 of 412 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.7% (3325 of 3367 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.7% (3324 of 3367 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 85.4% (776 of 908 strings)

Translated using Weblate (Polish)

Currently translated at 87.5% (7 of 8 strings)

Translated using Weblate (Polish)

Currently translated at 52.5% (1768 of 3367 strings)

Translated using Weblate (Polish)

Currently translated at 66.4% (573 of 862 strings)

Translated using Weblate (German)

Currently translated at 98.9% (3332 of 3367 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.5% (406 of 412 strings)

Translated using Weblate (German)

Currently translated at 98.9% (3330 of 3367 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.6% (842 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.5% (406 of 412 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.3% (405 of 412 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.6% (3322 of 3367 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (266 of 266 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (280 of 280 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.1% (3305 of 3367 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.1% (3305 of 3367 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3367 of 3367 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3367 of 3367 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (908 of 908 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Dutch)

Currently translated at 28.5% (70 of 245 strings)

Translated using Weblate (Dutch)

Currently translated at 97.8% (46 of 47 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.9% (3297 of 3367 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.5% (239 of 245 strings)

Co-authored-by: Alexandre Le Mercier <alexandre.le.mercier@ulb.be>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Dawid Wodyk <dawid.wodykk@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: FingerTiao <787170918@qq.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Jan P <jankrzpow@gmail.com>
Co-authored-by: LaiYi <lysinexxin@163.com>
Co-authored-by: Nik <doni.della.morte5619@gmail.com>
Co-authored-by: Pasquale Bosso <protagora87@gmail.com>
Co-authored-by: Saalima Aaseman <aaseman.bio@gmail.com>
Co-authored-by: Sam Hou <samhou777@outlook.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Summer_GUI <heyang94@163.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: helianlouchen <helianlouchen@gmail.com>
Co-authored-by: luckycc <806009164@qq.com>
Co-authored-by: luckycccc <806009164@qq.com>
Co-authored-by: sein <tjdgp0132n@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/it/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/it/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/nl/
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/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/front/de/
Translate-URL: https://translate.habitica.com/projects/habitica/front/es/
Translate-URL: https://translate.habitica.com/projects/habitica/front/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pl/
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/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
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/limited/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Spells
Translation: Habitica/Subscriber
2025-06-17 12:58:03 +02:00
Kalista Payne
98a6535dc3 fix(content): don't filter out the thing we want 2025-06-16 16:43:56 -05:00
Kalista Payne
9948e8ee44 fix(mobile): provide Challenge categories via API 2025-06-16 16:43:24 -05:00
Phillip Thelen
bce07ec357 wait for vue router to be ready (#15456)
* wait for vue router to be ready

* fix(lint): whitespace

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-06-16 11:48:37 -05:00
Kalista Payne
836807aa1e Update static features pages (#15451)
* update images on static features page

* small layout changes

* Update feature texts

* layout tweaks

* refactor(client): move to Vite by @phillipthelen

* update images on static features page

* make text more comfortable to read

* update capitalization

* fix(whitespace): add EOF CR

---------

Co-authored-by: Phillip Thelen <phillip@habitica.com>
2025-06-12 13:11:16 -05:00
Phillip Thelen
ebbcbef6d5 Fix deploying static files to S3 (#15450)
* upload all image types to s3

* fix __dirname not existing
2025-06-12 10:24:24 -05:00
Kalista Payne
ccc6c9867f refactor(client): move to Vite by @phillipthelen 2025-06-11 19:20:11 -05:00
Natalie
20d31ed8c8 fix: pw reset screen logo color (#15442)
* fix: pw reset screen logo color

* fix: habitica logo svg color on password reset page
2025-05-29 14:55:57 -05:00
Kalista Payne
39ff6cbe05 5.36.5 2025-05-29 11:57:45 -05:00
Natalie
1bf2efa885 fix: correct Soft White Suit's stats interpolation (#15431)
* update armorArmoireBasketballUniformNotes to correct stat display

* correct typo in Puppy pet adjective

* fix: fix Soft White Suit's stats interpolation

---------

Co-authored-by: Sabe Jones <sabe@habitica.com>
2025-05-29 11:56:54 -05:00
Weblate
4b45a6389c Translated using Weblate (Portuguese (Brazil))
Currently translated at 98.0% (404 of 412 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (German)

Currently translated at 98.8% (3329 of 3367 strings)

Translated using Weblate (Ukrainian)

Currently translated at 55.1% (135 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 96.9% (258 of 266 strings)

Translated using Weblate (German)

Currently translated at 98.8% (3329 of 3367 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.6% (842 of 862 strings)

Translated using Weblate (German)

Currently translated at 98.8% (3327 of 3367 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3367 of 3367 strings)

Translated using Weblate (German)

Currently translated at 98.7% (3325 of 3367 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.0% (404 of 412 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 99.3% (902 of 908 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.5% (239 of 245 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.6% (842 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.0% (404 of 412 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (908 of 908 strings)

Translated using Weblate (Italian)

Currently translated at 84.4% (363 of 430 strings)

Translated using Weblate (Italian)

Currently translated at 70.3% (64 of 91 strings)

Translated using Weblate (Italian)

Currently translated at 80.8% (215 of 266 strings)

Translated using Weblate (Italian)

Currently translated at 94.7% (127 of 134 strings)

Translated using Weblate (Italian)

Currently translated at 98.2% (57 of 58 strings)

Translated using Weblate (Italian)

Currently translated at 84.1% (362 of 430 strings)

Translated using Weblate (Italian)

Currently translated at 69.2% (63 of 91 strings)

Translated using Weblate (Italian)

Currently translated at 94.0% (126 of 134 strings)

Translated using Weblate (Italian)

Currently translated at 83.7% (360 of 430 strings)

Translated using Weblate (Italian)

Currently translated at 64.8% (59 of 91 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.5% (841 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.5% (841 of 862 strings)

Translated using Weblate (German)

Currently translated at 98.3% (848 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.5% (841 of 862 strings)

Translated using Weblate (German)

Currently translated at 100.0% (280 of 280 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (266 of 266 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3367 of 3367 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3367 of 3367 strings)

Translated using Weblate (German)

Currently translated at 99.2% (278 of 280 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.7% (3292 of 3367 strings)

Translated using Weblate (French)

Currently translated at 100.0% (266 of 266 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (266 of 266 strings)

Translated using Weblate (German)

Currently translated at 100.0% (266 of 266 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (280 of 280 strings)

Translated using Weblate (French)

Currently translated at 99.6% (3354 of 3367 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3367 of 3367 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.5% (841 of 862 strings)

Translated using Weblate (German)

Currently translated at 100.0% (908 of 908 strings)

Co-authored-by: AlexFad <2077505931@qq.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Giu <gcapogna28@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Marie Blosse--Gilbin <mbgil@hotmail.fr>
Co-authored-by: Sam Hou <samhou777@outlook.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: wangtongxue <wangtongxue123456@outlook.com>
Co-authored-by: Анна <antarinel+habitica@gmail.com>
Co-authored-by: Юрий Артамонов <zilberstein2211@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/it/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
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/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/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/it/
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/messages/it/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/it/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/it/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translation: Habitica/Backgrounds
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Questscontent
Translation: Habitica/Spells
Translation: Habitica/Subscriber
2025-05-29 18:55:30 +02:00
Phillip Thelen
5ba7d2395e Optimize database access for some use cases (#15444)
* optimize query when listing challenge tasks

* Optimize query for checking if user is party leader
2025-05-28 21:52:07 -05:00
dependabot[bot]
972f23e235 chore(deps): bump http-proxy-middleware in /website/client (#15427)
Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.6 to 2.0.9.
- [Release notes](https://github.com/chimurai/http-proxy-middleware/releases)
- [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.9/CHANGELOG.md)
- [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.6...v2.0.9)

---
updated-dependencies:
- dependency-name: http-proxy-middleware
  dependency-version: 2.0.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 16:01:41 -04:00
dependabot[bot]
9f599b0c8e chore(deps): bump @babel/runtime from 7.23.9 to 7.26.10 (#15410)
Bumps [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime) from 7.23.9 to 7.26.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.26.10/packages/babel-runtime)

---
updated-dependencies:
- dependency-name: "@babel/runtime"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 15:57:08 -04:00
dependabot[bot]
b937c2df0b chore(deps): bump @babel/helpers in /website/client (#15407)
Bumps [@babel/helpers](https://github.com/babel/babel/tree/HEAD/packages/babel-helpers) from 7.23.6 to 7.26.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.26.10/packages/babel-helpers)

---
updated-dependencies:
- dependency-name: "@babel/helpers"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 15:56:47 -04:00
dependabot[bot]
9c4396027a chore(deps): bump @babel/runtime-corejs2 in /website/client (#15406)
Bumps [@babel/runtime-corejs2](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime-corejs2) from 7.23.6 to 7.26.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.26.10/packages/babel-runtime-corejs2)

---
updated-dependencies:
- dependency-name: "@babel/runtime-corejs2"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 15:56:11 -04:00
dependabot[bot]
2bab20d032 chore(deps): bump prismjs from 1.29.0 to 1.30.0 (#15403)
Bumps [prismjs](https://github.com/PrismJS/prism) from 1.29.0 to 1.30.0.
- [Release notes](https://github.com/PrismJS/prism/releases)
- [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md)
- [Commits](https://github.com/PrismJS/prism/compare/v1.29.0...v1.30.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 15:55:59 -04:00
dependabot[bot]
cb2ee670e3 chore(deps-dev): bump axios from 1.7.4 to 1.8.2 (#15401)
Bumps [axios](https://github.com/axios/axios) from 1.7.4 to 1.8.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.7.4...v1.8.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 15:55:41 -04:00
dependabot[bot]
b65d23d535 chore(deps): bump serialize-javascript in /website/client (#15395)
Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/yahoo/serialize-javascript/releases)
- [Commits](https://github.com/yahoo/serialize-javascript/compare/v6.0.1...v6.0.2)

---
updated-dependencies:
- dependency-name: serialize-javascript
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 15:55:20 -04:00
Kalista Payne
007cdf0ca2 5.36.4 2025-05-19 17:21:35 -05:00
Weblate
1e4799bac6 Merge branch 'origin/develop' into Weblate. 2025-05-20 00:15:13 +02:00
Weblate
47222445ad Translated using Weblate (Ukrainian)
Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (280 of 280 strings)

Translated using Weblate (French)

Currently translated at 100.0% (280 of 280 strings)

Translated using Weblate (Spanish)

Currently translated at 99.6% (279 of 280 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.4% (840 of 862 strings)

Translated using Weblate (German)

Currently translated at 99.8% (907 of 908 strings)

Translated using Weblate (Dutch)

Currently translated at 79.3% (219 of 276 strings)

Translated using Weblate (Dutch)

Currently translated at 28.1% (69 of 245 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.4% (840 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.5% (402 of 412 strings)

Translated using Weblate (Dutch)

Currently translated at 91.5% (377 of 412 strings)

Translated using Weblate (Dutch)

Currently translated at 85.2% (774 of 908 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (908 of 908 strings)

Translated using Weblate (Slovak)

Currently translated at 63.4% (106 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (908 of 908 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (908 of 908 strings)

Translated using Weblate (Slovak)

Currently translated at 2.0% (5 of 245 strings)

Translated using Weblate (French)

Currently translated at 100.0% (908 of 908 strings)

Translated using Weblate (Russian)

Currently translated at 64.4% (158 of 245 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.0% (837 of 862 strings)

Translated using Weblate (German)

Currently translated at 97.9% (844 of 862 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.3% (401 of 412 strings)

Translated using Weblate (Portuguese)

Currently translated at 95.3% (393 of 412 strings)

Translated using Weblate (Slovak)

Currently translated at 45.6% (413 of 905 strings)

Translated using Weblate (Slovak)

Currently translated at 50.8% (85 of 167 strings)

Translated using Weblate (Russian)

Currently translated at 99.1% (113 of 114 strings)

Translated using Weblate (Russian)

Currently translated at 64.0% (157 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 64.0% (157 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 62.0% (152 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 62.0% (152 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 60.8% (149 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 60.8% (149 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 60.4% (148 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 60.4% (148 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 60.0% (147 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 60.0% (147 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 57.9% (142 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 57.9% (142 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 56.7% (139 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 56.7% (139 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 56.3% (138 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 56.3% (138 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 53.8% (132 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 53.8% (132 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 53.4% (131 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 53.4% (131 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 48.9% (120 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 48.9% (120 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 48.5% (119 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 48.5% (119 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 46.9% (115 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 46.9% (115 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 46.9% (115 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 46.9% (115 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 46.9% (115 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 46.9% (115 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 46.9% (115 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 46.9% (115 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 46.9% (115 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 46.9% (115 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 46.9% (115 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 46.9% (115 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 46.9% (115 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 45.3% (111 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 45.3% (111 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 45.3% (111 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 45.3% (111 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 44.4% (109 of 245 strings)

Translated using Weblate (German)

Currently translated at 99.9% (3324 of 3325 strings)

Translated using Weblate (Russian)

Currently translated at 44.4% (109 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 44.4% (109 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 93.8% (107 of 114 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.7% (429 of 430 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.1% (820 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.6% (902 of 905 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.1% (820 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.1% (820 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.1% (820 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 93.8% (107 of 114 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 93.6% (3114 of 3325 strings)

Translated using Weblate (Portuguese)

Currently translated at 53.9% (1793 of 3325 strings)

Translated using Weblate (Dutch)

Currently translated at 78.1% (2600 of 3325 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.5% (242 of 243 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.1% (820 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.6% (398 of 412 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.6% (902 of 905 strings)

Translated using Weblate (Italian)

Currently translated at 99.1% (113 of 114 strings)

Translated using Weblate (Italian)

Currently translated at 87.3% (2903 of 3325 strings)

Translated using Weblate (Italian)

Currently translated at 17.1% (42 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 99.0% (408 of 412 strings)

Translated using Weblate (Italian)

Currently translated at 92.7% (102 of 110 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.0% (3292 of 3325 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.7% (3285 of 3325 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.7% (3285 of 3325 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (905 of 905 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.1% (3262 of 3325 strings)

Co-authored-by: Andrea <goffopaguro@gmail.com>
Co-authored-by: Artem StolyROV <stolyarov11303@gmail.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: David Kaya <david@kaya.sk>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: FingerTiao <787170918@qq.com>
Co-authored-by: Irina  Shcherbinina <cat3dcat007@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Mencius <beautyalinap@gmail.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Nikita Maximov <ruvemaximus@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Summer_GUI <heyang94@163.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Tom <tompsognathus@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: V Aar <v.vanderaar@gmail.com>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: razil <boss.razmarin@gmail.com>
Co-authored-by: Волкозмей <klippiky@gmail.com>
Co-authored-by: Данила Мальцев <maltsev-danila@inbox.ru>
Co-authored-by: Татьяна Куклева <klippiky@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/it/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/content/it/
Translate-URL: https://translate.habitica.com/projects/habitica/content/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/sk/
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/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/es/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/it/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Npc
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
2025-05-20 00:15:02 +02:00
Kalista Payne
126b382da1 fix(logging): don't spam empty error events 2025-05-19 17:07:42 -05:00
Kalista Payne
ec78831a81 fix(script): don't use extremely costly regex 2025-05-19 17:05:09 -05:00
Natalie
9bfb2afd9c June 2025 content build (#15437)
* chore: June 2025 content build

* chore: typo fixing

* chore: corrections to summer 2025 mage armor, spritesheet

* fix(css): rebuild spritesmith-main

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-05-13 16:10:25 -05:00
Kalista Payne
389124b83f 5.36.3 2025-05-07 12:03:54 -05:00
Weblate
eb25330296 Merge branch 'origin/develop' into Weblate. 2025-05-07 19:00:50 +02:00
Phillip Thelen
29892ff5e3 Allow an email to be passed to forgot-password page (#15436) 2025-05-07 11:46:47 -05:00
Weblate
99a31b322a Translated using Weblate (Chinese (Simplified))
Currently translated at 97.9% (3257 of 3325 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.8% (3255 of 3325 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.7% (3250 of 3325 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.9% (235 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.5% (841 of 845 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 53.8% (49 of 91 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Ukrainian)

Currently translated at 54.2% (133 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 43.2% (106 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 43.2% (106 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Italian)

Currently translated at 94.7% (108 of 114 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 70.5% (2347 of 3325 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (905 of 905 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (German)

Currently translated at 99.9% (3322 of 3325 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3325 of 3325 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (862 of 862 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (905 of 905 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 88.0% (759 of 862 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 88.0% (759 of 862 strings)

Co-authored-by: Catherine Liang <catherine.cy.liang@gmail.com>
Co-authored-by: FingerTiao <787170918@qq.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Maya B <maya.bl@icloud.com>
Co-authored-by: Mencius <beautyalinap@gmail.com>
Co-authored-by: Nazar Paruna <nazarparuna@gmail.com>
Co-authored-by: Summer_GUI <heyang94@163.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Анна <antarinel+habitica@gmail.com>
Co-authored-by: Татьяна Куклева <klippiky@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/character/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/content/en_GB/
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/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/it/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/es/
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/settings/uk/
Translation: Habitica/Backgrounds
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Settings
2025-05-07 15:56:59 +02:00
Kalista Payne
1884c6c751 5.36.2 2025-05-06 12:24:15 -05:00
Natalie
9456477953 fix: correct party modal sort dropdown display (#15432) 2025-05-06 12:05:36 -05:00
Kalista Payne
e3512a2bdd 5.36.1 2025-04-30 10:38:05 -05:00
Kalista Payne
6ce3f84458 fix(css): add missing Cryptid references 2025-04-30 10:35:54 -05:00
Kalista Payne
484c3cbac8 5.36.0 2025-04-29 14:08:34 -05:00
Weblate
c199beaf8c Translated using Weblate (Ukrainian)
Currently translated at 98.1% (422 of 430 strings)

Translated using Weblate (Ukrainian)

Currently translated at 58.4% (1942 of 3325 strings)

Translated using Weblate (Ukrainian)

Currently translated at 54.2% (133 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Serbian)

Currently translated at 94.2% (132 of 140 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 96.5% (255 of 264 strings)

Translated using Weblate (Serbian)

Currently translated at 46.4% (123 of 265 strings)

Translated using Weblate (Serbian)

Currently translated at 94.6% (53 of 56 strings)

Translated using Weblate (Serbian)

Currently translated at 67.0% (63 of 94 strings)

Translated using Weblate (Serbian)

Currently translated at 75.0% (6 of 8 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (276 of 276 strings)

Translated using Weblate (Serbian)

Currently translated at 65.5% (282 of 430 strings)

Translated using Weblate (Serbian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.2% (3233 of 3325 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.2% (3233 of 3325 strings)

Translated using Weblate (Serbian)

Currently translated at 87.3% (159 of 182 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 5.3% (13 of 245 strings)

Translated using Weblate (Serbian)

Currently translated at 1.6% (4 of 245 strings)

Translated using Weblate (Serbian)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.5% (841 of 845 strings)

Translated using Weblate (Serbian)

Currently translated at 41.7% (38 of 91 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Serbian)

Currently translated at 74.0% (305 of 412 strings)

Translated using Weblate (Serbian)

Currently translated at 95.3% (184 of 193 strings)

Translated using Weblate (Serbian)

Currently translated at 26.3% (44 of 167 strings)

Translated using Weblate (Serbian)

Currently translated at 54.6% (142 of 260 strings)

Translated using Weblate (German)

Currently translated at 99.8% (3320 of 3325 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (408 of 408 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.8% (827 of 845 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.1% (821 of 845 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Spanish)

Currently translated at 99.9% (3322 of 3325 strings)

Translated using Weblate (Italian)

Currently translated at 14.2% (35 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 96.2% (813 of 845 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (862 of 862 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (905 of 905 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 94.5% (799 of 845 strings)

Translated using Weblate (Danish)

Currently translated at 69.9% (603 of 862 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 94.3% (797 of 845 strings)

Translated using Weblate (Danish)

Currently translated at 70.0% (604 of 862 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.9% (235 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.5% (234 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 13.8% (34 of 245 strings)

Translated using Weblate (Spanish)

Currently translated at 99.3% (856 of 862 strings)

Translated using Weblate (German)

Currently translated at 99.7% (3318 of 3325 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 92.8% (785 of 845 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Spanish)

Currently translated at 99.7% (3317 of 3325 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (German)

Currently translated at 99.7% (3316 of 3325 strings)

Translated using Weblate (French)

Currently translated at 100.0% (862 of 862 strings)

Translated using Weblate (Spanish)

Currently translated at 99.0% (854 of 862 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (French)

Currently translated at 100.0% (862 of 862 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.6% (398 of 412 strings)

Translated using Weblate (Italian)

Currently translated at 91.8% (101 of 110 strings)

Translated using Weblate (German)

Currently translated at 100.0% (265 of 265 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3325 of 3325 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (French)

Currently translated at 99.5% (858 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.3% (397 of 412 strings)

Translated using Weblate (German)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (905 of 905 strings)

Translated using Weblate (German)

Currently translated at 100.0% (905 of 905 strings)

Translated using Weblate (French)

Currently translated at 100.0% (265 of 265 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.6% (275 of 276 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.0% (3227 of 3325 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (411 of 412 strings)

Translated using Weblate (French)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (905 of 905 strings)

Translated using Weblate (French)

Currently translated at 100.0% (905 of 905 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.8% (253 of 264 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 96.0% (3195 of 3325 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.3% (397 of 412 strings)

Translated using Weblate (Russian)

Currently translated at 84.9% (225 of 265 strings)

Translated using Weblate (Russian)

Currently translated at 84.1% (223 of 265 strings)

Translated using Weblate (Spanish)

Currently translated at 99.7% (3316 of 3325 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (265 of 265 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3325 of 3325 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (862 of 862 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (265 of 265 strings)

Translated using Weblate (Hungarian)

Currently translated at 99.0% (854 of 862 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (905 of 905 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 93.1% (246 of 264 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 91.3% (772 of 845 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (411 of 412 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Polish)

Currently translated at 28.9% (71 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 28.5% (70 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.7% (407 of 408 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.7% (407 of 408 strings)

Translated using Weblate (Polish)

Currently translated at 28.1% (69 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Indonesian)

Currently translated at 97.1% (876 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (902 of 902 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.2% (260 of 276 strings)

Translated using Weblate (Ukrainian)

Currently translated at 58.4% (1939 of 3315 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 93.9% (3114 of 3315 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.8% (818 of 845 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.0% (396 of 408 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (902 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 78.3% (192 of 245 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.4% (815 of 845 strings)

Translated using Weblate (German)

Currently translated at 99.5% (841 of 845 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (902 of 902 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.5% (242 of 243 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 66.1% (162 of 245 strings)

Co-authored-by: Catherine Liang <catherine.cy.liang@gmail.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Dawid Wodyk <dawid.wodykk@gmail.com>
Co-authored-by: FingerTiao <787170918@qq.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Lizard <li07369427zard@gmail.com>
Co-authored-by: Lorenzo Lionello <lorenzo22092005@gmail.com>
Co-authored-by: Maja Miloradović <maka.zikelic@gmail.com>
Co-authored-by: Marie Blosse--Gilbin <mbgil@hotmail.fr>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Nazar Paruna <nazarparuna@gmail.com>
Co-authored-by: Oleh <okv23200@gmail.com>
Co-authored-by: Pasquale Bosso <protagora87@gmail.com>
Co-authored-by: Rocío Ibarra Robles <notion.ro.personal@gmail.com>
Co-authored-by: Saalima Aaseman <aaseman.bio@gmail.com>
Co-authored-by: Sofia Amaral Novaes <soso.novaes@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Summer_GUI <heyang94@163.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Ushkovsky Stas <che2116@ya.ru>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: theblighted <theamityd@gmail.com>
Co-authored-by: Анна <antarinel+habitica@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/sr/
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/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/id/
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/it/
Translate-URL: https://translate.habitica.com/projects/habitica/character/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/character/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/content/de/
Translate-URL: https://translate.habitica.com/projects/habitica/content/es/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/death/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/sr/
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/faq/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sr/
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/hu/
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/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/inventory/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/da/
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/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/uk/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Death
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/Overview
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Spells
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2025-04-29 20:23:27 +02:00
Phillip Thelen
553aa01c25 fix sprite alignment in mount raised modal 2025-04-28 09:39:54 -05:00
Natalie
8d1b10e458 May 2025 Content Build (#15422)
* chore: May 2025 subscriber gear, background, and armoire gear

* chore: May 2025 pet and hatching potion quests, stylesheet update

* fix: add space to pet quest rage description

* Update backgrounds.json

fix(typo): missing fullstop

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-04-18 13:05:03 -05:00
Phillip Thelen
0eaee9b1e4 Use stable sprites (#15421) 2025-04-17 16:38:22 -05:00
Kalista Payne
41bbc475ab 5.35.4 2025-04-17 16:18:55 -05:00
Natalie
d6e03c765e fix: correct number of available completed todos for users (#15424)
* fix: Correct number of available completed todos for users

* fix(test): remove test obsoleted by changes to GET completed

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-04-17 12:34:55 -05:00
Kalista Payne
dd6503d5ef 5.35.3 2025-04-15 14:25:24 -05:00
Weblate
36e5f39d7c Translated using Weblate (German)
Currently translated at 99.6% (259 of 260 strings)

Translated using Weblate (German)

Currently translated at 99.6% (259 of 260 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.4% (815 of 845 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.4% (815 of 845 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.4% (815 of 845 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 96.3% (236 of 245 strings)

Translated using Weblate (German)

Currently translated at 99.2% (133 of 134 strings)

Translated using Weblate (German)

Currently translated at 99.2% (133 of 134 strings)

Translated using Weblate (Japanese)

Currently translated at 93.1% (3087 of 3315 strings)

Translated using Weblate (Japanese)

Currently translated at 99.4% (192 of 193 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (902 of 902 strings)

Translated using Weblate (Japanese)

Currently translated at 75.9% (186 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 64.0% (157 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 13.4% (33 of 245 strings)

Translated using Weblate (Slovak)

Currently translated at 95.5% (128 of 134 strings)

Translated using Weblate (German)

Currently translated at 99.2% (133 of 134 strings)

Translated using Weblate (German)

Currently translated at 99.2% (133 of 134 strings)

Translated using Weblate (Italian)

Currently translated at 13.0% (32 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.2% (405 of 408 strings)

Translated using Weblate (German)

Currently translated at 99.4% (840 of 845 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (276 of 276 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 69.4% (2303 of 3315 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (408 of 408 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (902 of 902 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.3% (806 of 845 strings)

Translated using Weblate (German)

Currently translated at 99.9% (3314 of 3315 strings)

Translated using Weblate (German)

Currently translated at 99.9% (3314 of 3315 strings)

Translated using Weblate (Portuguese)

Currently translated at 34.5% (84 of 243 strings)

Translated using Weblate (Portuguese)

Currently translated at 99.4% (192 of 193 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (902 of 902 strings)

Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Eduardo Ariel Santos da Silva <are3380@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Forstwolf <forestwolf@spam.care>
Co-authored-by: Fwipa Penguin <fwipapenguin@gmail.com>
Co-authored-by: Hexe des Windes (she/her) <krausanna1@gmail.com>
Co-authored-by: Katharina <katharinaanna.wilding@gmail.com>
Co-authored-by: Kubo Mizuki <m.kubo.0916@gmail.com>
Co-authored-by: Pasquale Bosso <protagora87@gmail.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Zhi Hao Li <zhihaoli000@gmail.com>
Co-authored-by: hylconnect <hylconnect@outlook.com>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/character/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/character/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/character/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/content/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/de/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/de/
Translation: Habitica/Backgrounds
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Npc
Translation: Habitica/Questscontent
Translation: Habitica/Settings
2025-04-15 21:20:33 +02:00
Natalie
d48e4a664f Upgraded Inventory Search (#15417)
* fix: add search descriptions capability to items and equipment

* pet search working for mismatched pairs; mount still in progress

* added some comments about current issue to relevant portion of code

* search working on pet and mount names (except flying carpet)

* update comment text

* remove console.log(animals)

* add eggkey back in

* add mountName as property to animal object, amend filter function
2025-04-10 12:37:44 -05:00
Kalista Payne
661b30e807 5.35.2 2025-04-09 08:40:58 -05:00
Weblate
026e819271 Translated using Weblate (Ukrainian)
Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (French)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Italian)

Currently translated at 81.0% (214 of 264 strings)

Translated using Weblate (Italian)

Currently translated at 92.1% (105 of 114 strings)

Translated using Weblate (Italian)

Currently translated at 93.2% (125 of 134 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Italian)

Currently translated at 83.4% (359 of 430 strings)

Translated using Weblate (Italian)

Currently translated at 87.5% (2902 of 3315 strings)

Translated using Weblate (Italian)

Currently translated at 98.7% (240 of 243 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Italian)

Currently translated at 12.6% (31 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 63.7% (58 of 91 strings)

Translated using Weblate (Italian)

Currently translated at 95.7% (45 of 47 strings)

Translated using Weblate (Italian)

Currently translated at 98.9% (191 of 193 strings)

Translated using Weblate (Italian)

Currently translated at 98.7% (891 of 902 strings)

Translated using Weblate (Italian)

Currently translated at 96.5% (251 of 260 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.3% (806 of 845 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.0% (396 of 408 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (902 of 902 strings)

Translated using Weblate (German)

Currently translated at 99.2% (839 of 845 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.8% (901 of 902 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Slovak)

Currently translated at 67.0% (63 of 94 strings)

Translated using Weblate (Slovak)

Currently translated at 88.0% (118 of 134 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Russian)

Currently translated at 92.3% (780 of 845 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (408 of 408 strings)

Translated using Weblate (Slovak)

Currently translated at 43.7% (73 of 167 strings)

Translated using Weblate (Russian)

Currently translated at 91.1% (770 of 845 strings)

Translated using Weblate (Russian)

Currently translated at 91.1% (770 of 845 strings)

Translated using Weblate (Serbian)

Currently translated at 51.7% (1714 of 3315 strings)

Translated using Weblate (Serbian)

Currently translated at 94.8% (183 of 193 strings)

Translated using Weblate (Serbian)

Currently translated at 86.8% (99 of 114 strings)

Translated using Weblate (Serbian)

Currently translated at 94.3% (182 of 193 strings)

Translated using Weblate (Serbian)

Currently translated at 49.7% (449 of 902 strings)

Translated using Weblate (Serbian)

Currently translated at 25.7% (43 of 167 strings)

Translated using Weblate (Serbian)

Currently translated at 25.7% (43 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 96.2% (3190 of 3315 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.5% (402 of 408 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Polish)

Currently translated at 27.3% (67 of 245 strings)

Translated using Weblate (Dutch)

Currently translated at 90.9% (371 of 408 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (German)

Currently translated at 98.6% (834 of 845 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Co-authored-by: Andrea Brunato <andrea.brunato@live.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Jan met de Pet <stijn.koppers@gmail.com>
Co-authored-by: Klaudia Kasprzyk <kasprzyk.klaudiax@gmail.com>
Co-authored-by: Marie Blosse--Gilbin <mbgil@hotmail.fr>
Co-authored-by: Milica Gajić <potato.15.krompir@gmail.com>
Co-authored-by: Reiwa <reiwa.roan@gmail.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: fluffstuff <opositesandreality@gmail.com>
Co-authored-by: razil <boss.razmarin@gmail.com>
Co-authored-by: 沧浪 <963505255@qq.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/it/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/sr/
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/character/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/character/it/
Translate-URL: https://translate.habitica.com/projects/habitica/character/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/it/
Translate-URL: https://translate.habitica.com/projects/habitica/content/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/front/it/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/it/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/it/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/it/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/it/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/it/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/it/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
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/settings/it/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/it/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/it/
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/Generic
Translation: Habitica/Groups
Translation: Habitica/Loginincentives
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Spells
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2025-04-09 15:39:50 +02:00
Phillip Thelen
1fab19acf4 Refactor Cron to be more robust (#15399)
* Simplify cron code

use transactions for cron

remove only

bump mongoose to 8.x

remove deprecated config

fix race condition when users join a party

console debugging time

try calling transaction differently

add missing await

addditional console log

.

..

...

….

await

more debug log

mongoose logging

more logging

move session to encapsulate all of cron

delete old todos before fetching all tasks

changes

try waiting for mongoose connection

try adding timeout to time jump

cleanup and code refactoring

Translated using Weblate (Spanish)

Currently translated at 100.0% (821 of 821 strings)

Translated using Weblate (German)

Currently translated at 100.0% (821 of 821 strings)

Translated using Weblate (Japanese)

Currently translated at 87.0% (228 of 262 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 93.8% (107 of 114 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 18.1% (44 of 243 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 11.9% (29 of 243 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 88.1% (724 of 821 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 91.2% (104 of 114 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 7.4% (18 of 243 strings)

Translated using Weblate (Spanish)

Currently translated at 99.5% (817 of 821 strings)

Translated using Weblate (German)

Currently translated at 99.3% (816 of 821 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3265 of 3265 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3265 of 3265 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 98.2% (112 of 114 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 97.7% (131 of 134 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 69.1% (2257 of 3265 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 99.5% (239 of 240 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 16.4% (40 of 243 strings)

Translated using Weblate (German)

Currently translated at 99.9% (3264 of 3265 strings)

Translated using Weblate (Japanese)

Currently translated at 86.6% (227 of 262 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Japanese)

Currently translated at 97.9% (423 of 432 strings)

Translated using Weblate (German)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (German)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (German)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 14.8% (36 of 243 strings)

Translated using Weblate (German)

Currently translated at 99.1% (814 of 821 strings)

Translated using Weblate (German)

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 84.7% (222 of 262 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 84.3% (221 of 262 strings)

Translated using Weblate (German)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.0% (415 of 432 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.2% (3077 of 3265 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 63.7% (155 of 243 strings)

Translated using Weblate (German)

Currently translated at 99.0% (813 of 821 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 99.7% (396 of 397 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.7% (885 of 896 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (German)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Japanese)

Currently translated at 97.4% (265 of 272 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (60 of 60 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Japanese)

Currently translated at 98.7% (392 of 397 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Spanish)

Currently translated at 99.0% (813 of 821 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (French)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (French)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (French)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3265 of 3265 strings)

Translated using Weblate (French)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (French)

Currently translated at 100.0% (821 of 821 strings)

Translated using Weblate (French)

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (French)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3265 of 3265 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (French)

Currently translated at 100.0% (3265 of 3265 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3255 of 3255 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (15 of 15 strings)

Co-authored-by: Asier Gallego <agr2367789@gmail.com>
Co-authored-by: Asier Gallego Roca <asiernoide@users.noreply.translate.habitica.com>
Co-authored-by: Henrique Ferreira <pedroferreira217.ph@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: John Doe (Anonymous) <shyamjayeshduck@duck.com>
Co-authored-by: Katharina <katharinaanna.wilding@gmail.com>
Co-authored-by: Marie Blosse--Gilbin <mbgil@hotmail.fr>
Co-authored-by: Mauricio Pérez <mauriciodavidperez@gmail.com>
Co-authored-by: Raul Ernesto Ceron Lara <raztreuzz1234@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Willhelm Winter <carapax@posteo.de>
Co-authored-by: mattya 226 <worldworld1114@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/en_GB/
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/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/de/
Translate-URL: https://translate.habitica.com/projects/habitica/content/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/content/es/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/death/es/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/de/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/es/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/es/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/fr/
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/limited/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/de/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/es/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/es/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ja/
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/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translation: Habitica/Backgrounds
Translation: Habitica/Content
Translation: Habitica/Death
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Subscriber

5.33.1

fix(links): next round of wiki revisions

Translated using Weblate (German)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Portuguese)

Currently translated at 96.4% (864 of 896 strings)

Co-authored-by: Miya <baddybadges@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt/
Translation: Habitica/Backgrounds

5.33.2

Fix achievement display in admin panel (#15326)

Fix news related permission issues (#15287)

Support sprite version of armoire icon (#15354)

* Use sprite component for armoire sprite

* use gif version of armoire sprite

* fix(import): sprite component path

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>

log slow requests to loggly (#15364)

Update .eslintrc.js (#15388)

Add `require-await` to eslint config

Translated using Weblate (Japanese)

Currently translated at 93.0% (764 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 54.8% (1790 of 3265 strings)

Translated using Weblate (Hungarian)

Currently translated at 53.5% (1748 of 3265 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (Hungarian)

Currently translated at 52.1% (1704 of 3265 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 59.3% (532 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 79.3% (208 of 262 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 77.4% (2528 of 3265 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 93.0% (764 of 821 strings)

Translated using Weblate (French)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (French)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 94.8% (258 of 272 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.2% (378 of 397 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 82.8% (203 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (821 of 821 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.9% (377 of 397 strings)

Translated using Weblate (Hungarian)

Currently translated at 52.1% (1704 of 3265 strings)

Translated using Weblate (Hungarian)

Currently translated at 49.7% (122 of 245 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.1% (789 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (821 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 48.5% (119 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 26.1% (64 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2 of 2 strings)

Translated using Weblate (Hungarian)

Currently translated at 8.9% (22 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Hungarian)

Currently translated at 96.2% (790 of 821 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.4% (784 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 91.5% (752 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (German)

Currently translated at 99.2% (133 of 134 strings)

Translated using Weblate (German)

Currently translated at 99.2% (133 of 134 strings)

Translated using Weblate (Czech)

Currently translated at 95.2% (159 of 167 strings)

Translated using Weblate (Russian)

Currently translated at 91.2% (2978 of 3265 strings)

Translated using Weblate (Russian)

Currently translated at 99.3% (890 of 896 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3265 of 3265 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3265 of 3265 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (French)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (French)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Korean)

Currently translated at 98.5% (132 of 134 strings)

Translated using Weblate (Korean)

Currently translated at 6.9% (17 of 245 strings)

Translated using Weblate (Korean)

Currently translated at 71.9% (645 of 896 strings)

Translated using Weblate (Korean)

Currently translated at 49.2% (129 of 262 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Korean)

Currently translated at 81.9% (77 of 94 strings)

Translated using Weblate (Korean)

Currently translated at 91.6% (153 of 167 strings)

Translated using Weblate (Korean)

Currently translated at 67.3% (291 of 432 strings)

Translated using Weblate (Korean)

Currently translated at 79.5% (191 of 240 strings)

Translated using Weblate (Korean)

Currently translated at 54.6% (1785 of 3265 strings)

Translated using Weblate (Korean)

Currently translated at 88.8% (48 of 54 strings)

Translated using Weblate (Korean)

Currently translated at 89.3% (42 of 47 strings)

Translated using Weblate (Korean)

Currently translated at 93.9% (373 of 397 strings)

Translated using Weblate (Korean)

Currently translated at 54.9% (50 of 91 strings)

Translated using Weblate (German)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (German)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 73.0% (179 of 245 strings)

Translated using Weblate (French)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (German)

Currently translated at 99.1% (243 of 245 strings)

Translated using Weblate (French)

Currently translated at 99.5% (244 of 245 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 62.0% (152 of 245 strings)

Translated using Weblate (Indonesian)

Currently translated at 73.4% (180 of 245 strings)

Translated using Weblate (Indonesian)

Currently translated at 96.0% (861 of 896 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 62.0% (152 of 245 strings)

Translated using Weblate (German)

Currently translated at 98.7% (242 of 245 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Spanish)

Currently translated at 99.5% (244 of 245 strings)

Translated using Weblate (Portuguese)

Currently translated at 33.7% (82 of 243 strings)

Translated using Weblate (Portuguese)

Currently translated at 73.3% (602 of 821 strings)

Translated using Weblate (Portuguese)

Currently translated at 56.0% (51 of 91 strings)

Translated using Weblate (German)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (German)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.2% (107 of 110 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (896 of 896 strings)

Co-authored-by: César Orlando Pallares Delgado <copdeb@gmail.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Diego Benitez <diego.benitez@bigpond.com>
Co-authored-by: Finrod <963505255@qq.com>
Co-authored-by: ForbiddenFigs <sorautai@outlook.com>
Co-authored-by: Hexe des Windes (she/her) <krausanna1@gmail.com>
Co-authored-by: Icaro <icaro.mascarenhas@outlook.com>
Co-authored-by: Ikmal <ikmal.s.16@gmail.com>
Co-authored-by: Jackal <qwerty70244@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Katharina <katharinaanna.wilding@gmail.com>
Co-authored-by: Leslie Munguía <moongeeuh@gmail.com>
Co-authored-by: Lio Zam <zerofux@web.de>
Co-authored-by: Marius <mariusschmid11@gmail.com>
Co-authored-by: Miya <baddybadges@gmail.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Raul Ernesto Ceron Lara <raztreuzz1234@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: 小王 <963505255@qq.com>
Co-authored-by: 이채린 <cofls1256@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/cs/
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/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/id/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt/
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/challenge/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/character/es/
Translate-URL: https://translate.habitica.com/projects/habitica/character/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/character/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/character/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
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/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/es/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/death/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/ko/
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/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/id/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/front/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/hu/
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/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/noscript/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/de/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/es/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/es/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ko/
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/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Messages
Translation: Habitica/Noscript
Translation: Habitica/Npc
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Settings
Translation: Habitica/Spells
Translation: Habitica/Subscriber

5.33.3

March 2025 Content Build (#15392)

* build: March 2025 css, backgrounds, subscriber gear, armoire

* build: March 2025 quests, seasonal gear, various fixes

* fix: fix string

* fix: fixes to string errors

* fix: string fixes

wait for mongoose connection on timetravel

rework broken cron recovery

remove lodash from cron code

remove old cron notification

Simplify cron code

fix unit tests

Remove unnecessary user fetch

Further code simplification

fix test check

lint fix

disable world boss calculation during cron for now

prevent saving user twice in paralllel when leaving group plan

correctly call cron in api call

remove console

fix tests failing

mark cronSignature as modified

fix test

Translated using Weblate (Spanish)

Currently translated at 99.5% (3288 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 99.5% (832 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (German)

Currently translated at 98.8% (826 of 836 strings)

Translated using Weblate (Russian)

Currently translated at 40.8% (100 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (French)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Russian)

Currently translated at 40.4% (99 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 40.0% (98 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 40.0% (98 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 39.1% (96 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 91.2% (219 of 240 strings)

Translated using Weblate (Russian)

Currently translated at 38.7% (95 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 38.7% (95 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 38.7% (95 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 37.5% (92 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 37.1% (91 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 36.7% (90 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Russian)

Currently translated at 90.8% (218 of 240 strings)

Translated using Weblate (Russian)

Currently translated at 90.8% (218 of 240 strings)

Translated using Weblate (Russian)

Currently translated at 36.3% (89 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 36.3% (89 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Russian)

Currently translated at 99.3% (893 of 899 strings)

Translated using Weblate (Russian)

Currently translated at 99.2% (892 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 99.4% (831 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 62.6% (2068 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.9% (804 of 821 strings)

Translated using Weblate (Portuguese)

Currently translated at 72.0% (602 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 99.1% (829 of 836 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.4% (885 of 899 strings)

Translated using Weblate (Portuguese)

Currently translated at 99.6% (896 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 57.9% (1915 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.4% (800 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 57.6% (1903 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 57.5% (1900 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.0% (797 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Bulgarian)

Currently translated at 84.0% (79 of 94 strings)

Translated using Weblate (Bulgarian)

Currently translated at 84.0% (79 of 94 strings)

Translated using Weblate (Spanish)

Currently translated at 98.4% (823 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 98.7% (3263 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.8% (795 of 821 strings)

Translated using Weblate (Spanish)

Currently translated at 98.3% (822 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 98.5% (3256 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.8% (795 of 821 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.8% (795 of 821 strings)

Translated using Weblate (French)

Currently translated at 98.4% (823 of 836 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.5% (793 of 821 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (French)

Currently translated at 99.8% (3297 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.4% (792 of 821 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (French)

Currently translated at 99.3% (3280 of 3303 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (897 of 899 strings)

Translated using Weblate (French)

Currently translated at 99.1% (3275 of 3303 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (897 of 899 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (897 of 899 strings)

Translated using Weblate (German)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (897 of 899 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.6% (3187 of 3265 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (897 of 899 strings)

Translated using Weblate (French)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Hungarian)

Currently translated at 58.1% (1898 of 3265 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 62.8% (154 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.4% (792 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.2% (378 of 397 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (896 of 896 strings)

Co-authored-by: Anna <shiloanna007@gmail.com>
Co-authored-by: Besogon <victoria_murka@mail.ru>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: ForbiddenFigs <sorautai@outlook.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Nell Chant <doubletailor@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: razil <boss.razmarin@gmail.com>
Co-authored-by: 小王 <963505255@qq.com>
Co-authored-by: 海岛钓鱼佬 <963505255@qq.com>
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/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt/
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/communityguidelines/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/death/hu/
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/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/bg/
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/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hu/
Translation: Habitica/Backgrounds
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Death
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Limited
Translation: Habitica/Messages
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Subscriber

5.34.0

Update test.yml (#15397)

combined messages restyling - next round (#15386)

* split component prepare new views / states

* extract empty and disabled state as components

* fix empty state mail icon

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

* extract autoCompleteHelper.js

* style header + start new message input

* style plus button + focus input

* state logic, types for sanity

* WIP PM new Message started

* add /members/username test

* first design changes to messageCard

* delete private message or chat - based on the mode

* copy as todo

* mention links to modal

* report chat or private message

* WIP likeButton

* likeButton styling

* hide like on private message cards

* fix unit test

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

* style changes

* menu position + like button width

* dropdown items background + like font

* fix like button padding

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

* restyle system messages

* Dropdown Radius and Padding

* WIP system messages

* fix lint

* copy delta commit of allowing liking own private messages

* enable liking private messages

* fix menu non hovered item icon color

* fix import path

* ignore background on system messages

* requested changes + migration

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

* migration based on users pagination

* fix(migration): use Promise.all

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

* check for array

* use rest operator ...

* skip sorting to get the users

* remove migration, disable like for private messages without uniqueMessageId

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

* add a limit 2 get the messages by uniqueId

* Adding a simple server start script

* remove pinned nodemon dep

* fix inbox controller/tests

* fix / requested style changes

* fix empty state padding /

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

* Hourglass Simplification (#15323)

* begin removing obsolete tests

* begin refactoring

* update cron tests

* cleanup

* finish basic implementation of new logic

* add more subscription tests

* subscription test improvements

* return nextHourglassDate again

* fix gem limit

* fix(test): short circuit this.

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

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

* fix hourglass count

* Fix hourglass logic for upgrades

* fix admin panel display

* WIP(subs): extant Stripe state

* fix admin panel strings

* fix missing transaction type

* add new field for cumulative subscription count

* show date for hourglass bonus if it was received

* fix test

* feat(subscription): max Gems progress readout

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

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

* fix(stripe): correct redirect after success

* Admin panel display fixes

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

* fix issue with promo hourglasses

* fix(subscription): update layout when gifting

* fix(subscriptions): more gift layout revisions

* fix(subscriptions): minor visual updates

* fix(subs): pass autoRenews through Stripe

* fix(subs): gifts DON't renew

* fix(lint): unnecessary ternary

* fix(lint): do negate object ig

* fix(subs): try again on gifts

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

* fix bug with incorrectly giving HG bonus

* remove only

* fix test

* fix test

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

* fix(subs): fix typeError

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

---------

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

* chore(sprites): update subproject

* fix(layout): tighten cancellation note

* fix(subs): Google wording and HG escape

* chore(testing): fake g1g1 dates

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

* fix(subs): center next hourglass message

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

* fix(git): remove changes from old develop

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

This reverts commit 0e30f7df00.

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

* adding an empty loading state, hiding

* fought the avatar arch nemesis again

* fix chatMessages (party chat) message spacing

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

* show disabled private messages top panel

* fix font color

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

* fix(lint): add missing prop defaults

* fix(lint): object default should be fn

* fix(chat): correct grammar in error

* remove weapon position relative

* revert most of avatar.vue changes, add back weapons in chat message UI

* show date tooltip above system / skill messages

* fix toggle disable icon position

* trivial CSS cleanup

* fix(typo): English syntax in test

* chore(test): small style cleanup

* chore(logging): revert debug function

* chore(debug): remove timers from inbox like

---------

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

* improve method signature

* add fallback

* syntax fix

* fix merge error

* facepalm

---------

Co-authored-by: SabreCat <sabe@habitica.com>
Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-04-03 12:16:36 -05:00
Kalista Payne
5743fb86b0 5.35.1 2025-04-02 16:45:23 -05:00
Weblate
5443bf2459 Translated using Weblate (Russian)
Currently translated at 89.8% (2977 of 3315 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (900 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Ukrainian)

Currently translated at 84.8% (224 of 264 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.4% (419 of 430 strings)

Translated using Weblate (Ukrainian)

Currently translated at 58.4% (1939 of 3315 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 93.9% (3114 of 3315 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 63.6% (156 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 84.2% (2793 of 3315 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 98.3% (239 of 243 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 98.4% (256 of 260 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Ukrainian)

Currently translated at 97.0% (875 of 902 strings)

Translated using Weblate (Indonesian)

Currently translated at 96.8% (874 of 902 strings)

Translated using Weblate (Slovak)

Currently translated at 81.3% (109 of 134 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Slovak)

Currently translated at 68.2% (577 of 845 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (408 of 408 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Italian)

Currently translated at 11.8% (29 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (German)

Currently translated at 99.9% (3314 of 3315 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Korean)

Currently translated at 79.0% (713 of 902 strings)

Translated using Weblate (Korean)

Currently translated at 95.4% (232 of 243 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Korean)

Currently translated at 75.4% (681 of 902 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (German)

Currently translated at 99.9% (3313 of 3315 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (German)

Currently translated at 99.9% (3312 of 3315 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.2% (805 of 845 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Polish)

Currently translated at 25.3% (62 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (German)

Currently translated at 99.7% (3308 of 3315 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Polish)

Currently translated at 24.0% (59 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (408 of 408 strings)

Co-authored-by: Alice Haida <alicehaida.work@gmail.com>
Co-authored-by: Bernardo Oliveira Abrão <bernardooliveiraabrao@gmail.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Emilia Samantha T <emilia.s.thomas@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Jan Willem Middag <jwmiddag@gmail.com>
Co-authored-by: Jan met de Pet <stijn.koppers@gmail.com>
Co-authored-by: Kim DG <dgkimuniversity@gmail.com>
Co-authored-by: Klaudia Kasprzyk <kasprzyk.klaudiax@gmail.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Pasquale Bosso <protagora87@gmail.com>
Co-authored-by: Raul Ernesto Ceron Lara <raztreuzz1234@gmail.com>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: 沧浪 <963505255@qq.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/id/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/character/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/death/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/front/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es_419/
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/generic/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/uk/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
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/Loginincentives
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2025-04-02 21:00:16 +02:00
Phillip Thelen
c0d5566417 Make sure no userhistory exists before initializing it (#15415) 2025-03-26 13:37:52 -05:00
negue
ded71b46c5 use the correct like icon/boldness when not the current user liked it (#15409) 2025-03-25 12:12:44 -05:00
Kalista Payne
9693ad321c 5.35.0 2025-03-24 15:40:18 -05:00
Weblate
dd3679f329 Translated using Weblate (Hungarian)
Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (French)

Currently translated at 100.0% (408 of 408 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3315 of 3315 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Italian)

Currently translated at 11.4% (28 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (408 of 408 strings)

Translated using Weblate (German)

Currently translated at 100.0% (408 of 408 strings)

Translated using Weblate (Slovak)

Currently translated at 75.3% (101 of 134 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3315 of 3315 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (408 of 408 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (408 of 408 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (902 of 902 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3315 of 3315 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.7% (429 of 430 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 93.4% (3097 of 3315 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Polish)

Currently translated at 23.2% (57 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 11.0% (27 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Spanish)

Currently translated at 99.5% (841 of 845 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.5% (395 of 405 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (902 of 902 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (902 of 902 strings)

Translated using Weblate (German)

Currently translated at 100.0% (902 of 902 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3315 of 3315 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (Hungarian)

Currently translated at 99.8% (3310 of 3315 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (902 of 902 strings)

Translated using Weblate (Hungarian)

Currently translated at 99.2% (262 of 264 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Polish)

Currently translated at 22.4% (55 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Spanish)

Currently translated at 99.1% (838 of 845 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3315 of 3315 strings)

Translated using Weblate (French)

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (German)

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (French)

Currently translated at 99.9% (3314 of 3315 strings)

Translated using Weblate (French)

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Spanish)

Currently translated at 99.0% (837 of 845 strings)

Translated using Weblate (French)

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (French)

Currently translated at 100.0% (902 of 902 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (260 of 260 strings)

Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Klaudia Kasprzyk <kasprzyk.klaudiax@gmail.com>
Co-authored-by: Larissa dos Santos Brasil <larissabrasil009@gmail.com>
Co-authored-by: Pasquale Bosso <protagora87@gmail.com>
Co-authored-by: Rivian <rebecca.jason.books@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/character/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/content/de/
Translate-URL: https://translate.habitica.com/projects/habitica/content/es/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
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/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/en_GB/
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/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hu/
Translation: Habitica/Backgrounds
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Npc
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Subscriber
2025-03-24 21:33:59 +01:00
Kalista Payne
f3029953dc Squashed commit of the following:
commit bde71cc45d
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Mar 20 15:25:11 2025 -0500

    fix(potions): add missing Cryptid release date

commit 1a50413402
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Tue Mar 18 16:52:19 2025 -0500

    fix(wacky): revise text, address linting

commit 2d49a16f55
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Mar 18 16:05:17 2025 +0100

    Fix displaying countdown for recurring event items

commit cf20b30975
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Fri Mar 14 18:01:37 2025 -0500

    fix(lint): line length

commit e39e490e41
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Fri Mar 14 17:55:12 2025 -0500

    fix(foolin): correct animation and end date of potions

commit 164e2eefdd
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Mar 13 17:25:14 2025 -0500

    fix(event): Panda Cub typo, shift start date

commit 394c922287
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Mar 13 17:02:12 2025 -0500

    fix(test): account for addition of Cryptid

commit bea9e40338
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Mar 13 16:52:37 2025 -0500

    feat(event): April Fools 2025
2025-03-21 15:02:31 -05:00
negue
01881b2fd8 Avatar Component: fix pet position (#15414) 2025-03-21 14:55:32 -05:00
Natalie
11a22d0f5d April 2025 content build (#15411)
* chore: April 2025 CSS

* chore: April 2025 subscriber and armoire items, backgrounds

* chore: Update April 2025 subscriber items

* chore: April 2025 CSS fix

* chore: April 2025 pet quest and magic hatching potion

* fix: April 2025 fixes

* fix: typo

* fix: typo

* fix(typo): whitespace

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-03-20 14:30:51 -05:00
Kalista Payne
5f9bf07045 5.34.4 2025-03-20 11:46:03 -05:00
Weblate
719c03e2f5 Translated using Weblate (German)
Currently translated at 99.9% (3302 of 3303 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Dutch)

Currently translated at 78.7% (2600 of 3303 strings)

Translated using Weblate (Dutch)

Currently translated at 86.4% (210 of 243 strings)

Translated using Weblate (Dutch)

Currently translated at 95.0% (173 of 182 strings)

Translated using Weblate (Dutch)

Currently translated at 25.7% (63 of 245 strings)

Translated using Weblate (Dutch)

Currently translated at 60.4% (55 of 91 strings)

Translated using Weblate (Dutch)

Currently translated at 67.6% (176 of 260 strings)

Translated using Weblate (Dutch)

Currently translated at 67.6% (176 of 260 strings)

Translated using Weblate (Dutch)

Currently translated at 76.0% (200 of 263 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Dutch)

Currently translated at 94.5% (172 of 182 strings)

Translated using Weblate (Dutch)

Currently translated at 24.4% (60 of 245 strings)

Translated using Weblate (Dutch)

Currently translated at 57.1% (52 of 91 strings)

Translated using Weblate (Dutch)

Currently translated at 91.4% (43 of 47 strings)

Translated using Weblate (Dutch)

Currently translated at 91.4% (43 of 47 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Dutch)

Currently translated at 56.0% (51 of 91 strings)

Translated using Weblate (Dutch)

Currently translated at 87.5% (7 of 8 strings)

Translated using Weblate (Dutch)

Currently translated at 78.6% (2599 of 3303 strings)

Translated using Weblate (Dutch)

Currently translated at 23.6% (58 of 245 strings)

Translated using Weblate (Dutch)

Currently translated at 23.6% (58 of 245 strings)

Translated using Weblate (Dutch)

Currently translated at 56.0% (51 of 91 strings)

Translated using Weblate (Dutch)

Currently translated at 23.2% (57 of 245 strings)

Translated using Weblate (Dutch)

Currently translated at 23.2% (57 of 245 strings)

Translated using Weblate (Dutch)

Currently translated at 54.9% (50 of 91 strings)

Translated using Weblate (Dutch)

Currently translated at 66.9% (174 of 260 strings)

Translated using Weblate (Dutch)

Currently translated at 72.5% (312 of 430 strings)

Translated using Weblate (Dutch)

Currently translated at 22.4% (55 of 245 strings)

Translated using Weblate (Dutch)

Currently translated at 84.5% (707 of 836 strings)

Translated using Weblate (Dutch)

Currently translated at 66.5% (173 of 260 strings)

Translated using Weblate (Japanese)

Currently translated at 99.8% (898 of 899 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Japanese)

Currently translated at 97.8% (46 of 47 strings)

Translated using Weblate (Romanian)

Currently translated at 4.4% (11 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (German)

Currently translated at 99.9% (3300 of 3303 strings)

Translated using Weblate (French)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (French)

Currently translated at 100.0% (260 of 260 strings)

Co-authored-by: Alison Alex <spamkari@hotmail.com>
Co-authored-by: Anna Wolthuis <annawolthuis20@gmail.com>
Co-authored-by: Avril Manoah <manoah.avril@gmail.com>
Co-authored-by: Gabone <gabyjoaca2@gmail.com>
Co-authored-by: Julius Eikmans <jcs.e@icloud.com>
Co-authored-by: M Timmermans <merel11timmermans@gmail.com>
Co-authored-by: Mika <isekai.chr@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/character/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/front/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/nl/
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/Generic
Translation: Habitica/Groups
Translation: Habitica/Loginincentives
Translation: Habitica/Overview
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Settings
Translation: Habitica/Subscriber
2025-03-20 15:11:05 +01:00
Phillip Thelen
379afa9554 Improve Adminpanel with local logs (#15404)
* log armoire, quoest response and cron events to history

* show user history in admin panel

* allow stats to be edited from admin panel

* Improve admin panel stats input

* improve setting client in history

* fix tests

* fix lint

* fix armoire buying issue

* Improve hero saving

* Formatting fix

* Improve user history logging

* allow class to be changed from admin panel

* make terminating subscriptions easier

* support decimal extraMonths

* Fix editing some achievements in admin panel

* log if a user invites party to quest

* Log more quest events into user history

* make userhistory length configurable

* fix some numbered achievements

* fix extraMonths field

* Automatically set up group plan subs with admin panel

* show party info nicer in admin panel

* improve admin panel sub handling

* add missing brace

* display when there are unsaved changes

* fix setting group plan

* fix showing group id

* Display group plan info in admin panel

* fix setting hourglass promo date

* Improve termination handling in admin panel

* reload data after certain save events in admin panel

* remove console

* fix plan.extraMonths not being reset if terminating a sub

* add more options when cancelling subs

* reload data after group plan change

* Add a way to remove users from a party

* fix issue with removing user from party

* pass party id correctly

* correctly call async function

* Improve sub display in admin panel

* fix line length

* fix line

* shorter

* plaid

* fix(lint): vue code style

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-03-17 16:48:21 -05:00
Kalista Payne
dbc23e89b8 chore(git): update subproj 2025-03-17 16:25:06 -05:00
negue
0c6e254742 correct async/await usages, improve email tests (#15408) 2025-03-17 16:11:38 -05:00
Kalista Payne
8327e69bdd 5.34.3 2025-03-17 09:00:49 -05:00
Kalista Payne
2d953f4f59 chore(git): update subproject 2025-03-17 09:00:44 -05:00
Weblate
7118d63949 Merge branch 'origin/develop' into Weblate. 2025-03-17 14:58:58 +01:00
Weblate
20af8d038e Translated using Weblate (German)
Currently translated at 99.8% (3298 of 3303 strings)

Translated using Weblate (Portuguese)

Currently translated at 72.2% (604 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Slovak)

Currently translated at 73.1% (98 of 134 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (German)

Currently translated at 99.7% (3296 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 89.7% (236 of 263 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.9% (273 of 276 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.7% (3191 of 3265 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 68.9% (2278 of 3303 strings)

Translated using Weblate (German)

Currently translated at 99.7% (3294 of 3303 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 87.0% (229 of 263 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.6% (259 of 260 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (German)

Currently translated at 99.6% (3292 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (German)

Currently translated at 99.6% (3290 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (899 of 899 strings)

Co-authored-by: AlexFad <2077505931@qq.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Samir Mahmoud Samir <moonlordslayer080@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: 沧浪 <963505255@qq.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/character/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/character/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/inventory/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Inventory
Translation: Habitica/Limited
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Subscriber
2025-03-17 14:58:47 +01:00
Phillip Thelen
3d9dfbb5e1 fix jade potion release date (#15413) 2025-03-17 08:55:47 -05:00
Natalie
ae0b966f45 fix: update currency display in setting for change class (#15391)
* fix: add neededCurrencyOnly prop

* fix: worked on logic for displaying only gems

* fix: removed gold & hourglasses if neededCurrencyOnly is true

* fix: aligned gem count properly

* fix: updates based on some comments (all comments answered)

* fix: working on displaying only required currency in a new array

* chore: trying to figure out currency logic

* trying to use .find() now

* remove unneeded line of code

* still working on finding/filtering the currency

* trying to move requiredCurrency into a new function (?!)

* fix: added logic to filter for a single currency and fixed CSS

* fix: clean up code

* fix: really clean up code, sheesh

* fix: updated per comments on PR

* fix(style): vertically align elements in your-balance

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-03-13 15:01:50 -05:00
Kalista Payne
cef8a34c06 5.34.2 2025-03-12 09:01:14 -05:00
Weblate
6432823eec Translated using Weblate (Hungarian)
Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 17.1% (42 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (German)

Currently translated at 99.5% (3288 of 3303 strings)

Co-authored-by: Eddy Nottingham <habitica.com.scone566@simplelogin.com>
Co-authored-by: Sujay Thomas <mail.sujaythomas@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/character/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/character/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/content/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hu/
Translation: Habitica/Backgrounds
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Questscontent
2025-03-12 09:49:58 +01:00
Phillip Thelen
563b780d85 fix system messages not being translated (#15405) 2025-03-11 16:58:39 -05:00
Phillip Thelen
aa9b1b2cac More Sprites (#15400)
* remove need for quest_ notif_ and inventory_quest_ in css

* fix test
2025-03-11 16:48:19 -05:00
Kalista Payne
401e541b86 5.34.1 2025-03-11 09:56:21 -05:00
Weblate
c13bed3bad Translated using Weblate (Hungarian)
Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (German)

Currently translated at 99.4% (3286 of 3303 strings)

Translated using Weblate (Indonesian)

Currently translated at 95.6% (109 of 114 strings)

Translated using Weblate (Indonesian)

Currently translated at 98.2% (57 of 58 strings)

Translated using Weblate (Indonesian)

Currently translated at 88.1% (379 of 430 strings)

Translated using Weblate (Indonesian)

Currently translated at 73.3% (2424 of 3303 strings)

Translated using Weblate (Indonesian)

Currently translated at 89.3% (217 of 243 strings)

Translated using Weblate (Indonesian)

Currently translated at 74.2% (182 of 245 strings)

Translated using Weblate (Indonesian)

Currently translated at 83.5% (76 of 91 strings)

Translated using Weblate (Indonesian)

Currently translated at 89.0% (98 of 110 strings)

Translated using Weblate (Indonesian)

Currently translated at 96.3% (866 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Turkish)

Currently translated at 31.0% (76 of 245 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Turkish)

Currently translated at 82.3% (214 of 260 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Italian)

Currently translated at 10.6% (26 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (German)

Currently translated at 99.4% (3284 of 3303 strings)

Translated using Weblate (Italian)

Currently translated at 10.2% (25 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Japanese)

Currently translated at 91.5% (765 of 836 strings)

Translated using Weblate (German)

Currently translated at 99.5% (242 of 243 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.4% (419 of 430 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.7% (240 of 243 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (German)

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (260 of 260 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Hungarian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (French)

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (French)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (47 of 47 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Hungarian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (German)

Currently translated at 99.3% (3280 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Russian)

Currently translated at 91.3% (764 of 836 strings)

Translated using Weblate (Korean)

Currently translated at 98.8% (165 of 167 strings)

Translated using Weblate (Korean)

Currently translated at 54.1% (1788 of 3303 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Hebrew)

Currently translated at 55.7% (241 of 432 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (German)

Currently translated at 99.2% (3278 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hebrew)

Currently translated at 66.7% (558 of 836 strings)

Translated using Weblate (Hebrew)

Currently translated at 87.2% (96 of 110 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (263 of 263 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Hebrew)

Currently translated at 88.7% (213 of 240 strings)

Translated using Weblate (Hebrew)

Currently translated at 83.7% (201 of 240 strings)

Translated using Weblate (Italian)

Currently translated at 9.7% (24 of 245 strings)

Translated using Weblate (German)

Currently translated at 99.1% (3276 of 3303 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Italian)

Currently translated at 9.3% (23 of 245 strings)

Translated using Weblate (German)

Currently translated at 99.1% (3274 of 3303 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.2% (258 of 260 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.6% (3187 of 3265 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 91.3% (764 of 836 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 84.7% (223 of 263 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 93.5% (3089 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3272 of 3303 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3271 of 3303 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.5% (399 of 401 strings)

Translated using Weblate (Italian)

Currently translated at 8.9% (22 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Hebrew)

Currently translated at 24.2% (67 of 276 strings)

Translated using Weblate (Hebrew)

Currently translated at 80.0% (192 of 240 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 15.5% (38 of 245 strings)

Translated using Weblate (German)

Currently translated at 99.5% (244 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 93.4% (3087 of 3303 strings)

Translated using Weblate (German)

Currently translated at 99.6% (833 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Bulgarian)

Currently translated at 98.4% (190 of 193 strings)

Translated using Weblate (German)

Currently translated at 98.9% (3268 of 3303 strings)

Translated using Weblate (Hebrew)

Currently translated at 39.3% (1301 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Hebrew)

Currently translated at 39.3% (1301 of 3303 strings)

Translated using Weblate (Hebrew)

Currently translated at 83.3% (45 of 54 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.5% (379 of 401 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Hebrew)

Currently translated at 78.9% (90 of 114 strings)

Translated using Weblate (Hebrew)

Currently translated at 62.5% (5 of 8 strings)

Translated using Weblate (Hebrew)

Currently translated at 55.7% (241 of 432 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Hebrew)

Currently translated at 50.3% (131 of 260 strings)

Translated using Weblate (German)

Currently translated at 99.4% (831 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (French)

Currently translated at 100.0% (263 of 263 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Hebrew)

Currently translated at 61.4% (70 of 114 strings)

Translated using Weblate (Hebrew)

Currently translated at 50.0% (4 of 8 strings)

Translated using Weblate (French)

Currently translated at 100.0% (276 of 276 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Hebrew)

Currently translated at 1.2% (3 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Hebrew)

Currently translated at 99.7% (400 of 401 strings)

Translated using Weblate (French)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Hebrew)

Currently translated at 50.3% (131 of 260 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (263 of 263 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (276 of 276 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (German)

Currently translated at 100.0% (263 of 263 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (German)

Currently translated at 100.0% (276 of 276 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 99.1% (113 of 114 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Hungarian)

Currently translated at 96.4% (3185 of 3303 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Hungarian)

Currently translated at 63.7% (2106 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (German)

Currently translated at 100.0% (401 of 401 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.0% (130 of 134 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.1% (233 of 245 strings)

Translated using Weblate (Portuguese)

Currently translated at 94.5% (86 of 91 strings)

Translated using Weblate (Portuguese)

Currently translated at 98.7% (392 of 397 strings)

Translated using Weblate (Portuguese)

Currently translated at 98.9% (191 of 193 strings)

Translated using Weblate (Portuguese)

Currently translated at 99.0% (109 of 110 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 93.3% (3084 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 63.4% (2096 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 93.1% (3078 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 63.2% (2089 of 3303 strings)

Translated using Weblate (German)

Currently translated at 98.8% (3266 of 3303 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 92.6% (227 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 92.6% (227 of 245 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 62.6% (2068 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Co-authored-by: Annika Nuding <annikanuding@gmail.com>
Co-authored-by: Chen Shaham <chenshaham@gmail.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Deleted User <noreply+1293@weblate.org>
Co-authored-by: Filip Betko <filipbetko@gmail.com>
Co-authored-by: Filip Kober <koberfilip2@gmail.com>
Co-authored-by: ForbiddenFigs <sorautai@outlook.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Kacchan <h.mrena97@gmail.com>
Co-authored-by: Kovács Máté <kovacsur10@gmail.com>
Co-authored-by: Miya <baddybadges@gmail.com>
Co-authored-by: Muhammad Naufal Ramadhan <naufalramadhan281004@gmail.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Nell Chant <doubletailor@gmail.com>
Co-authored-by: Pasquale Bosso <protagora87@gmail.com>
Co-authored-by: Siying Li <lsy68653654@qq.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Sujay Thomas <mail.sujaythomas@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Wyatt <1697570085@qq.com>
Co-authored-by: XiaoWan <2558968618@qq.com>
Co-authored-by: Yaşar Efe Çelik <yasar.123.sevda@gmail.com>
Co-authored-by: 沧浪 <963505255@qq.com>
Co-authored-by: 海岛钓鱼佬 <963505255@qq.com>
Co-authored-by: 박제균 <parkjekyun@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/he/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/id/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/he/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/id/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/character/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/character/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/character/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/character/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/character/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/id/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/content/de/
Translate-URL: https://translate.habitica.com/projects/habitica/content/es/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/he/
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/content/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/death/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/he/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/de/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/he/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/id/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/front/hu/
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/he/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/id/
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/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/de/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/es/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/he/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/id/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/es/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/he/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/id/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/inventory/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/de/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/es/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/he/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/id/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/he/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/he/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/id/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/he/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/zh_Hans/
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/he/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ja/
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/rebirth/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/he/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/hu/
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
2025-03-11 15:54:34 +01:00
negue
b3c4817fb4 Private Messages: improve "scrollToBottom" logic 2025-03-05 22:18:38 +01:00
negue
7c9c45ac5f combined messages restyling - next round (#15386)
* split component prepare new views / states

* extract empty and disabled state as components

* fix empty state mail icon

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

* extract autoCompleteHelper.js

* style header + start new message input

* style plus button + focus input

* state logic, types for sanity

* WIP PM new Message started

* add /members/username test

* first design changes to messageCard

* delete private message or chat - based on the mode

* copy as todo

* mention links to modal

* report chat or private message

* WIP likeButton

* likeButton styling

* hide like on private message cards

* fix unit test

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

* style changes

* menu position + like button width

* dropdown items background + like font

* fix like button padding

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

* restyle system messages

* Dropdown Radius and Padding

* WIP system messages

* fix lint

* copy delta commit of allowing liking own private messages

* enable liking private messages

* fix menu non hovered item icon color

* fix import path

* ignore background on system messages

* requested changes + migration

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

* migration based on users pagination

* fix(migration): use Promise.all

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

* check for array

* use rest operator ...

* skip sorting to get the users

* remove migration, disable like for private messages without uniqueMessageId

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

* add a limit 2 get the messages by uniqueId

* Adding a simple server start script

* remove pinned nodemon dep

* fix inbox controller/tests

* fix / requested style changes

* fix empty state padding /

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

* Hourglass Simplification (#15323)

* begin removing obsolete tests

* begin refactoring

* update cron tests

* cleanup

* finish basic implementation of new logic

* add more subscription tests

* subscription test improvements

* return nextHourglassDate again

* fix gem limit

* fix(test): short circuit this.

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

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

* fix hourglass count

* Fix hourglass logic for upgrades

* fix admin panel display

* WIP(subs): extant Stripe state

* fix admin panel strings

* fix missing transaction type

* add new field for cumulative subscription count

* show date for hourglass bonus if it was received

* fix test

* feat(subscription): max Gems progress readout

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

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

* fix(stripe): correct redirect after success

* Admin panel display fixes

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

* fix issue with promo hourglasses

* fix(subscription): update layout when gifting

* fix(subscriptions): more gift layout revisions

* fix(subscriptions): minor visual updates

* fix(subs): pass autoRenews through Stripe

* fix(subs): gifts DON't renew

* fix(lint): unnecessary ternary

* fix(lint): do negate object ig

* fix(subs): try again on gifts

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

* fix bug with incorrectly giving HG bonus

* remove only

* fix test

* fix test

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

* fix(subs): fix typeError

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

---------

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

* chore(sprites): update subproject

* fix(layout): tighten cancellation note

* fix(subs): Google wording and HG escape

* chore(testing): fake g1g1 dates

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

* fix(subs): center next hourglass message

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

* fix(git): remove changes from old develop

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

This reverts commit 0e30f7df00.

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

* adding an empty loading state, hiding

* fought the avatar arch nemesis again

* fix chatMessages (party chat) message spacing

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

* show disabled private messages top panel

* fix font color

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

* fix(lint): add missing prop defaults

* fix(lint): object default should be fn

* fix(chat): correct grammar in error

* remove weapon position relative

* revert most of avatar.vue changes, add back weapons in chat message UI

* show date tooltip above system / skill messages

* fix toggle disable icon position

* trivial CSS cleanup

* fix(typo): English syntax in test

* chore(test): small style cleanup

* chore(logging): revert debug function

* chore(debug): remove timers from inbox like

---------

Co-authored-by: SabreCat <sabe@habitica.com>
Co-authored-by: Kalista Payne <sabrecat@gmail.com>
Co-authored-by: Phillip Thelen <phillip@habitica.com>
2025-03-04 17:00:24 -06:00
Phillip Thelen
95142e3684 Update test.yml (#15397) 2025-03-04 16:19:44 -06:00
Kalista Payne
dc1cce6ddb 5.34.0 2025-02-20 15:29:15 -06:00
Weblate
43cf77f33c Translated using Weblate (Spanish)
Currently translated at 99.5% (3288 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 99.5% (832 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (German)

Currently translated at 98.8% (826 of 836 strings)

Translated using Weblate (Russian)

Currently translated at 40.8% (100 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (French)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Russian)

Currently translated at 40.4% (99 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 40.0% (98 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 40.0% (98 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 39.1% (96 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 91.2% (219 of 240 strings)

Translated using Weblate (Russian)

Currently translated at 38.7% (95 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 38.7% (95 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 38.7% (95 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 37.5% (92 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 37.1% (91 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 36.7% (90 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Russian)

Currently translated at 90.8% (218 of 240 strings)

Translated using Weblate (Russian)

Currently translated at 90.8% (218 of 240 strings)

Translated using Weblate (Russian)

Currently translated at 36.3% (89 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 36.3% (89 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Russian)

Currently translated at 99.3% (893 of 899 strings)

Translated using Weblate (Russian)

Currently translated at 99.2% (892 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 99.4% (831 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 62.6% (2068 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.9% (804 of 821 strings)

Translated using Weblate (Portuguese)

Currently translated at 72.0% (602 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 99.1% (829 of 836 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.4% (885 of 899 strings)

Translated using Weblate (Portuguese)

Currently translated at 99.6% (896 of 899 strings)

Translated using Weblate (Hungarian)

Currently translated at 57.9% (1915 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.4% (800 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 57.6% (1903 of 3303 strings)

Translated using Weblate (Hungarian)

Currently translated at 57.5% (1900 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.0% (797 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (836 of 836 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Bulgarian)

Currently translated at 84.0% (79 of 94 strings)

Translated using Weblate (Bulgarian)

Currently translated at 84.0% (79 of 94 strings)

Translated using Weblate (Spanish)

Currently translated at 98.4% (823 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 98.7% (3263 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.8% (795 of 821 strings)

Translated using Weblate (Spanish)

Currently translated at 98.3% (822 of 836 strings)

Translated using Weblate (Spanish)

Currently translated at 98.5% (3256 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.8% (795 of 821 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.8% (795 of 821 strings)

Translated using Weblate (French)

Currently translated at 98.4% (823 of 836 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3303 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.5% (793 of 821 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (French)

Currently translated at 99.8% (3297 of 3303 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.4% (792 of 821 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (French)

Currently translated at 99.3% (3280 of 3303 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (897 of 899 strings)

Translated using Weblate (French)

Currently translated at 99.1% (3275 of 3303 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (897 of 899 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (897 of 899 strings)

Translated using Weblate (German)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (897 of 899 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.6% (3187 of 3265 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (897 of 899 strings)

Translated using Weblate (French)

Currently translated at 100.0% (899 of 899 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Hungarian)

Currently translated at 58.1% (1898 of 3265 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 62.8% (154 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.4% (792 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.2% (378 of 397 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (896 of 896 strings)

Co-authored-by: Anna <shiloanna007@gmail.com>
Co-authored-by: Besogon <victoria_murka@mail.ru>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: ForbiddenFigs <sorautai@outlook.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Nell Chant <doubletailor@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: razil <boss.razmarin@gmail.com>
Co-authored-by: 小王 <963505255@qq.com>
Co-authored-by: 海岛钓鱼佬 <963505255@qq.com>
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/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt/
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/communityguidelines/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/death/hu/
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/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/bg/
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/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hu/
Translation: Habitica/Backgrounds
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Death
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Limited
Translation: Habitica/Messages
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Subscriber
2025-02-20 22:28:02 +01:00
Natalie
93780d7056 March 2025 Content Build (#15392)
* build: March 2025 css, backgrounds, subscriber gear, armoire

* build: March 2025 quests, seasonal gear, various fixes

* fix: fix string

* fix: fixes to string errors

* fix: string fixes
2025-02-18 15:48:08 -06:00
Kalista Payne
2ad17d408e 5.33.3 2025-02-18 15:05:44 -06:00
Weblate
b0f7567367 Translated using Weblate (Japanese)
Currently translated at 93.0% (764 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 54.8% (1790 of 3265 strings)

Translated using Weblate (Hungarian)

Currently translated at 53.5% (1748 of 3265 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (Hungarian)

Currently translated at 52.1% (1704 of 3265 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 59.3% (532 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 79.3% (208 of 262 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 77.4% (2528 of 3265 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 93.0% (764 of 821 strings)

Translated using Weblate (French)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (French)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 94.8% (258 of 272 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.2% (378 of 397 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 82.8% (203 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (821 of 821 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.9% (377 of 397 strings)

Translated using Weblate (Hungarian)

Currently translated at 52.1% (1704 of 3265 strings)

Translated using Weblate (Hungarian)

Currently translated at 49.7% (122 of 245 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.1% (789 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (821 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 48.5% (119 of 245 strings)

Translated using Weblate (Hungarian)

Currently translated at 26.1% (64 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2 of 2 strings)

Translated using Weblate (Hungarian)

Currently translated at 8.9% (22 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Hungarian)

Currently translated at 96.2% (790 of 821 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.4% (784 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 91.5% (752 of 821 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (German)

Currently translated at 99.2% (133 of 134 strings)

Translated using Weblate (German)

Currently translated at 99.2% (133 of 134 strings)

Translated using Weblate (Czech)

Currently translated at 95.2% (159 of 167 strings)

Translated using Weblate (Russian)

Currently translated at 91.2% (2978 of 3265 strings)

Translated using Weblate (Russian)

Currently translated at 99.3% (890 of 896 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3265 of 3265 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3265 of 3265 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (French)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (French)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Korean)

Currently translated at 98.5% (132 of 134 strings)

Translated using Weblate (Korean)

Currently translated at 6.9% (17 of 245 strings)

Translated using Weblate (Korean)

Currently translated at 71.9% (645 of 896 strings)

Translated using Weblate (Korean)

Currently translated at 49.2% (129 of 262 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Korean)

Currently translated at 81.9% (77 of 94 strings)

Translated using Weblate (Korean)

Currently translated at 91.6% (153 of 167 strings)

Translated using Weblate (Korean)

Currently translated at 67.3% (291 of 432 strings)

Translated using Weblate (Korean)

Currently translated at 79.5% (191 of 240 strings)

Translated using Weblate (Korean)

Currently translated at 54.6% (1785 of 3265 strings)

Translated using Weblate (Korean)

Currently translated at 88.8% (48 of 54 strings)

Translated using Weblate (Korean)

Currently translated at 89.3% (42 of 47 strings)

Translated using Weblate (Korean)

Currently translated at 93.9% (373 of 397 strings)

Translated using Weblate (Korean)

Currently translated at 54.9% (50 of 91 strings)

Translated using Weblate (German)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (German)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 73.0% (179 of 245 strings)

Translated using Weblate (French)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (German)

Currently translated at 99.1% (243 of 245 strings)

Translated using Weblate (French)

Currently translated at 99.5% (244 of 245 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 62.0% (152 of 245 strings)

Translated using Weblate (Indonesian)

Currently translated at 73.4% (180 of 245 strings)

Translated using Weblate (Indonesian)

Currently translated at 96.0% (861 of 896 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 62.0% (152 of 245 strings)

Translated using Weblate (German)

Currently translated at 98.7% (242 of 245 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Spanish)

Currently translated at 99.5% (244 of 245 strings)

Translated using Weblate (Portuguese)

Currently translated at 33.7% (82 of 243 strings)

Translated using Weblate (Portuguese)

Currently translated at 73.3% (602 of 821 strings)

Translated using Weblate (Portuguese)

Currently translated at 56.0% (51 of 91 strings)

Translated using Weblate (German)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (German)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.2% (107 of 110 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (896 of 896 strings)

Co-authored-by: César Orlando Pallares Delgado <copdeb@gmail.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Diego Benitez <diego.benitez@bigpond.com>
Co-authored-by: Finrod <963505255@qq.com>
Co-authored-by: ForbiddenFigs <sorautai@outlook.com>
Co-authored-by: Hexe des Windes (she/her) <krausanna1@gmail.com>
Co-authored-by: Icaro <icaro.mascarenhas@outlook.com>
Co-authored-by: Ikmal <ikmal.s.16@gmail.com>
Co-authored-by: Jackal <qwerty70244@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Katharina <katharinaanna.wilding@gmail.com>
Co-authored-by: Leslie Munguía <moongeeuh@gmail.com>
Co-authored-by: Lio Zam <zerofux@web.de>
Co-authored-by: Marius <mariusschmid11@gmail.com>
Co-authored-by: Miya <baddybadges@gmail.com>
Co-authored-by: Natalie Luhrs <eilatan@gmail.com>
Co-authored-by: Raul Ernesto Ceron Lara <raztreuzz1234@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: 小王 <963505255@qq.com>
Co-authored-by: 이채린 <cofls1256@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/cs/
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/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/id/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt/
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/challenge/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/character/es/
Translate-URL: https://translate.habitica.com/projects/habitica/character/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/character/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/character/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/content/hu/
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/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/es/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/death/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/ko/
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/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/id/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/front/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/hu/
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/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/noscript/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/de/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/es/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/es/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ko/
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/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Messages
Translation: Habitica/Noscript
Translation: Habitica/Npc
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Settings
Translation: Habitica/Spells
Translation: Habitica/Subscriber
2025-02-18 22:03:17 +01:00
Phillip Thelen
3f2b1d3f79 Update .eslintrc.js (#15388)
Add `require-await` to eslint config
2025-02-11 12:05:28 -06:00
Phillip Thelen
29eb8ca10b log slow requests to loggly (#15364) 2025-02-11 12:05:06 -06:00
Phillip Thelen
8c71ca12b8 Support sprite version of armoire icon (#15354)
* Use sprite component for armoire sprite

* use gif version of armoire sprite

* fix(import): sprite component path

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-02-11 12:04:28 -06:00
Phillip Thelen
72a753626f Fix news related permission issues (#15287) 2025-02-11 12:04:00 -06:00
Phillip Thelen
35ebb12bf2 Fix achievement display in admin panel (#15326) 2025-02-11 12:02:50 -06:00
Kalista Payne
1ff418f62d 5.33.2 2025-02-06 13:53:00 -06:00
Weblate
e1aa437ea5 Translated using Weblate (German)
Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Portuguese)

Currently translated at 96.4% (864 of 896 strings)

Co-authored-by: Miya <baddybadges@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt/
Translation: Habitica/Backgrounds
2025-02-06 20:48:32 +01:00
Kalista Payne
2a4239bf3c fix(links): next round of wiki revisions 2025-02-05 09:18:22 -06:00
Kalista Payne
399563435b 5.33.1 2025-02-05 09:08:26 -06:00
Weblate
59f7e25c85 Translated using Weblate (Spanish)
Currently translated at 100.0% (821 of 821 strings)

Translated using Weblate (German)

Currently translated at 100.0% (821 of 821 strings)

Translated using Weblate (Japanese)

Currently translated at 87.0% (228 of 262 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 93.8% (107 of 114 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 18.1% (44 of 243 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 11.9% (29 of 243 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 88.1% (724 of 821 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 91.2% (104 of 114 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 7.4% (18 of 243 strings)

Translated using Weblate (Spanish)

Currently translated at 99.5% (817 of 821 strings)

Translated using Weblate (German)

Currently translated at 99.3% (816 of 821 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3265 of 3265 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3265 of 3265 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 98.2% (112 of 114 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 97.7% (131 of 134 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 69.1% (2257 of 3265 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 99.5% (239 of 240 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 16.4% (40 of 243 strings)

Translated using Weblate (German)

Currently translated at 99.9% (3264 of 3265 strings)

Translated using Weblate (Japanese)

Currently translated at 86.6% (227 of 262 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (272 of 272 strings)

Translated using Weblate (Japanese)

Currently translated at 97.9% (423 of 432 strings)

Translated using Weblate (German)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (German)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (German)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 14.8% (36 of 243 strings)

Translated using Weblate (German)

Currently translated at 99.1% (814 of 821 strings)

Translated using Weblate (German)

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 84.7% (222 of 262 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 84.3% (221 of 262 strings)

Translated using Weblate (German)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.0% (415 of 432 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.2% (3077 of 3265 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 63.7% (155 of 243 strings)

Translated using Weblate (German)

Currently translated at 99.0% (813 of 821 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 99.7% (396 of 397 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.7% (885 of 896 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (German)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Japanese)

Currently translated at 97.4% (265 of 272 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (60 of 60 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Japanese)

Currently translated at 98.7% (392 of 397 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Spanish)

Currently translated at 99.0% (813 of 821 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (French)

Currently translated at 100.0% (262 of 262 strings)

Translated using Weblate (French)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (French)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3265 of 3265 strings)

Translated using Weblate (French)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (French)

Currently translated at 100.0% (821 of 821 strings)

Translated using Weblate (French)

Currently translated at 100.0% (397 of 397 strings)

Translated using Weblate (French)

Currently translated at 100.0% (896 of 896 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3265 of 3265 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (French)

Currently translated at 100.0% (3265 of 3265 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3255 of 3255 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (15 of 15 strings)

Co-authored-by: Asier Gallego <agr2367789@gmail.com>
Co-authored-by: Asier Gallego Roca <asiernoide@users.noreply.translate.habitica.com>
Co-authored-by: Henrique Ferreira <pedroferreira217.ph@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: John Doe (Anonymous) <shyamjayeshduck@duck.com>
Co-authored-by: Katharina <katharinaanna.wilding@gmail.com>
Co-authored-by: Marie Blosse--Gilbin <mbgil@hotmail.fr>
Co-authored-by: Mauricio Pérez <mauriciodavidperez@gmail.com>
Co-authored-by: Raul Ernesto Ceron Lara <raztreuzz1234@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Willhelm Winter <carapax@posteo.de>
Co-authored-by: mattya 226 <worldworld1114@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/en_GB/
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/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/de/
Translate-URL: https://translate.habitica.com/projects/habitica/content/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/content/es/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/death/es/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/de/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/es/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/es/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/fr/
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/limited/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/de/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/es/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/es/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ja/
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/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translation: Habitica/Backgrounds
Translation: Habitica/Content
Translation: Habitica/Death
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Subscriber
2025-02-05 04:34:26 +01:00
Kalista Payne
ad845dff43 fix(test): nudge expected no-promo date 2025-01-29 17:17:28 -06:00
Kalista Payne
fd1eb2d900 feat(events): revise dates of promos 2025-01-29 16:59:14 -06:00
Kalista Payne
26cb6df9d9 5.33.0 2025-01-28 09:33:31 -06:00
klim
b0aafb079a Translated using Weblate (Russian)
Currently translated at 100.0% (167 of 167 strings)

Translation: Habitica/Achievements
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ru/
2025-01-28 16:31:17 +01:00
Weblate
58f0837c50 Translated using Weblate (Russian)
Currently translated at 100.0% (167 of 167 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 86.4% (210 of 243 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 87.6% (783 of 893 strings)

Translated using Weblate (French)

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (French)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Indonesian)

Currently translated at 95.9% (857 of 893 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 77.6% (2526 of 3255 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 86.9% (227 of 261 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.2% (133 of 134 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.4% (419 of 430 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (German)

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (German)

Currently translated at 100.0% (243 of 243 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Spanish (Latin America))

Currently translated at 42.8% (39 of 91 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 94.2% (229 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 93.9% (763 of 812 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 88.4% (215 of 243 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 79.8% (194 of 243 strings)

Translated using Weblate (German)

Currently translated at 99.2% (133 of 134 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3255 of 3255 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 79.4% (193 of 243 strings)

Translated using Weblate (German)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (German)

Currently translated at 100.0% (812 of 812 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (393 of 394 strings)

Translated using Weblate (German)

Currently translated at 98.8% (3217 of 3255 strings)

Translated using Weblate (German)

Currently translated at 98.8% (3217 of 3255 strings)

Translated using Weblate (Indonesian)

Currently translated at 74.4% (181 of 243 strings)

Translated using Weblate (Indonesian)

Currently translated at 80.2% (73 of 91 strings)

Translated using Weblate (German)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (German)

Currently translated at 98.6% (3211 of 3255 strings)

Translated using Weblate (French)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (French)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (French)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (German)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (German)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (German)

Currently translated at 100.0% (260 of 260 strings)

Translated using Weblate (German)

Currently translated at 98.2% (3198 of 3255 strings)

Translated using Weblate (German)

Currently translated at 98.1% (3195 of 3255 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3255 of 3255 strings)

Co-authored-by: @hb-8zzgfb5cn8qla6olm <sinconexion3@gmail.com>
Co-authored-by: Aditya Setyo Lutfiandhika <sladityaa176@gmail.com>
Co-authored-by: Felix Yan <felixonmars@archlinux.org>
Co-authored-by: Happy Knight <2953467684@qq.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Katharina <katharinaanna.wilding@gmail.com>
Co-authored-by: LPDJ <petits.julius@gmail.com>
Co-authored-by: Natalia <nati.love.bill@gmail.com>
Co-authored-by: Nebula <habitica-translate.utopia411@passinbox.com>
Co-authored-by: Nisa Hadisti <nisahadisti18@gmail.com>
Co-authored-by: Phillip Thelen <phillip@habitica.com>
Co-authored-by: Sarah <sarah.huang063@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: klim <petrrudichev@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/he/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/id/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/id/
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/id/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
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/generic/de/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/es/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/es/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/de/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/es/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/zh_Hans/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2025-01-28 16:31:07 +01:00
Kalista Payne
a6378b3d43 fix(wiki): use new API Guidelines link 2025-01-27 19:37:38 -06:00
Kalista Payne
ddbf95da92 Squashed commit of the following:
commit 78fe7721a373effbfe62f31583296ee02c050a92
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Wed Jan 15 15:16:56 2025 -0500

    fix: typo fix

commit 6a73cb247aff59102f91171f52c1d56f87bff562
Author: CuriousMagpie <eilatan@gmail.com>
Date:   Wed Jan 15 14:51:00 2025 -0500

    build: February 2025 content build
2025-01-23 15:33:16 -06:00
Kalista Payne
4d31e0286b chore(images): update subproj and CSS 2025-01-23 13:26:00 -06:00
Phillip Thelen
7a74825121 Remove references of certain social site for obvious reasons 2025-01-23 12:52:35 -06:00
Kalista Payne
be0e8779d5 fix(links): correct Guilds FAQ redirect and Contact Us 2025-01-23 12:50:03 -06:00
Kalista Payne
fffbe17bcc Squashed commit of the following:
commit 3746ccb2fdfb23276f49a9aee25e00ca366be14c
Author: Phillip Thelen <phillip@habitica.com>
Date:   Thu Jan 23 17:30:31 2025 +0100

    fix giving gear to contributors

commit 557cb582df47abb75331794e2af5c69da5548a90
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Jan 21 11:10:29 2025 +0100

    Give contributors gear immediately

commit 8d25bef6e1c6e48aa4d5a3b0cde49844d3164ed9
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Jan 21 11:10:16 2025 +0100

    adjust contributor tests

commit d918738533fe059db65d9020adb0126b43aaf0b3
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Jan 21 11:02:19 2025 +0100

    adjust gems per tier
2025-01-23 12:41:06 -06:00
Rafał Jagielski
ca4ee8b513 Fix intro guide (#15247)
* Provide window.jquery to modules in vue.config.js

* Fix curly-spacing lint error
2025-01-23 12:27:26 -06:00
671 changed files with 24141 additions and 17423 deletions

View File

@@ -1,12 +1,12 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": true
}
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": true
}
]
}
]
}
]
}

View File

@@ -7,5 +7,14 @@ module.exports = {
rules: {
'prefer-regex-literals': 'warn',
'import/no-extraneous-dependencies': 'off',
'require-await': 'error',
},
overrides: [
{
files: ['migrations/**', 'gulp/**'], // Or *.test.js
rules: {
'require-await': 'off',
},
},
],
};

View File

@@ -1,6 +1,13 @@
name: Test
on: [push, pull_request]
on:
push:
branches-ignore:
- 'phillip/**'
- 'sabrecat/**'
- 'kalista/**'
- 'natalie/**'
pull_request:
permissions:
contents: read

View File

@@ -2,4 +2,4 @@
This webpage includes the documentation for version 3 of the [Habitica](https://habitica.com) API.
If you're developing a 3rd party tool that uses the Habitica API you should read the [Guidance for Comrades](https://habitica.fandom.com/wiki/Guidance_for_Comrades) and in particular the section called [Rules for Third-Party Tools](https://habitica.fandom.com/wiki/Guidance_for_Comrades#Rules_for_Third-Party_Tools) which includes suggestions on how to best use the API and the rules to follow when interacting with it.
If you're developing a 3rd party tool that uses the Habitica API, read the [API Usage Guidelines](https://github.com/HabitRPG/habitica/wiki/API-Usage-Guidelines), which describe how to be a responsible user of our server resources!

View File

@@ -93,5 +93,6 @@
"TRUSTED_DOMAINS": "localhost,https://habitica.com",
"TIME_TRAVEL_ENABLED": "false",
"DEBUG_ENABLED": "false",
"CONTENT_SWITCHOVER_TIME_OFFSET": 8
"CONTENT_SWITCHOVER_TIME_OFFSET": 8,
"SLOW_REQUEST_THRESHOLD": 1000
}

View File

@@ -64,6 +64,15 @@ function filterFile (file) {
if (file.relative.indexOf('icon_background') === 0) {
return false;
}
if (file.relative.indexOf('notif_') === 0) {
return false;
}
if (file.relative.indexOf('quest_') === 0) {
return false;
}
if (file.relative.indexOf('inventory_quest_') === 0) {
return false;
}
return true;
}

765
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "5.32.5",
"version": "5.38.2",
"main": "./website/server/index.js",
"dependencies": {
"@babel/core": "^7.22.10",
@@ -50,11 +50,10 @@
"method-override": "^3.0.0",
"moment": "^2.29.4",
"moment-recur": "git://github.com/HabitRPG/moment-recur.git#d3e8e6da0806f13b74dd2e4d7d9053e6a63db119",
"mongoose": "^7.8.3",
"mongoose": "^8.9.5",
"morgan": "^1.10.0",
"nconf": "^0.12.1",
"node-gcm": "^1.0.5",
"nodemon": "^3.1.9",
"on-headers": "^1.0.2",
"passport": "^0.5.3",
"passport-facebook": "^3.0.0",
@@ -107,14 +106,14 @@
"start": "node --watch ./website/server/index.js",
"start:simple": "node ./website/server/index.js",
"debug": "node --watch --inspect ./website/server/index.js",
"mongo:dev": "run-rs -v 5.0.23 -l ubuntu1804 --keep --dbpath mongodb-data --number 1 --quiet",
"mongo:test": "run-rs -v 5.0.23 -l ubuntu1804 --keep --dbpath mongodb-data-testing --number 1 --quiet",
"mongo:dev": "run-rs -v 7.0.23 -l ubuntu2404 --keep --dbpath mongodb-data --number 1 --quiet",
"mongo:test": "run-rs -v 7.0.23 -l ubuntu2404 --keep --dbpath mongodb-data-testing --number 1 --quiet",
"postinstall": "git config --global url.\"https://\".insteadOf git:// && gulp build && cd website/client && npm install",
"apidoc": "gulp apidoc",
"heroku-postbuild": ".heroku/report_deploy.sh"
},
"devDependencies": {
"axios": "^1.7.4",
"axios": "^1.8.2",
"chai": "^4.3.7",
"chai-as-promised": "^7.1.1",
"chai-moment": "^0.1.0",

View File

@@ -71,15 +71,14 @@ async function deleteHabiticaData (user, email) {
}
async function processEmailAddress (email) {
const emailRegex = new RegExp(`^${email}$`, 'i');
const localUsers = await User.find(
{ 'auth.local.email': emailRegex },
{ 'auth.local.email': email },
{ _id: 1, apiToken: 1, auth: 1 },
).exec();
const socialUsers = await User.find(
{
'auth.local.email': { $not: emailRegex },
'auth.local.email': { $ne: email },
$or: [
{ 'auth.facebook.emails.value': email },
{ 'auth.google.emails.value': email },

View File

@@ -8,7 +8,17 @@ const TASK_VALUE_CHANGE_FACTOR = 0.9747;
const MIN_TASK_VALUE = -47.27;
async function updateTeamTasks (team) {
if (team.purchased.plan.dateTerminated) {
const dateTerminated = new Date(team.purchased.plan.dateTerminated);
if (dateTerminated < new Date()) {
team.purchased.plan.customerId = undefined;
team.markModified('purchased.plan');
return team.save();
}
}
const toSave = [];
let teamLeader = await User.findOne({ _id: team.leader }, 'preferences').exec();
if (!teamLeader) { // why would this happen?
@@ -93,12 +103,7 @@ async function updateTeamTasks (team) {
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();
}, { cron: 1, leader: 1, purchased: 1 }).exec();
const cronPromises = activeTeams.map(updateTeamTasks);
return Promise.all(cronPromises);

View File

@@ -2,13 +2,22 @@
import moment from 'moment';
import nconf from 'nconf';
import requireAgain from 'require-again';
import { recoverCron, cron } from '../../../../website/server/libs/cron';
import { v4 as generateUUID } from 'uuid';
import {
generateRes,
generateReq,
generateTodo,
generateDaily,
} from '../../../helpers/api-unit.helper';
import { cron, cronWrapper } from '../../../../website/server/libs/cron';
import { model as User } from '../../../../website/server/models/user';
import * as Tasks from '../../../../website/server/models/task';
import common from '../../../../website/common';
import * as analytics from '../../../../website/server/libs/analyticsService';
import { model as Group } from '../../../../website/server/models/group';
// const scoreTask = common.ops.scoreTask;
const CRON_TIMEOUT_WAIT = new Date(5 * 60 * 1000).getTime();
const CRON_TIMEOUT_UNIT = new Date(60 * 1000).getTime();
const pathToCronLib = '../../../../website/server/libs/cron';
@@ -1200,7 +1209,7 @@ describe('cron', async () => {
it('increments perfect day achievement if all (at least 1) due dailies were completed', async () => {
daysMissed = 1;
tasksByType.dailys[0].completed = true;
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
tasksByType.dailys[0].isDue = true;
await cron({
user, tasksByType, daysMissed, analytics,
@@ -1212,7 +1221,7 @@ describe('cron', async () => {
it('does not increment perfect day achievement if no due dailies', async () => {
daysMissed = 1;
tasksByType.dailys[0].completed = true;
tasksByType.dailys[0].startDate = moment(new Date()).add({ days: 1 });
tasksByType.dailys[0].isDue = false;
await cron({
user, tasksByType, daysMissed, analytics,
@@ -1224,7 +1233,7 @@ describe('cron', async () => {
it('gives perfect day buff if all (at least 1) due dailies were completed', async () => {
daysMissed = 1;
tasksByType.dailys[0].completed = true;
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
tasksByType.dailys[0].isDue = true;
const previousBuffs = user.stats.buffs.toObject();
@@ -1242,7 +1251,7 @@ describe('cron', async () => {
user.preferences.sleep = true;
daysMissed = 1;
tasksByType.dailys[0].completed = true;
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
tasksByType.dailys[0].isDue = true;
const previousBuffs = user.stats.buffs.toObject();
@@ -1259,7 +1268,7 @@ describe('cron', async () => {
it('clears buffs if user does not have a perfect day (no due dailys)', async () => {
daysMissed = 1;
tasksByType.dailys[0].completed = true;
tasksByType.dailys[0].startDate = moment(new Date()).add({ days: 1 });
tasksByType.dailys[0].isDue = false;
user.stats.buffs = {
str: 1,
@@ -1488,78 +1497,6 @@ describe('cron', async () => {
});
});
describe('notifications', async () => {
it('adds a user notification', async () => {
const mpBefore = user.stats.mp;
tasksByType.dailys[0].completed = true;
const statsComputedRes = common.statsComputed(user);
const stubbedStatsComputed = sinon.stub(common, 'statsComputed');
stubbedStatsComputed.returns(Object.assign(statsComputedRes, { maxMP: 100 }));
daysMissed = 1;
const hpBefore = user.stats.hp;
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.notifications.length).to.be.greaterThan(0);
expect(user.notifications[1].type).to.equal('CRON');
expect(user.notifications[1].data).to.eql({
hp: user.stats.hp - hpBefore,
mp: user.stats.mp - mpBefore,
});
common.statsComputed.restore();
});
it('condenses multiple notifications into one', async () => {
const mpBefore1 = user.stats.mp;
tasksByType.dailys[0].completed = true;
const statsComputedRes = common.statsComputed(user);
const stubbedStatsComputed = sinon.stub(common, 'statsComputed');
stubbedStatsComputed.returns(Object.assign(statsComputedRes, { maxMP: 100 }));
daysMissed = 1;
const hpBefore1 = user.stats.hp;
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.notifications.length).to.be.greaterThan(0);
expect(user.notifications[1].type).to.equal('CRON');
expect(user.notifications[1].data).to.eql({
hp: user.stats.hp - hpBefore1,
mp: user.stats.mp - mpBefore1,
});
const notifsBefore2 = user.notifications.length;
const hpBefore2 = user.stats.hp;
const mpBefore2 = user.stats.mp;
user.lastCron = moment(new Date()).subtract({ days: 2 });
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.notifications.length - notifsBefore2).to.equal(0);
expect(user.notifications[0].type).to.not.equal('CRON');
expect(user.notifications[1].type).to.equal('CRON');
expect(user.notifications[1].data).to.eql({
hp: user.stats.hp - hpBefore2 - (hpBefore2 - hpBefore1),
mp: user.stats.mp - mpBefore2 - (mpBefore2 - mpBefore1),
});
expect(user.notifications[0].type).to.not.equal('CRON');
common.statsComputed.restore();
});
});
describe('private messages', async () => {
let lastMessageId;
@@ -1606,7 +1543,7 @@ describe('cron', async () => {
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.notifications.length).to.be.greaterThan(1);
expect(user.notifications.length).to.eql(1);
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
});
@@ -1820,64 +1757,258 @@ describe('cron', async () => {
});
});
describe('recoverCron', async () => {
let locals; let status; let
execStub;
describe('cron wrapper', () => {
let res; let
req;
let user;
beforeEach(async () => {
execStub = sandbox.stub();
sandbox.stub(User, 'findOne').returns({ exec: execStub });
status = { times: 0 };
locals = {
user: new User({
auth: {
local: {
username: 'username',
lowerCaseUsername: 'username',
email: 'email@example.com',
salt: 'salt',
hashed_password: 'hashed_password', // eslint-disable-line camelcase
},
},
}),
};
res = generateRes();
req = generateReq();
user = await res.locals.user.save();
res.analytics = analytics;
});
afterEach(async () => {
afterEach(() => {
sandbox.restore();
});
it('throws an error if user cannot be found', async () => {
execStub.returns(Promise.resolve(null));
it('calls next when user is not attached', async () => {
res.locals.user = null;
await cronWrapper(req, res);
});
it('calls next when days have not been missed', async () => {
await cronWrapper(req, res);
});
it('should clear todos older than 30 days for free users', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
const task = generateTodo(user);
task.dateCompleted = moment(new Date()).subtract({ days: 31 });
task.completed = true;
await task.save();
await user.save();
await cronWrapper(req, res);
const taskRes = await Tasks.Task.findOne({ _id: task._id });
expect(taskRes).to.not.exist;
});
it('should not clear todos older than 30 days for subscribed users', async () => {
user.purchased.plan.customerId = 'subscribedId';
user.purchased.plan.dateUpdated = moment('012013', 'MMYYYY');
user.lastCron = moment(new Date()).subtract({ days: 2 });
const task = generateTodo(user);
task.dateCompleted = moment(new Date()).subtract({ days: 31 });
task.completed = true;
await Promise.all([task.save(), user.save()]);
await cronWrapper(req, res);
const taskRes = await Tasks.Task.findOne({ _id: task._id });
expect(taskRes).to.exist;
});
it('should clear todos older than 90 days for subscribed users', async () => {
user.purchased.plan.customerId = 'subscribedId';
user.purchased.plan.dateUpdated = moment('012013', 'MMYYYY');
user.lastCron = moment(new Date()).subtract({ days: 2 });
const task = generateTodo(user);
task.dateCompleted = moment(new Date()).subtract({ days: 91 });
task.completed = true;
await task.save();
await user.save();
await cronWrapper(req, res);
const taskRes = await Tasks.Task.findOne({ _id: task._id });
expect(taskRes).to.not.exist;
});
it('should call next if user was not modified after cron', async () => {
const hpBefore = user.stats.hp;
user.lastCron = moment(new Date()).subtract({ days: 2 });
await user.save();
await cronWrapper(req, res);
expect(hpBefore).to.equal(user.stats.hp);
});
it('runs cron if previous cron was incomplete', async () => {
user.lastCron = moment(new Date()).subtract({ days: 1 });
user.auth.timestamps.loggedin = moment(new Date()).subtract({ days: 4 });
const now = new Date();
await user.save();
await cronWrapper(req, res);
expect(moment(now).isSame(user.lastCron, 'day'));
expect(moment(now).isSame(user.auth.timestamps.loggedin, 'day'));
});
it('updates user.auth.timestamps.loggedin and lastCron', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
const now = new Date();
await user.save();
await cronWrapper(req, res);
expect(moment(now).isSame(user.lastCron, 'day'));
expect(moment(now).isSame(user.auth.timestamps.loggedin, 'day'));
});
it('does damage for missing dailies', async () => {
const hpBefore = user.stats.hp;
user.lastCron = moment(new Date()).subtract({ days: 2 });
const daily = generateDaily(user);
daily.startDate = moment(new Date()).subtract({ days: 2 });
await daily.save();
await user.save();
await cronWrapper(req, res);
const updatedUser = await User.findOne({ _id: user._id });
expect(updatedUser.stats.hp).to.be.lessThan(hpBefore);
});
it('updates tasks', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
const todo = generateTodo(user);
const todoValueBefore = todo.value;
await Promise.all([todo.save(), user.save()]);
await cronWrapper(req, res);
const todoFound = await Tasks.Task.findOne({ _id: todo._id });
expect(todoFound.value).to.be.lessThan(todoValueBefore);
});
it('updates large number of tasks', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
const todo = generateTodo(user);
const todoValueBefore = todo.value;
const start = new Date();
const saves = [todo.save(), user.save()];
for (let i = 0; i < 200; i += 1) {
const newTodo = generateTodo(user);
newTodo.value = i;
saves.push(newTodo.save());
}
await Promise.all(saves);
await cronWrapper(req, res);
const duration = new Date() - start;
expect(duration).to.be.lessThan(1000);
const todoFound = await Tasks.Task.findOne({ _id: todo._id });
expect(moment(start).isSame(user.lastCron, 'day'));
expect(moment(start).isSame(user.auth.timestamps.loggedin, 'day'));
expect(todoFound.value).to.be.lessThan(todoValueBefore);
});
it('fails entire cron if one task is failing', async () => {
const lastCron = moment(new Date()).subtract({ days: 2 });
user.lastCron = lastCron;
const todo = generateTodo(user);
const todoValueBefore = todo.value;
const badTodo = generateTodo(user);
badTodo.text = 'bad todo';
badTodo.attribute = 'bad';
await Promise.all([badTodo.save({ validateBeforeSave: false }), todo.save(), user.save()]);
try {
await recoverCron(status, locals);
throw new Error('no exception when user cannot be found');
await cronWrapper(req, res);
} catch (err) {
expect(err.message).to.eql(`User ${locals.user._id} not found while recovering.`);
expect(err).to.exist;
}
const todoFound = await Tasks.Task.findOne({ _id: todo._id });
expect(moment(lastCron).isSame(user.lastCron, 'day'));
expect(todoFound.value).to.be.equal(todoValueBefore);
});
it('applies quest progress', async () => {
const hpBefore = user.stats.hp;
user.lastCron = moment(new Date()).subtract({ days: 2 });
const daily = generateDaily(user);
daily.startDate = moment(new Date()).subtract({ days: 2 });
await daily.save();
const questKey = 'dilatory';
user.party.quest.key = questKey;
const party = new Group({
type: 'party',
name: generateUUID(),
leader: user._id,
});
party.quest.members[user._id] = true;
party.quest.key = questKey;
await party.save();
user.party._id = party._id;
await user.save();
party.startQuest(user);
await cronWrapper(req, res);
const updatedUser = await User.findOne({ _id: user._id });
expect(updatedUser.stats.hp).to.be.lessThan(hpBefore);
});
it('cronSignature less than 5 minutes ago should error', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
const now = new Date();
await User.updateOne({
_id: user._id,
}, {
$set: {
_cronSignature: now.getTime() - CRON_TIMEOUT_WAIT + CRON_TIMEOUT_UNIT,
},
}).exec();
await user.save();
try {
await cronWrapper(req, res);
} catch (err) {
expect(err).to.exist;
}
});
it('increases status.times count and reruns up to 4 times', async () => {
execStub.returns(Promise.resolve({ _cronSignature: 'RUNNING_CRON' }));
execStub.onCall(4).returns(Promise.resolve({ _cronSignature: 'NOT_RUNNING' }));
it('cronSignature longer than an hour ago should allow cron', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
const now = new Date();
await User.updateOne({
_id: user._id,
}, {
$set: {
_cronSignature: now.getTime() - CRON_TIMEOUT_WAIT - CRON_TIMEOUT_UNIT,
},
}).exec();
await user.save();
await recoverCron(status, locals);
expect(status.times).to.eql(4);
expect(locals.user).to.eql({ _cronSignature: 'NOT_RUNNING' });
await cronWrapper(req, res);
expect(moment(now).isSame(user.auth.timestamps.loggedin, 'day'));
expect(user._cronSignature).to.be.equal('NOT_RUNNING');
});
it('throws an error if recoverCron runs 5 times', async () => {
execStub.returns(Promise.resolve({ _cronSignature: 'RUNNING_CRON' }));
it('cron should not run more than once', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
await user.save();
try {
await recoverCron(status, locals);
throw new Error('no exception when recoverCron runs 5 times');
} catch (err) {
expect(status.times).to.eql(5);
expect(err.message).to.eql(`Impossible to recover from cron for user ${locals.user._id}.`);
}
const result = await Promise.allSettled([
cronWrapper(req, res),
cronWrapper(req, res),
new Promise((resolve, reject) => {
setTimeout(async () => {
try {
const runResult = await cronWrapper(req, res);
if (runResult !== null) {
reject(new Error('cron ran more than once'));
} else {
resolve();
}
} catch (err) {
reject(err);
}
}, 200);
}),
]);
expect(result.filter(r => r.status === 'fulfilled')).to.have.lengthOf(2);
expect(result.filter(r => r.status === 'rejected')).to.have.lengthOf(1);
});
});

View File

@@ -171,23 +171,23 @@ describe('emails', () => {
expect(got.post).not.to.be.called;
});
it('throws error when mail target is only a string', () => {
it('throws error when mail target is only a string', async () => {
const emailType = 'an email type';
const mailingInfo = 'my email';
expect(sendTxn(mailingInfo, emailType)).to.throw;
await expect(sendTxn(mailingInfo, emailType)).to.be.rejectedWith('Argument Error mailingInfoArray: does not contain email or _id');
});
it('throws error when mail target has no _id or email', () => {
it('throws error when mail target has no _id or email', async () => {
const emailType = 'an email type';
const mailingInfo = {
};
expect(sendTxn(mailingInfo, emailType)).to.throw;
await expect(sendTxn(mailingInfo, emailType)).to.be.rejectedWith('Argument Error mailingInfoArray: does not contain email or _id');
});
it('throws error when variables not an array', () => {
it('throws error when variables not an array', async () => {
const emailType = 'an email type';
const mailingInfo = {
name: 'my name',
@@ -195,9 +195,10 @@ describe('emails', () => {
};
const variables = {};
expect(sendTxn(mailingInfo, emailType, variables)).to.throw;
await expect(sendTxn(mailingInfo, emailType, variables)).to.be.rejectedWith('Argument Error variables: is not an array');
});
it('throws error when variables array not contain name/content', () => {
it('throws error when variables array not contain name/content', async () => {
const emailType = 'an email type';
const mailingInfo = {
name: 'my name',
@@ -209,8 +210,9 @@ describe('emails', () => {
},
];
expect(sendTxn(mailingInfo, emailType, variables)).to.throw;
await expect(sendTxn(mailingInfo, emailType, variables)).to.be.rejectedWith('Argument Error variables: does not contain name or content');
});
it('throws no error when variables array contain name but no content', () => {
const emailType = 'an email type';
const mailingInfo = {

View File

@@ -1,5 +1,4 @@
import os from 'os';
import nconf from 'nconf';
import requireAgain from 'require-again';
const pathToMongoLib = '../../../../website/server/libs/mongodb';
@@ -29,22 +28,4 @@ describe('mongodb', () => {
expect(string).to.equal('mongodb://hostname:3030');
});
});
describe('getDefaultConnectionOptions', () => {
it('returns development config when IS_PROD is false', () => {
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(false);
const mongoLibOverride = requireAgain(pathToMongoLib);
const options = mongoLibOverride.getDefaultConnectionOptions();
expect(options).to.have.all.keys(['useNewUrlParser', 'useUnifiedTopology']);
});
it('returns production config when IS_PROD is true', () => {
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(true);
const mongoLibOverride = requireAgain(pathToMongoLib);
const options = mongoLibOverride.getDefaultConnectionOptions();
expect(options).to.have.all.keys(['useNewUrlParser', 'useUnifiedTopology']);
});
});
});

View File

@@ -1,8 +1,11 @@
import nconf from 'nconf';
import requireAgain from 'require-again';
import {
generateRes,
generateReq,
} from '../../../helpers/api-unit.helper';
import { authWithHeaders as authWithHeadersFactory } from '../../../../website/server/middlewares/auth';
const authPath = '../../../../website/server/middlewares/auth';
describe('auth middleware', () => {
let res; let req; let
@@ -16,6 +19,7 @@ describe('auth middleware', () => {
describe('auth with headers', () => {
it('allows to specify a list of user field that we do not want to load', done => {
const authWithHeadersFactory = requireAgain(authPath).authWithHeaders;
const authWithHeaders = authWithHeadersFactory({
userFieldsToExclude: ['items'],
});
@@ -35,6 +39,7 @@ describe('auth middleware', () => {
});
it('makes sure some fields are always included', done => {
const authWithHeadersFactory = requireAgain(authPath).authWithHeaders;
const authWithHeaders = authWithHeadersFactory({
userFieldsToExclude: [
'items', 'auth.timestamps',
@@ -60,5 +65,57 @@ describe('auth middleware', () => {
return done();
});
});
it('errors with InvalidCredentialsError and code when token is wrong', done => {
const authWithHeadersFactory = requireAgain(authPath).authWithHeaders;
const authWithHeaders = authWithHeadersFactory({ userFieldsToExclude: [] });
req.headers['x-api-user'] = user._id;
req.headers['x-api-key'] = 'totally-wrong-token';
authWithHeaders(req, res, err => {
expect(err).to.exist;
expect(err.name).to.equal('InvalidCredentialsError');
expect(err.code).to.equal('invalid_credentials');
expect(err.message).to.equal(res.t('invalidCredentials'));
return done();
});
});
describe('when ENFORCE_CLIENT_HEADER is true', () => {
let authFactory;
beforeEach(() => {
sandbox.stub(nconf, 'get').withArgs('ENFORCE_CLIENT_HEADER').returns('true');
authFactory = requireAgain(authPath).authWithHeaders;
});
it('errors with missingClientHeader when x-client header is not present', done => {
const authWithHeaders = authFactory({ userFieldsToExclude: [] });
req.headers['x-api-user'] = user._id;
req.headers['x-api-key'] = user;
authWithHeaders(req, res, err => {
expect(err).to.exist;
expect(err.name).to.equal('BadRequest');
expect(err.message).to.equal(res.t('missingClientHeader'));
return done();
});
});
it('allows request to pass when x-client header is present', done => {
const authWithHeaders = authFactory({ userFieldsToExclude: [] });
req.headers['x-api-user'] = user._id;
req.headers['x-api-key'] = user.apiToken;
req.headers['x-client'] = 'habitica-web';
authWithHeaders(req, res, err => {
if (err) return done(err);
expect(res.locals.user).to.exist;
return done();
});
});
});
});
});

View File

@@ -0,0 +1,206 @@
import nconf from 'nconf';
import requireAgain from 'require-again';
import {
generateRes,
generateReq,
generateNext,
} from '../../../helpers/api-unit.helper';
import { Forbidden } from '../../../../website/server/libs/errors';
import { apiError } from '../../../../website/server/libs/apiError';
import { model as Blocker } from '../../../../website/server/models/blocker';
function checkIPBlockedErrorThrown (next) {
expect(next).to.have.been.calledOnce;
const calledWith = next.getCall(0).args;
expect(calledWith[0].message).to.equal(apiError('ipAddressBlocked'));
expect(calledWith[0] instanceof Forbidden).to.equal(true);
}
function checkClientBlockedErrorThrown (next) {
expect(next).to.have.been.calledOnce;
const calledWith = next.getCall(0).args;
expect(calledWith[0].message).to.equal(apiError('clientBlocked'));
expect(calledWith[0] instanceof Forbidden).to.equal(true);
}
function checkErrorNotThrown (next) {
expect(next).to.have.been.calledOnce;
const calledWith = next.getCall(0).args;
expect(typeof calledWith[0] === 'undefined').to.equal(true);
}
describe('Blocker middleware', () => {
const pathToBlocker = '../../../../website/server/middlewares/blocker';
let res; let req; let next;
beforeEach(() => {
res = generateRes();
req = generateReq();
next = generateNext();
});
describe('Blocking IPs', () => {
it('is disabled when the env var is not defined', () => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns(undefined);
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('is disabled when the env var is an empty string', () => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('');
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('is disabled when the env var contains comma separated empty strings', () => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns(' , , ');
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('does not throw when the ip does not match', () => {
req.ip = '192.168.1.1';
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('192.168.1.2');
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('does not throw when the blocker IP does not match', async () => {
req.ip = '192.168.1.1';
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'ipaddress', area: 'full', value: '192.168.1.2' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('does not throw when a client is blocked', async () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'client', area: 'full', value: '192.168.1.1' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('throws when the blocker IP is blocked', async () => {
req.ip = '192.168.1.1';
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'ipaddress', area: 'full', value: '192.168.1.1' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkIPBlockedErrorThrown(next);
});
it('throws when the ip is blocked', () => {
req.ip = '192.168.1.1';
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('192.168.1.1');
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkIPBlockedErrorThrown(next);
});
});
describe('Blocking clients', () => {
beforeEach(() => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('');
req.headers['x-client'] = 'test-client';
});
it('is disabled when no clients are blocked', () => {
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('does not throw when the client does not match', async () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'client', area: 'full', value: 'another-client' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('throws when the client is blocked', async () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'client', area: 'full', value: 'test-client' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkClientBlockedErrorThrown(next);
});
it('does not throw when an ip is blocked', async () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'ipaddress', area: 'full', value: 'test-client' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('updates the list when data changes', async () => {
let blockCallback;
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
blockCallback = callback;
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'client', area: 'full', value: 'another-client' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
blockCallback({ operation: 'add', blocker: { type: 'client', area: 'full', value: 'test-client' } });
attachBlocker(req, res, next);
expect(next).to.have.been.calledTwice;
const calledWith = next.getCall(1).args;
expect(calledWith[0].message).to.equal(apiError('clientBlocked'));
expect(calledWith[0] instanceof Forbidden).to.equal(true);
});
});
});

View File

@@ -1,332 +0,0 @@
import moment from 'moment';
import { v4 as generateUUID } from 'uuid';
import {
generateRes,
generateReq,
generateTodo,
generateDaily,
} from '../../../helpers/api-unit.helper';
import cronMiddleware from '../../../../website/server/middlewares/cron';
import { model as User } from '../../../../website/server/models/user';
import { model as Group } from '../../../../website/server/models/group';
import * as Tasks from '../../../../website/server/models/task';
import * as analyticsService from '../../../../website/server/libs/analyticsService';
import * as cronLib from '../../../../website/server/libs/cron';
const CRON_TIMEOUT_WAIT = new Date(60 * 60 * 1000).getTime();
const CRON_TIMEOUT_UNIT = new Date(60 * 1000).getTime();
describe('cron middleware', () => {
let res; let
req;
let user;
beforeEach(async () => {
res = generateRes();
req = generateReq();
user = await res.locals.user.save();
res.analytics = analyticsService;
});
afterEach(() => {
sandbox.restore();
});
it('calls next when user is not attached', done => {
res.locals.user = null;
cronMiddleware(req, res, done);
});
it('calls next when days have not been missed', done => {
cronMiddleware(req, res, done);
});
it('should clear todos older than 30 days for free users', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
const task = generateTodo(user);
task.dateCompleted = moment(new Date()).subtract({ days: 31 });
task.completed = true;
await task.save();
await user.save();
await new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
Tasks.Task.findOne({ _id: task }).then(foundTask => {
expect(foundTask).to.not.exist;
resolve();
});
return null;
});
});
});
it('should not clear todos older than 30 days for subscribed users', async () => {
user.purchased.plan.customerId = 'subscribedId';
user.purchased.plan.dateUpdated = moment('012013', 'MMYYYY');
user.lastCron = moment(new Date()).subtract({ days: 2 });
const task = generateTodo(user);
task.dateCompleted = moment(new Date()).subtract({ days: 31 });
task.completed = true;
await task.save();
await user.save();
await new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
Tasks.Task.findOne({ _id: task }).then(foundTask => {
expect(foundTask).to.exist;
return resolve();
});
return null;
});
});
});
it('should clear todos older than 90 days for subscribed users', async () => {
user.purchased.plan.customerId = 'subscribedId';
user.purchased.plan.dateUpdated = moment('012013', 'MMYYYY');
user.lastCron = moment(new Date()).subtract({ days: 2 });
const task = generateTodo(user);
task.dateCompleted = moment(new Date()).subtract({ days: 91 });
task.completed = true;
await task.save();
await user.save();
await new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
Tasks.Task.findOne({ _id: task }).then(foundTask => {
expect(foundTask).to.not.exist;
return resolve();
});
return null;
});
});
});
it('should call next if user was not modified after cron', async () => {
const hpBefore = user.stats.hp;
user.lastCron = moment(new Date()).subtract({ days: 2 });
await user.save();
await new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
expect(hpBefore).to.equal(user.stats.hp);
return resolve();
});
});
});
it('runs cron if previous cron was incomplete', async () => {
user.lastCron = moment(new Date()).subtract({ days: 1 });
user.auth.timestamps.loggedin = moment(new Date()).subtract({ days: 4 });
const now = new Date();
await user.save();
await new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
expect(moment(now).isSame(user.lastCron, 'day'));
expect(moment(now).isSame(user.auth.timestamps.loggedin, 'day'));
return resolve();
});
});
});
it('updates user.auth.timestamps.loggedin and lastCron', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
const now = new Date();
await user.save();
await new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
expect(moment(now).isSame(user.lastCron, 'day'));
expect(moment(now).isSame(user.auth.timestamps.loggedin, 'day'));
return resolve();
});
});
});
it('does damage for missing dailies', async () => {
const hpBefore = user.stats.hp;
user.lastCron = moment(new Date()).subtract({ days: 2 });
const daily = generateDaily(user);
daily.startDate = moment(new Date()).subtract({ days: 2 });
await daily.save();
await user.save();
await new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
return User.findOne({ _id: user._id }).then(updatedUser => {
expect(updatedUser.stats.hp).to.be.lessThan(hpBefore);
return resolve();
});
});
});
});
it('updates tasks', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
const todo = generateTodo(user);
const todoValueBefore = todo.value;
await Promise.all([todo.save(), user.save()]);
await new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
return Tasks.Task.findOne({ _id: todo._id }).then(todoFound => {
expect(todoFound.value).to.be.lessThan(todoValueBefore);
return resolve();
});
});
});
});
it('applies quest progress', async () => {
const hpBefore = user.stats.hp;
user.lastCron = moment(new Date()).subtract({ days: 2 });
const daily = generateDaily(user);
daily.startDate = moment(new Date()).subtract({ days: 2 });
await daily.save();
const questKey = 'dilatory';
user.party.quest.key = questKey;
const party = new Group({
type: 'party',
name: generateUUID(),
leader: user._id,
});
party.quest.members[user._id] = true;
party.quest.key = questKey;
await party.save();
user.party._id = party._id;
await user.save();
party.startQuest(user);
await new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
return User.findOne({ _id: user._id }).then(updatedUser => {
expect(updatedUser.stats.hp).to.be.lessThan(hpBefore);
return resolve();
});
});
});
});
it('recovers from failed cron and does not error when user is already cronning', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
await user.save();
const updatedUser = user.toObject();
updatedUser.matchedCount = 0;
sandbox.spy(cronLib, 'recoverCron');
sandbox.stub(User, 'updateOne')
.withArgs({
_id: user._id,
$or: [
{ _cronSignature: 'NOT_RUNNING' },
{ _cronSignature: { $lt: sinon.match.number } },
],
})
.returns({
exec () {
return Promise.resolve(updatedUser);
},
});
await new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
expect(cronLib.recoverCron).to.be.calledOnce;
return resolve();
});
});
});
it('cronSignature less than an hour ago should error', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
const now = new Date();
await User.updateOne({
_id: user._id,
}, {
$set: {
_cronSignature: now.getTime() - CRON_TIMEOUT_WAIT + CRON_TIMEOUT_UNIT,
},
}).exec();
await user.save();
const expectedErrMessage = `Impossible to recover from cron for user ${user._id}.`;
await new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (!err) return reject(new Error('Cron should have failed.'));
expect(err.message).to.be.equal(expectedErrMessage);
return resolve();
});
});
});
it('cronSignature longer than an hour ago should allow cron', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
const now = new Date();
await User.updateOne({
_id: user._id,
}, {
$set: {
_cronSignature: now.getTime() - CRON_TIMEOUT_WAIT - CRON_TIMEOUT_UNIT,
},
}).exec();
await user.save();
await new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
expect(moment(now).isSame(user.auth.timestamps.loggedin, 'day'));
expect(user._cronSignature).to.be.equal('NOT_RUNNING');
return resolve();
});
});
});
it('cron should not run more than once', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
await user.save();
sandbox.spy(cronLib, 'cron');
await Promise.all([new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
return resolve();
});
}), new Promise((resolve, reject) => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
return resolve();
});
}), new Promise((resolve, reject) => {
setTimeout(() => {
cronMiddleware(req, res, err => {
if (err) return reject(err);
return resolve();
});
}, 400);
}),
]);
expect(cronLib.cron).to.be.calledOnce;
});
});

View File

@@ -1,76 +0,0 @@
import nconf from 'nconf';
import requireAgain from 'require-again';
import {
generateRes,
generateReq,
generateNext,
} from '../../../helpers/api-unit.helper';
import { Forbidden } from '../../../../website/server/libs/errors';
import { apiError } from '../../../../website/server/libs/apiError';
function checkErrorThrown (next) {
expect(next).to.have.been.calledOnce;
const calledWith = next.getCall(0).args;
expect(calledWith[0].message).to.equal(apiError('ipAddressBlocked'));
expect(calledWith[0] instanceof Forbidden).to.equal(true);
}
function checkErrorNotThrown (next) {
expect(next).to.have.been.calledOnce;
const calledWith = next.getCall(0).args;
expect(typeof calledWith[0] === 'undefined').to.equal(true);
}
describe('ipBlocker middleware', () => {
const pathToIpBlocker = '../../../../website/server/middlewares/ipBlocker';
let res; let req; let next;
beforeEach(() => {
res = generateRes();
req = generateReq();
next = generateNext();
});
it('is disabled when the env var is not defined', () => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns(undefined);
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
attachIpBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('is disabled when the env var is an empty string', () => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('');
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
attachIpBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('is disabled when the env var contains comma separated empty strings', () => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns(' , , ');
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
attachIpBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('does not throw when the ip does not match', () => {
req.ip = '192.168.1.1';
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('192.168.1.2');
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
attachIpBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('throws when the ip is blocked', () => {
req.ip = '192.168.1.1';
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('192.168.1.1');
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
attachIpBlocker(req, res, next);
checkErrorThrown(next);
});
});

View File

@@ -1,9 +1,13 @@
import moment from 'moment';
import requireAgain from 'require-again';
import { model as User } from '../../../../website/server/models/user';
import { model as NewsPost } from '../../../../website/server/models/newsPost';
import { model as Group } from '../../../../website/server/models/group';
import { model as Blocker } from '../../../../website/server/models/blocker';
import common from '../../../../website/common';
const pathToUserSchema = '../../../../website/server/models/user/schema';
describe('User Model', () => {
describe('.toJSON()', () => {
it('keeps user._tmp when calling .toJSON', () => {
@@ -912,4 +916,73 @@ describe('User Model', () => {
expect(user.toJSON().flags.newStuff).to.equal(true);
});
});
describe('validates email', () => {
it('does not throw an error for a valid email', () => {
const user = new User();
user.auth.local.email = 'hello@example.com';
const errors = user.validateSync();
expect(errors.errors['auth.local.email']).to.not.exist;
});
it('throws an error if email is not valid', () => {
const user = new User();
user.auth.local.email = 'invalid-email';
const errors = user.validateSync();
expect(errors.errors['auth.local.email'].message).to.equal(common.i18n.t('invalidEmail'));
});
it('throws an error if email is using a restricted domain', () => {
const user = new User();
user.auth.local.email = 'scammer@habitica.com';
const errors = user.validateSync();
expect(errors.errors['auth.local.email'].message).to.equal(common.i18n.t('invalidEmailDomain', { domains: 'habitica.com, habitrpg.com' }));
});
it('throws an error if email was blocked specifically', () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: 'blocked@example.com' } });
},
});
const schema = requireAgain(pathToUserSchema).UserSchema;
const valid = schema.paths['auth.local.email'].options.validate.every(v => v.validator('blocked@example.com'));
expect(valid).to.equal(false);
});
it('throws an error if email domain was blocked', () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: '@example.com' } });
},
});
const schema = requireAgain(pathToUserSchema).UserSchema;
const valid = schema.paths['auth.local.email'].options.validate.every(v => v.validator('blocked@example.com'));
expect(valid).to.equal(false);
});
it('throws an error if user portion of email was blocked', () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: 'blocked@' } });
},
});
const schema = requireAgain(pathToUserSchema).UserSchema;
const valid = schema.paths['auth.local.email'].options.validate.every(v => v.validator('blocked@example.com'));
expect(valid).to.equal(false);
});
it('does not throw an error if email is not blocked', () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: '@example.com' } });
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: 'blocked@' } });
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: 'bad@test.com' } });
},
});
const schema = requireAgain(pathToUserSchema).UserSchema;
const valid = schema.paths['auth.local.email'].options.validate.every(v => v.validator('good@test.com'));
expect(valid).to.equal(true);
});
});
});

View File

@@ -10,6 +10,7 @@ describe('GET /heroes/:heroId', () => {
const heroFields = [
'_id', 'id', 'auth', 'balance', 'contributor', 'flags', 'items',
'lastCron', 'party', 'preferences', 'profile', 'purchased', 'secret', 'achievements',
'stats',
];
before(async () => {

View File

@@ -11,6 +11,7 @@ describe('PUT /heroes/:heroId', () => {
const heroFields = [
'_id', 'auth', 'balance', 'contributor', 'flags', 'items', 'lastCron',
'party', 'preferences', 'profile', 'purchased', 'secret', 'permissions', 'achievements',
'stats',
];
before(async () => {
@@ -60,12 +61,12 @@ describe('PUT /heroes/:heroId', () => {
expect(heroRes.profile).to.have.all.keys(['name']);
// test response values
expect(heroRes.balance).to.equal(3 + 0.75); // 3+0.75 for first contrib level
expect(heroRes.balance).to.equal(3 + 2.5); // 3+2.5 for first contrib level
expect(heroRes.contributor.level).to.equal(1);
expect(heroRes.purchased.ads).to.equal(true);
// test hero values
await hero.sync();
expect(hero.balance).to.equal(3 + 0.75); // 3+0.75 for first contrib level
expect(hero.balance).to.equal(3 + 2.5); // 3+2.5 for first contrib level
expect(hero.contributor.level).to.equal(1);
expect(hero.purchased.ads).to.equal(true);
expect(hero.auth.blocked).to.equal(prevBlockState);
@@ -136,12 +137,12 @@ describe('PUT /heroes/:heroId', () => {
expect(heroRes.profile).to.have.all.keys(['name']);
// test response values
expect(heroRes.balance).to.equal(1); // 0+1 for sixth contrib level
expect(heroRes.balance).to.equal(15); // 0+15 for sixth contrib level
expect(heroRes.contributor.level).to.equal(6);
expect(heroRes.items.pets['Dragon-Hydra']).to.equal(5);
// test hero values
await hero.sync();
expect(hero.balance).to.equal(1); // 0+1 for sixth contrib level
expect(hero.balance).to.equal(15); // 0+15 for sixth contrib level
expect(hero.contributor.level).to.equal(6);
expect(hero.items.pets['Dragon-Hydra']).to.equal(5);
});

View File

@@ -19,7 +19,7 @@ describe('GET /members/username/:username', () => {
});
});
it('returns a member public data only', async () => {
it('returns a member\'s public data only', async () => {
// make sure user has all the fields that can be returned by the getMember call
const member = await generateUser({
contributor: { level: 1 },

View File

@@ -101,34 +101,6 @@ describe('GET /tasks/user', () => {
expect(allCompletedTodos[allCompletedTodos.length - 1].text).to.equal('todo to complete 2');
});
it('returns only some completed todos if req.query.type is "completedTodos" or "_allCompletedTodos"', async () => {
const LIMIT = 30;
const numberOfTodos = LIMIT + 1;
const todosInput = [];
for (let i = 0; i < numberOfTodos; i += 1) {
todosInput[i] = { text: `todo to complete ${i}`, type: 'todo' };
}
const todos = await user.post('/tasks/user', todosInput);
await user.sync();
const initialTodoCount = user.tasksOrder.todos.length;
for (let i = 0; i < numberOfTodos; i += 1) {
const id = todos[i]._id;
await user.post(`/tasks/${id}/score/up`); // eslint-disable-line no-await-in-loop
}
await user.sync();
expect(user.tasksOrder.todos.length).to.equal(initialTodoCount - numberOfTodos);
const completedTodos = await user.get('/tasks/user?type=completedTodos');
expect(completedTodos.length).to.equal(LIMIT);
const allCompletedTodos = await user.get('/tasks/user?type=_allCompletedTodos');
expect(allCompletedTodos.length).to.equal(numberOfTodos);
});
it('returns dailies with isDue for the date specified', async () => {
// @TODO Add required format
const startDate = moment().subtract('1', 'days').toISOString();

View File

@@ -238,6 +238,28 @@ describe('POST /user/auth/reset-password-set-new-one', () => {
expect(isPassValid).to.equal(true);
});
it('changes the apiToken on password reset', async () => {
const user = await generateUser();
const previousToken = user.apiToken;
const code = encrypt(JSON.stringify({
userId: user._id,
expiresAt: moment().add({ days: 1 }),
}));
await user.updateOne({
'auth.local.passwordResetCode': code,
});
await api.post(`${endpoint}`, {
newPassword: 'my new password',
confirmPassword: 'my new password',
code,
});
await user.sync();
expect(user.apiToken).to.not.eql(previousToken);
});
it('renders the success page and convert the password from sha1 to bcrypt', async () => {
const user = await generateUser();

View File

@@ -27,11 +27,30 @@ describe('PUT /user/auth/update-password', async () => {
newPassword,
confirmPassword: newPassword,
});
expect(response).to.eql({});
expect(response).to.exist;
expect(response.apiToken).to.exist;
await user.sync();
expect(user.auth.local.hashed_password).to.not.eql(previousHashedPassword);
});
it('should change the apiToken on password change', async () => {
const previousToken = user.apiToken;
const response = await user.put(ENDPOINT, {
password,
newPassword,
confirmPassword: newPassword,
});
const newToken = response.apiToken;
expect(newToken).to.exist;
await user.sync();
expect(user.apiToken).to.eql(newToken);
expect(user.apiToken).to.not.eql(previousToken);
});
it('returns an error when confirmPassword does not match newPassword', async () => {
await expect(user.put(ENDPOINT, {
password,

View File

@@ -26,7 +26,7 @@ describe('POST /inbox/like-private-message/:messageId', () => {
userToSendMessage = await generateUser();
});
it('Returns an error when private message is not found', async () => {
it('returns an error when private message is not found', async () => {
await expect(userToSendMessage.post(getLikeUrl('some-unknown-id')))
.to.eventually.be.rejected.and.eql({
code: 404,
@@ -35,7 +35,7 @@ describe('POST /inbox/like-private-message/:messageId', () => {
});
});
it('Likes a message', async () => {
it('likes a message', async () => {
const receiver = await generateUser();
const sentMessageResult = await userToSendMessage.post('/members/send-private-message', {
@@ -57,7 +57,7 @@ describe('POST /inbox/like-private-message/:messageId', () => {
expectMessagesLikeStatus(receiversMessages, uniqueMessageId, receiver._id, true);
});
it('Allows to likes their own private message', async () => {
it('allows a user to like their own private message', async () => {
const receiver = await generateUser();
const sentMessageResult = await userToSendMessage.post('/members/send-private-message', {
@@ -78,7 +78,7 @@ describe('POST /inbox/like-private-message/:messageId', () => {
expectMessagesLikeStatus(receiversMessages, uniqueMessageId, userToSendMessage._id, true);
});
it('Unlikes a message', async () => {
it('unlikes a message', async () => {
const receiver = await generateUser();
const sentMessageResult = await userToSendMessage.post('/members/send-private-message', {

View File

@@ -10,7 +10,7 @@ describe('events', () => {
});
it('returns empty array when no events are active', () => {
clock = sinon.useFakeTimers(new Date('2024-01-08'));
clock = sinon.useFakeTimers(new Date('2024-01-11'));
const events = getRepeatingEvents();
expect(events).to.be.empty;
});

View File

@@ -133,21 +133,21 @@ describe('Content Schedule', () => {
});
it('sets the end date for a gala', () => {
const date = new Date('2024-05-20');
const date = new Date('2024-05-31');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2024-06-21T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2024-06-01T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
it('sets the end date for a winter gala', () => {
const date = new Date('2024-12-22');
const date = new Date('2025-02-28');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2025-03-21T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2025-03-01T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
it('sets the end date in new year for a winter gala', () => {
const date = new Date('2025-01-04');
const date = new Date('2025-02-28');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2025-03-21T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2025-03-01T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
it('uses correct date for first hours of the month', () => {
@@ -190,7 +190,7 @@ describe('Content Schedule', () => {
const date = new Date('2024-04-15');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.premiumHatchingPotions).to.exist;
expect(matchers.premiumHatchingPotions.items.length).to.equal(5);
expect(matchers.premiumHatchingPotions.items.length).to.equal(6);
expect(matchers.premiumHatchingPotions.items.indexOf('Veggie')).to.not.equal(-1);
expect(matchers.premiumHatchingPotions.items.indexOf('Porcelain')).to.not.equal(-1);
});

View File

@@ -18,7 +18,7 @@ describe('Shop Featured Items', () => {
});
it('contains the current premium hatching potions', () => {
clock = Sinon.useFakeTimers(new Date('2024-04-08'));
clock = Sinon.useFakeTimers(new Date('2024-04-09'));
const items = featuredItems.market();
expect(_.find(items, item => item.path === 'premiumHatchingPotions.Porcelain')).to.exist;
});

View File

@@ -74,15 +74,10 @@ export async function getDocument (collectionName, doc) {
}
before(done => {
mongoose.connection.on('open', err => {
if (err) return done(err);
return resetHabiticaDB()
.then(() => {
done();
})
.catch(error => {
throw error;
});
mongoose.connection.once('open', async err => {
if (err) throw err;
await resetHabiticaDB();
done();
});
});

View File

@@ -1,36 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Habitica - Gamify Your Life</title>
<meta name="description" content="Habitica is a free habit and productivity app that treats your real life like a game. Habitica can help you achieve your goals to become healthy and happy.">
<meta name="keywords" content="Habits,Goals,Todo,Gamification,Health,Fitness,School,Work">
<link href="https://fonts.googleapis.com/css?family=Roboto+Condensed:400,400i,700,700i|Roboto:400,400i,700,700i" rel="stylesheet">
<link rel="shortcut icon" sizes="48x48" href="/static/icons/favicon.ico">
<link rel="shortcut icon" sizes="192x192" href="/static/icons/favicon_192x192.png">
<link rel="mask-icon" href="/static/icons/favicon.ico">
<meta property="og:image" content="/static/emails/images/meta-image.png" />
<script type="module" src="/src/main-static.js"></script>
</head>
<body>
<div id="loading-screen">
<svg width="80" height="80" viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg">
<path
xmlns="http://www.w3.org/2000/svg"
fill-rule="evenodd"
clip-rule="evenodd"
d="M79.05 72.15c-.8-1.766-2.643-2.62-3.845-1.766-1.201.855-2.867.985-4.448.602-1.584-.385-1.885-4.01-1.543-8.195.342-4.184.909-5.795 1.267-7.314.404-1.524 2.191-1.404 2.405-.209.215 1.196 1.454 1.196 3.266-.979 1.811-2.175 1.543-8.52-.546-13.684-2.088-5.163.817-4.661 1.66-4.149.844.513 1.362-.255 1.156-3.2-.204-2.945-2.916-5.247-5.096-6.657-2.184-1.41-4.842-2.967-4.78-6.745.063-3.777 5.2-3.658 5.897-3.596.697.063 2.037-.233 1.264-4.157-.773-3.924-3.575-4.673-5.332-4.567-1.758.106-2.943 1.071-5.427.133-2.484-.938-4.136-.572-6.45-.057-2.313.515-5.343 1.94-9.112 2.959-1.989.545-2.661.683-4.828.718-1.33.02-1.885 1.633-.106 3.61 1.408 1.608 4.597 2.036 6.515 1.768 1.236-.174 1.521.645 1.407 1.85a20.023 20.023 0 0 0-.024 4.488c.198 1.5.45 4.051-.258 5.713-.35.817-1.361 1.693-2.449 1.633-1.413-.084-2.555-1.75-3.537-3.726-2.06-4.152-4.48-5.033-13.509-8.835-8.12-3.417-12.516-8.749-15.24-12.185-2.421-3.042-4.846-1.89-4.626.855.179 2.128 1.48 9.008 4.781 13.141 4.058 6.314 10.32 9.177 17.534 9.739 1.885.149 3.065.52 3.225 1.383.236 1.835-1.557 3.11-4.898 2.722-3.341-.39-4.768.22-4.103 2.121 2.123 4.477 7.021 4.672 9.058 4.857.686.122 3.114 0 4.41.355 1.51.418 1.836 2.514-.353 3.648-3.892 1.903-5.59 3.479-7.561 7.075-1.486 2.826-2.77 7.555-1.435 14.365 1.283 6.62-8.342 6.83-12.497 5.89-1.793-.377-3.675-3.778.716-6.625 3.553-2.305 4.269-3.724 4.111-6.642-.184-3.4-2.058-3.644-2.053-6.598v-7.05c0-.602-.488-1.088-1.087-1.088h-3.334a1.087 1.087 0 0 1-1.087-1.087v-4.25c0-.602-.488-1.087-1.088-1.087h-3.317a1.087 1.087 0 0 1-1.087-1.088v-3.81c0-.602-.489-1.087-1.088-1.087h-4.04a1.087 1.087 0 0 1-1.089-1.088V26.25c0-.602-.488-1.088-1.087-1.088H1.088C.485 25.161 0 25.65 0 26.25v4.26c0 .602.488 1.087 1.088 1.087h4.049c.602 0 1.087.489 1.087 1.088v15.192c0 .602.489 1.087 1.088 1.087h4.277c.602 0 1.088.489 1.088 1.088v4.968c0 .602.488 1.087 1.087 1.087h6.005c1.836-.13 2.156 2.335 2.137 3.214-.04 2.007-2.308 2.652-3.382 3.487-2.861 2.21-5.077 4.459-3.78 8.781l.032.09c2.362 5.017 8.855 4.499 12.956 4.499h25.817c1.459 0 2.959.339 2.614-1.362-.342-1.7-1.063-4.024-3.162-4.024-2.1 0-1.758 1.166-3.81.57-2.054-.597-2.057-3.371 1.027-8.198 3.19-4.122 8.652-3.81 11.952-.895 3.301 2.915 2.325 7.978 1.633 10.885-.396 2.048.545 3.06 1.67 3.032H78.58c2.015-.035 1.62-1.391.464-4.035h.008z"
fill="#fff"
>
</path>
</svg>
</div>
<div id="app"></div>
<script type="text/javascript" src="//cloudfront.loggly.com/js/loggly.tracker-latest.min.js" async></script>
<!-- Translations -->
</body>
</html>

View File

@@ -1,64 +0,0 @@
import fs from 'fs';
import path from 'path';
import { approvedLanguages } from '../common/script/libs/i18n';
const localePath = path.join(__dirname, '../common/locales/');
const translations = {};
const momentLangs = {};
function loadFile (file) {
const contents = fs.readFileSync(file);
return JSON.parse(contents);
}
function _loadTranslations (locale) {
const files = fs.readdirSync(path.join(localePath, locale));
translations[locale] = {};
files.forEach(file => {
if (path.extname(file) !== '.json') return;
const t = loadFile(path.join(localePath, locale, file))
translations[locale] = {
...translations[locale],
...t,
}
});
}
// First fetch English strings so we can merge them with missing strings in other languages
_loadTranslations('en');
// Then load all other languages
approvedLanguages.forEach(file => {
if (file === 'en' || fs.statSync(path.join(localePath, file)).isDirectory() === false) return;
_loadTranslations(file);
// Strip empty strings, then merge missing strings from english
translations[file] = {
...translations.en,
...translations[file],
}
});
export default function localePlugin() {
const virtualModuleId = 'virtual:translations'
const resolvedVirtualModuleId = '\0' + virtualModuleId
return {
name: 'locale', // required, will show up in warnings and errors
resolveId(id) {
if (id === virtualModuleId) {
return resolvedVirtualModuleId
}
},
load(id) {
if (id === resolvedVirtualModuleId) {
return `export default ${JSON.stringify(translations)}`;
}
},
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,7 @@
"postinstall": "node ./scripts/npm-postinstall.js"
},
"dependencies": {
"@froxz/vite-plugin-s3": "^1.6.0",
"@vitejs/plugin-vue2": "^2.3.3",
"@vue/test-utils": "1.0.0-beta.29",
"amplitude-js": "^8.21.3",
@@ -31,6 +32,7 @@
"intro.js": "^7.2.0",
"jquery": "^3.7.1",
"lodash": "^4.17.21",
"markdown-it": "^14.0.0",
"moment": "^2.29.4",
"nconf": "^0.12.1",
"sass": "^1.63.4",
@@ -40,7 +42,7 @@
"uuid": "^9.0.1",
"validator": "^13.9.0",
"vite": "^6.0.0",
"vite-plugin-prerender": "^1.0.8",
"vite-plugin-compression2": "^1.3.3",
"vue": "^2.7.10",
"vue-fragment": "^1.6.0",
"vue-mugen-scroll": "^0.2.6",
@@ -50,8 +52,6 @@
},
"devDependencies": {
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@prerenderer/renderer-puppeteer": "^1.2.4",
"@prerenderer/rollup-plugin": "^0.3.12",
"@vitest/browser": "^3.0.5",
"babel-plugin-lodash": "^3.3.4",
"inspectpack": "^4.7.1",

View File

@@ -29,7 +29,9 @@
</div>
<snackbars />
<router-view v-if="!isUserLoggedIn || isStaticPage" />
<user-main v-else />
<div v-else>
<user-main />
</div>
</div>
</template>
@@ -108,16 +110,16 @@ import axios from 'axios';
import * as Analytics from '@/libs/analytics';
import { mapState } from '@/libs/store';
import userMain from '@/pages/user-main';
import snackbars from '@/components/snackbars/notifications';
import { LOCALSTORAGE_AUTH_KEY } from '@/libs/auth';
const COMMUNITY_MANAGER_EMAIL = import.meta.env.EMAILS_COMMUNITY_MANAGER_EMAIL; // eslint-disable-line
const COMMUNITY_MANAGER_EMAIL = import.meta.env.EMAILS_COMMUNITY_MANAGER_EMAIL;
export default {
name: 'App',
components: {
snackbars,
userMain,
userMain: () => import('@/pages/user-main'),
},
data () {
return {
@@ -221,11 +223,10 @@ export default {
const errorData = error.response.data;
const errorMessage = errorData.message || errorData;
const errorCode = errorData.error;
// Check for conditions to reset the user auth
// TODO use a specific error like NotificationNotFound instead of checking for the string
const invalidUserMessage = [this.$t('invalidCredentials'), 'Missing authentication headers.'];
if (invalidUserMessage.indexOf(errorMessage) !== -1) {
// If 'invalid_credentials' signaled, force logout
if (error.response.status === 401 && errorCode === 'invalid_credentials') {
this.$store.dispatch('auth:logout', { redirectToLogin: true });
return null;
}
@@ -268,16 +269,29 @@ export default {
const loadingScreen = document.getElementById('loading-screen');
if (loadingScreen) document.body.removeChild(loadingScreen);
if (this.isStaticPage || !this.isUserLoggedIn) {
this.hideLoadingScreen();
// Check if we need to show password change success message
if (sessionStorage.getItem('passwordChangeSuccess') === 'true') {
sessionStorage.removeItem('passwordChangeSuccess');
this.$store.dispatch('snackbars:add', {
title: 'Habitica',
text: this.$t('passwordSuccess'),
type: 'success',
timeout: true,
});
}
this.$router.onReady(() => {
if (this.isStaticPage || !this.isUserLoggedIn) {
this.hideLoadingScreen();
}
});
},
methods: {
hideLoadingScreen () {
this.loading = false;
},
checkForBannedUser (error) {
const AUTH_SETTINGS = localStorage.getItem('habit-mobile-settings');
const AUTH_SETTINGS = localStorage.getItem(LOCALSTORAGE_AUTH_KEY);
const parseSettings = JSON.parse(AUTH_SETTINGS);
const errorMessage = error.response.data.message;
@@ -301,4 +315,3 @@ export default {
</script>
<style src="@/assets/scss/index.scss" lang="scss"></style>
<style src="@/assets/scss/sprites.scss" lang="scss"></style>

View File

@@ -22,7 +22,8 @@
height: 219px;
}
.Pet_HatchingPotion_Dessert, .Pet_HatchingPotion_Veggie, .Pet_HatchingPotion_Windup, .Pet_HatchingPotion_VirtualPet, .Pet_HatchingPotion_Fungi {
.Pet_HatchingPotion_Dessert, .Pet_HatchingPotion_Veggie, .Pet_HatchingPotion_Windup,
.Pet_HatchingPotion_VirtualPet, .Pet_HatchingPotion_Fungi, .Pet_HatchingPotion_Cryptid {
width: 68px;
height: 68px;
}
@@ -47,6 +48,10 @@
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Fungi.gif") no-repeat;
}
.Pet_HatchingPotion_Cryptid {
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Cryptid.gif") no-repeat;
}
.Gems {
display:inline-block;
margin-right:5px;
@@ -172,7 +177,7 @@
height: 96px;
}
.Mount_Head_Gryphon-Gryphatrice, .Mount_Body_Gryphon-Gryphatrice {
.Mount_Head_Gryphon-Gryphatrice, .Mount_Body_Gryphon-Gryphatrice, .Mount_Head_Dragon-Hydra, .Mount_Body_Dragon-Hydra {
width: 135px;
height: 135px;
}
@@ -185,6 +190,14 @@
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Mount-Body-Gryphatrice.gif") no-repeat;
}
.Mount_Head_Dragon-Hydra {
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Mount_Head_Dragon-Hydra.gif") no-repeat;
}
.Mount_Body_Dragon-Hydra {
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Mount_Body_Dragon-Hydra.gif") no-repeat;
}
.background_airship, .background_clocktower, .background_steamworks {
width: 141px;
height: 147px;

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,7 @@
top: -16px !important;
}
$foolPets: Veggie, Dessert, VirtualPet, TeaShop, Fungi;
$foolPets: Veggie, Dessert, VirtualPet, TeaShop, Fungi, Cryptid;
@each $foolPet in $foolPets {
.Pet.Pet-FlyingPig-#{$foolPet} {

View File

@@ -316,3 +316,9 @@
line-height: 2;
padding: 2px 2px;
}
.btn-lg {
font-size: 1.25rem;
line-height: 1.5;
padding: .5rem 1rem;
}

View File

@@ -46,13 +46,11 @@
.background {
background-repeat: repeat-x;
height:216px;
width: 100%;
position: absolute;
top: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: center;
@@ -67,6 +65,13 @@
flex-direction: column;
}
.shop-message {
position: relative;
height: 76px;
margin: 71px auto;
width: 240px;
}
.npc {
position: absolute;
left: 0;

View File

@@ -0,0 +1,3 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M4,0C1.79,0,0,1.79,0,4v16c0,2.21,1.79,4,4,4h16c2.21,0,4-1.79,4-4V4c0-2.21-1.79-4-4-4H4ZM12,11.57c-.72-1.49-2.7-4.26-4.53-5.63-1.32-.99-3.47-1.75-3.47.68,0,.49.28,4.08.44,4.66.57,2.03,2.65,2.55,4.5,2.23-3.24.55-4.06,2.36-2.28,4.17,3.38,3.44,4.85-.86,5.23-1.97h0s0,0,0,0c.07-.2.1-.29.1-.21,0-.08.03.01.1.22h0c.38,1.1,1.85,5.41,5.23,1.97,1.78-1.81.95-3.63-2.28-4.17,1.85.31,3.93-.2,4.5-2.23.16-.58.44-4.18.44-4.66,0-2.43-2.14-1.67-3.47-.68-1.83,1.37-3.81,4.14-4.53,5.63Z" fill-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 572 B

View File

@@ -1,3 +0,0 @@
<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: 622 B

View File

@@ -25,9 +25,9 @@
<router-link to="/">
Homepage
</router-link>or
<router-link :to="contactUsLink">
<a href="mailto:admin@habitica.com">
Contact Us
</router-link>about the issue.
</a>about the issue.
</p>
</div>
</div>
@@ -40,12 +40,6 @@ import { mapState } from '@/libs/store';
export default {
computed: {
...mapState(['isUserLoggedIn']),
contactUsLink () {
if (this.isUserLoggedIn) {
return { name: 'guild', params: { groupId: 'a29da26b-37de-4a71-b0c6-48e72a900dac' } };
}
return { name: 'contact' };
},
retiredChatPage () {
return this.$route.fullPath.indexOf('/groups') !== -1;
},

View File

@@ -8,7 +8,7 @@
<div class="modal-body">
<div class="row">
<div class="col-6 offset-3">
<div class="shop_armoire"></div>
<Sprite image-name="shop_armoire" />
<p>{{ $t('armoireLastItem') }}</p>
<p>{{ $t('armoireNotesEmpty') }}</p>
</div>
@@ -34,7 +34,12 @@
</style>
<script>
import Sprite from '@/components/ui/sprite';
export default {
components: {
Sprite,
},
methods: {
close () {
this.$root.$emit('bv::hide::modal', 'armoire-empty');

View File

@@ -95,7 +95,11 @@
@click="clickDisableClasses(); close();"
>{{ $t('optOutOfClasses') }}</span>
</div>
<span class="opt-out-description">{{ $t('optOutOfClassesText') }}</span>
<div
v-once
class="opt-out-description"
v-html="$t('optOutOfClassesText')"
></div>
</div>
</div>
</div>

View File

@@ -48,7 +48,7 @@
></span>
</div>
<div :class="questClass"></div>
<Sprite :image-name="questClass" />
</section>
<!-- @TODO: Keep this? .checkboxinput(type='checkbox', v-model=
'user.preferences.suppressModals.levelUp', @change='changeLevelupSuppress()')
@@ -150,15 +150,12 @@ label(style='display:inline-block') {{ $t('dontShowAgain') }}
section.greyed {
padding-bottom: 17px
}
.scroll {
margin: -11px auto 0;
}
}
</style>
<script>
import Avatar from '../avatar';
import Sprite from '@/components/ui/sprite';
import { mapState } from '@/libs/store';
import starGroup from '@/assets/svg/star-group.svg?raw';
import sparkles from '@/assets/svg/sparkles-left.svg?raw';
@@ -173,6 +170,7 @@ const levelQuests = {
export default {
components: {
Avatar,
Sprite,
},
data () {
return {
@@ -191,7 +189,9 @@ export default {
return this.user.stats.lvl in levelQuests;
},
questClass () {
return `scroll inventory_quest_scroll_${levelQuests[this.user.stats.lvl]}`;
const questKey = levelQuests[this.user.stats.lvl];
if (questKey) return `inventory_quest_scroll_${questKey}`;
return '';
},
},
methods: {

View File

@@ -97,9 +97,9 @@ import { mapState } from '@/libs/store';
import Sprite from '@/components/ui/sprite';
export default {
components: [
components: {
Sprite,
],
},
data () {
return {
maxHealth,

View File

@@ -55,7 +55,7 @@
<p v-html="$t('moreGearAchievements')"></p>
<br>
</div>
<div class="shop_armoire"></div>
<Sprite image-name="shop_armoire" />
<p v-html="$t('armoireUnlocked')"></p>
<br>
<button
@@ -87,11 +87,13 @@
import achievementFooter from './achievementFooter';
import achievementAvatar from './achievementAvatar';
import { mapState } from '@/libs/store';
import Sprite from '@/components/ui/sprite.vue';
export default {
components: {
achievementFooter,
achievementAvatar,
Sprite,
},
computed: {
...mapState({ user: 'user.data' }),

View File

@@ -1,20 +0,0 @@
export default {
methods: {
async saveHero ({ hero, msg = 'User', clearData }) {
await this.$store.dispatch('hall:updateHero', { heroDetails: hero });
await this.$store.dispatch('snackbars:add', {
title: '',
text: `${msg} updated`,
type: 'info',
});
if (clearData) {
// Use clearData when the saved changes may affect data in other components
// (e.g., adding a contributor tier will increase the Gem balance)
// The admin should re-fetch the data if they need to keep working on that user.
this.$emit('clear-data');
this.$router.push({ name: 'adminPanel' });
}
},
},
};

View File

@@ -1,268 +0,0 @@
<template>
<form @submit.prevent="saveHero({ hero, msg: 'Subscription Perks' })">
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{ 'open': expand }"
@click="expand = !expand"
>
Subscription, Monthly Perks
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
<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.planId == 'group_plan_auto'">
Group plan ID:
<strong>{{ hero.purchased.plan.owner }}</strong>
</div>
<div
v-if="hero.purchased.plan.dateCreated"
class="form-group row"
>
<label class="col-sm-3 col-form-label">
Creation date:
</label>
<div class="col-sm-9">
<div class="input-group">
<input
v-model="hero.purchased.plan.dateCreated"
class="form-control"
type="text"
>
<div class="input-group-append">
<strong class="input-group-text">
{{ dateFormat(hero.purchased.plan.dateCreated) }}
</strong>
</div>
</div>
</div>
</div>
<div
v-if="hero.purchased.plan.dateCurrentTypeCreated"
class="form-group row"
>
<label class="col-sm-3 col-form-label">
Current sub start date:
</label>
<div class="col-sm-9">
<div class="input-group">
<input
v-model="hero.purchased.plan.dateCurrentTypeCreated"
class="form-control"
type="text"
>
<div class="input-group-append">
<strong class="input-group-text">
{{ dateFormat(hero.purchased.plan.dateCurrentTypeCreated) }}
</strong>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Termination date:
</label>
<div class="col-sm-9">
<div class="input-group">
<input
v-model="hero.purchased.plan.dateTerminated"
class="form-control"
type="text"
>
<div class="input-group-append">
<strong class="input-group-text">
{{ dateFormat(hero.purchased.plan.dateTerminated) }}
</strong>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Cumulative months:
</label>
<div class="col-sm-9">
<input
v-model="hero.purchased.plan.cumulativeCount"
class="form-control"
type="number"
min="0"
step="1"
>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Received hourglass bonus:
</label>
<div class="col-sm-9">
<div class="input-group">
<input
v-model="hero.purchased.plan.hourglassPromoReceived"
class="form-control"
type="text"
>
<div class="input-group-append">
<strong class="input-group-text">
{{ dateFormat(hero.purchased.plan.hourglassPromoReceived) }}
</strong>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Mystic Hourglasses:
</label>
<div class="col-sm-9">
<input
v-model="hero.purchased.plan.consecutive.trinkets"
class="form-control"
type="number"
min="0"
step="1"
>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Gem cap increase:
</label>
<div class="col-sm-9">
<input
v-model="hero.purchased.plan.consecutive.gemCapExtra"
class="form-control"
type="number"
min="0"
max="26"
step="2"
>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Total Gem cap:
</label>
<strong class="col-sm-9 col-form-label">
{{ Number(hero.purchased.plan.consecutive.gemCapExtra) + 24 }}
</strong>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Gems bought this month:
</label>
<div class="col-sm-9">
<input
v-model="hero.purchased.plan.gemsBought"
class="form-control"
type="number"
min="0"
:max="hero.purchased.plan.consecutive.gemCapExtra + 24"
step="1"
>
</div>
</div>
<div v-if="hero.purchased.plan.extraMonths > 0">
Additional credit (applied upon cancellation):
<strong>{{ hero.purchased.plan.extraMonths }}</strong>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Mystery Items:
</label>
<div class="col-sm-9 col-form-label">
<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>
</div>
</div>
<div
v-if="expand"
class="card-footer"
>
<input
type="submit"
value="Save"
class="btn btn-primary mt-1"
>
</div>
</div>
</form>
</template>
<style lang="scss" scoped>
@import '@/assets/scss/colors.scss';
.input-group-append {
width: auto;
.input-group-text {
border-bottom-right-radius: 2px;
border-top-right-radius: 2px;
font-weight: 600;
font-size: 0.8rem;
color: $gray-200;
}
}
</style>
<script>
import moment from 'moment';
import { getPlanContext } from '@/../../common/script/cron';
import saveHero from '../mixins/saveHero';
export default {
mixins: [saveHero],
props: {
hero: {
type: Object,
required: true,
},
},
data () {
return {
expand: false,
};
},
computed: {
nextHourglassDate () {
const currentPlanContext = getPlanContext(this.hero, new Date());
if (!currentPlanContext.nextHourglassDate) return 'N/A';
return currentPlanContext.nextHourglassDate.format('MMMM YYYY');
},
},
methods: {
dateFormat (date) {
if (!date) {
return '--';
}
return moment(date).format('YYYY/MM/DD');
},
},
};
</script>

View File

@@ -1,7 +1,7 @@
<template>
<div class="row standard-page col-12 d-flex justify-content-center">
<div class="admin-panel-content">
<h1>Admin Panel</h1>
<h1>{{ $t("adminPanel") }}</h1>
<form
class="form-inline"
@submit.prevent="searchUsers(userIdentifier)"
@@ -72,7 +72,7 @@ export default {
},
mounted () {
this.$store.dispatch('common:setTitle', {
section: 'Admin Panel',
section: this.$t('adminPanel'),
});
},
methods: {
@@ -92,8 +92,6 @@ export default {
params: { userIdentifier },
}).catch(failure => {
if (isNavigationFailure(failure, NavigationFailureType.duplicated)) {
// the admin has requested that the same user be displayed again so reload the page
// (e.g., if they changed their mind about changes they were making)
this.$router.go();
}
});
@@ -101,14 +99,16 @@ export default {
async loadUser (userIdentifier) {
const id = userIdentifier || this.user._id;
this.$router.push({
if (this.$router.currentRoute.name === 'adminPanelUser') {
await this.$router.push({
name: 'adminPanel',
});
}
await this.$router.push({
name: 'adminPanelUser',
params: { userIdentifier: id },
}).catch(failure => {
if (isNavigationFailure(failure, NavigationFailureType.duplicated)) {
// the admin has requested that the same user be displayed again so reload the page
// (e.g., if they changed their mind about changes they were making)
this.$router.go();
}
});

View File

@@ -0,0 +1,43 @@
import VueRouter from 'vue-router';
const { isNavigationFailure, NavigationFailureType } = VueRouter;
export default {
methods: {
async saveHero ({
hero,
msg = 'User',
clearData,
reloadData,
}) {
await this.$store.dispatch('hall:updateHero', { heroDetails: hero });
await this.$store.dispatch('snackbars:add', {
title: '',
text: `${msg} updated`,
type: 'info',
});
if (clearData) {
// Use clearData when the saved changes may affect data in other components
// (e.g., adding a contributor tier will increase the Gem balance)
// The admin should re-fetch the data if they need to keep working on that user.
this.$emit('clear-data');
this.$router.push({ name: 'adminPanel' });
} else if (reloadData) {
if (this.$router.currentRoute.name === 'adminPanelUser') {
await this.$router.push({
name: 'adminPanel',
});
}
await this.$router.push({
name: 'adminPanelUser',
params: { userIdentifier: hero._id },
}).catch(failure => {
if (isNavigationFailure(failure, NavigationFailureType.duplicated)) {
this.$router.go();
}
});
}
},
},
};

View File

@@ -55,7 +55,7 @@
<script>
import VueRouter from 'vue-router';
import { mapState } from '@/libs/store';
import LoadingSpinner from '../ui/loadingSpinner';
import LoadingSpinner from '../../ui/loadingSpinner';
const { isNavigationFailure, NavigationFailureType } = VueRouter;
@@ -63,6 +63,10 @@ export default {
components: {
LoadingSpinner,
},
beforeRouteUpdate (to, from, next) {
this.userIdentifier = to.params.userIdentifier;
next();
},
data () {
return {
userIdentifier: '',
@@ -74,10 +78,6 @@ export default {
computed: {
...mapState({ user: 'user.data' }),
},
beforeRouteUpdate (to, from, next) {
this.userIdentifier = to.params.userIdentifier;
next();
},
watch: {
userIdentifier () {
this.isSearching = true;

View File

@@ -17,6 +17,7 @@
<li
v-for="item in achievements"
:key="item.path"
v-b-tooltip.hover="item.notes"
>
<form @submit.prevent="saveItem(item)">
<span
@@ -27,7 +28,7 @@
{{ item.value }}
</span>
:
{{ item.text || item.key }}
{{ item.text || item.key }} - <i> {{ item.key }} </i>
</span>
<div
@@ -68,6 +69,7 @@
<li
v-for="item in nestedAchievements[achievementType]"
:key="item.path"
v-b-tooltip.hover="item.notes"
>
<form @submit.prevent="saveItem(item)">
<span
@@ -78,7 +80,7 @@
{{ item.value }}
</span>
:
{{ item.text || item.key }}
{{ item.text || item.key }} - <i> {{ item.key }} </i>
</span>
<div
@@ -143,79 +145,28 @@ function getText (achievementItem) {
}
const { titleKey } = achievementItem;
if (titleKey !== undefined) {
return i18n.t(titleKey, 'en');
return i18n.t(titleKey);
}
const { singularTitleKey } = achievementItem;
if (singularTitleKey !== undefined) {
return i18n.t(singularTitleKey, 'en');
return i18n.t(singularTitleKey);
}
return achievementItem.key;
}
function collateItemData (self) {
const achievements = [];
const nestedAchievements = {};
const basePath = 'achievements';
const ownedAchievements = self.hero.achievements;
const allAchievements = content.achievements;
for (const key of Object.keys(ownedAchievements)) {
const value = ownedAchievements[key];
if (typeof value === 'object') {
nestedAchievements[key] = [];
for (const nestedKey of Object.keys(value)) {
const valueIsInteger = self.integerTypes.includes(key);
let text = nestedKey;
if (allAchievements[key] && allAchievements[key][nestedKey]) {
text = getText(allAchievements[key][nestedKey]);
}
nestedAchievements[key].push({
key: nestedKey,
text,
achievementType: key,
modified: false,
path: `${basePath}.${key}.${nestedKey}`,
value: value[nestedKey],
valueIsInteger,
});
}
} else {
const valueIsInteger = self.integerTypes.includes(key);
achievements.push({
key,
text: getText(allAchievements[key]),
modified: false,
path: `${basePath}.${key}`,
value: ownedAchievements[key],
valueIsInteger,
});
}
function getNotes (achievementItem, count) {
if (achievementItem === undefined) {
return '';
}
for (const key of Object.keys(allAchievements)) {
if (key !== '' && !key.endsWith('UltimateGear') && !key.endsWith('Quest')) {
if (ownedAchievements[key] === undefined) {
const valueIsInteger = self.integerTypes.includes(key);
achievements.push({
key,
text: getText(allAchievements[key]),
modified: false,
path: `${basePath}.${key}`,
value: valueIsInteger ? 0 : false,
valueIsInteger,
neverOwned: true,
});
}
}
const { textKey } = achievementItem;
if (textKey !== undefined) {
return i18n.t(textKey, { count });
}
self.achievements = achievements;
self.nestedAchievements = nestedAchievements;
}
function resetData (self) {
collateItemData(self);
self.nestedAchievementKeys.forEach(itemType => { self.expandItemType[itemType] = false; });
const { singularTextKey } = achievementItem;
if (singularTextKey !== undefined) {
return i18n.t(singularTextKey, { count });
}
return '';
}
export default {
@@ -241,26 +192,34 @@ export default {
},
nestedAchievementKeys: ['quests', 'ultimateGearSets'],
integerTypes: ['streak', 'perfect', 'birthday', 'habiticaDays', 'habitSurveys', 'habitBirthdays',
'valentine', 'congrats', 'shinySeed', 'goodluck', 'thankyou', 'seafoam', 'snowball', 'quests'],
'valentine', 'congrats', 'shinySeed', 'goodluck', 'thankyou', 'seafoam', 'snowball', 'quests',
'rebirths', 'rebirthLevel', 'greeting', 'spookySparkles', 'nye', 'costumeContests', 'congrats',
'getwell', 'beastMasterCount', 'mountMasterCount', 'triadBingoCount',
],
cardTypes: ['greeting', 'birthday', 'valentine', 'goodluck', 'thankyou', 'greeting', 'nye',
'congrats', 'getwell'],
achievements: [],
nestedAchievements: {},
};
},
watch: {
resetCounter () {
resetData(this);
this.resetData();
},
},
mounted () {
resetData(this);
this.resetData();
},
methods: {
async saveItem (item) {
// prepare the item's new value and path for being saved
this.hero.achievementPath = item.path;
this.hero.achievementVal = item.value;
await this.saveHero({ hero: this.hero, msg: item.path });
await this.saveHero({
hero: {
_id: this.hero._id,
achievementPath: item.path,
achievementVal: item.value,
},
msg: item.path,
});
item.modified = false;
},
enableValueChange (item) {
@@ -270,6 +229,85 @@ export default {
item.value = !item.value;
}
},
resetData () {
this.collateItemData();
this.nestedAchievementKeys.forEach(itemType => { this.expandItemType[itemType] = false; });
},
collateItemData () {
const achievements = [];
const nestedAchievements = {};
const basePath = 'achievements';
const ownedAchievements = this.hero.achievements;
const allAchievements = content.achievements;
const ownedKeys = Object.keys(ownedAchievements).sort();
for (const key of ownedKeys) {
const value = ownedAchievements[key];
let contentKey = key;
if (this.cardTypes.indexOf(key) !== -1) {
contentKey += 'Cards';
}
if (typeof value === 'object') {
nestedAchievements[key] = [];
for (const nestedKey of Object.keys(value)) {
const valueIsInteger = this.integerTypes.includes(key);
let text = nestedKey;
if (allAchievements[key] && allAchievements[key][contentKey]) {
text = getText(allAchievements[key][contentKey]);
}
let notes = '';
if (allAchievements[key] && allAchievements[key][contentKey]) {
notes = getNotes(allAchievements[key][contentKey], ownedAchievements[key]);
}
nestedAchievements[key].push({
key: nestedKey,
text,
notes,
achievementType: key,
modified: false,
path: `${basePath}.${key}.${nestedKey}`,
value: value[nestedKey],
valueIsInteger,
});
}
} else {
const valueIsInteger = this.integerTypes.includes(key);
achievements.push({
key,
text: getText(allAchievements[contentKey]),
notes: getNotes(allAchievements[contentKey], ownedAchievements[key]),
modified: false,
path: `${basePath}.${key}`,
value: ownedAchievements[key],
valueIsInteger,
});
}
}
const allKeys = Object.keys(allAchievements).sort();
for (const key of allKeys) {
if (key !== '' && !key.endsWith('UltimateGear') && !key.endsWith('Quest')) {
const ownedKey = key.replace('Cards', '');
if (ownedAchievements[ownedKey] === undefined) {
const valueIsInteger = this.integerTypes.includes(ownedKey);
achievements.push({
key: ownedKey,
text: getText(allAchievements[key]),
notes: getNotes(allAchievements[key], 0),
modified: false,
path: `${basePath}.${ownedKey}`,
value: valueIsInteger ? 0 : false,
valueIsInteger,
neverOwned: true,
});
}
}
}
this.achievements = achievements;
this.nestedAchievements = nestedAchievements;
},
},
};
</script>

View File

@@ -1,5 +1,12 @@
<template>
<form @submit.prevent="saveHero({ hero, msg: 'Contributor details', clearData: true })">
<form
@submit.prevent="saveHero({ hero: {
_id: hero._id,
contributor: hero.contributor,
secret: hero.secret,
permissions: hero.permissions,
}, msg: 'Contributor details', clearData: true })"
>
<div class="card mt-2">
<div class="card-header">
<h3
@@ -8,6 +15,12 @@
@click="expand = !expand"
>
Contributor Details
<b
v-if="hasUnsavedChanges && !expand"
class="text-warning float-right"
>
Unsaved changes
</b>
</h3>
</div>
<div
@@ -25,12 +38,17 @@
>
<div class="custom-control custom-checkbox">
<input
:id="permission.key"
v-model="hero.permissions[permission.key]"
:disabled="!hasPermission(user, permission.key)"
:disabled="!hasPermission(user, permission.key)
|| (hero.permissions.fullAccess && permission.key !== 'fullAccess')"
class="custom-control-input"
type="checkbox"
>
<label class="custom-control-label">
<label
class="custom-control-label"
:for="permission.key"
>
{{ permission.name }}<br>
<small class="text-secondary">{{ permission.description }}</small>
</label>
@@ -104,13 +122,19 @@
</div>
<div
v-if="expand"
class="card-footer"
class="card-footer d-flex align-items-center justify-content-between"
>
<input
type="submit"
value="Save"
class="btn btn-primary mt-1"
>
<b
v-if="hasUnsavedChanges"
class="text-warning float-right"
>
Unsaved changes
</b>
</div>
</div>
</form>
@@ -131,7 +155,7 @@ import markdownDirective from '@/directives/markdown';
import saveHero from '../mixins/saveHero';
import { mapState } from '@/libs/store';
import { userStateMixin } from '../../../mixins/userState';
import { userStateMixin } from '../../../../mixins/userState';
const permissionList = [
{
@@ -159,6 +183,11 @@ const permissionList = [
name: 'Challenge Admin',
description: 'Can create official habitica challenges and admin all challenges',
},
{
key: 'accessControl',
name: 'Access Control',
description: 'Can manage IP-Address, Client and E-Mail blockers',
},
{
key: 'coupons',
name: 'Coupon Creator',
@@ -190,6 +219,10 @@ export default {
type: Object,
required: true,
},
hasUnsavedChanges: {
type: Boolean,
required: true,
},
},
data () {
return {

View File

@@ -1,5 +1,11 @@
<template>
<form @submit.prevent="saveHero({ hero, msg: 'Authentication' })">
<form
@submit.prevent="saveHero({ hero: {
_id: hero._id,
auth: hero.auth,
preferences: hero.preferences,
}, msg: 'Authentication' })"
>
<div class="card mt-2">
<div class="card-header">
<h3
@@ -56,12 +62,12 @@
<div class="col-sm-9 col-form-label">
<strong>
{{ hero.auth.timestamps.loggedin | formatDate }}</strong>
<button
<a
class="btn btn-warning btn-sm ml-4"
@click="resetCron()"
>
Reset Cron to Yesterday
</button>
</a>
</div>
</div>
<div class="form-group row">
@@ -113,13 +119,14 @@
<div class="form-group row">
<label class="col-sm-3 col-form-label">API Token</label>
<div class="col-sm-9">
<button
<a
href="#"
value="Change API Token"
class="btn btn-danger"
@click="changeApiToken()"
>
Change API Token
</button>
</a>
<div
v-if="tokenModified"
>
@@ -271,13 +278,24 @@ export default {
return false;
},
async changeApiToken () {
this.hero.changeApiToken = true;
await this.saveHero({ hero: this.hero, msg: 'API Token' });
await this.saveHero({
hero: {
_id: this.hero._id,
changeApiToken: true,
},
msg: 'API Token',
});
this.tokenModified = true;
},
resetCron () {
this.hero.resetCron = true;
this.saveHero({ hero: this.hero, msg: 'Last Cron', clearData: true });
this.saveHero({
hero: {
_id: this.hero._id,
resetCron: true,
},
msg: 'Last Cron',
clearData: true,
});
},
},
};

View File

@@ -46,7 +46,7 @@
:
<span :class="{ ownedItem: !item.neverOwned }">{{ item.text }}</span>
</span>
{{ item.set }}
- {{ itemType }}.{{ item.key }} - <i> {{ item.set }}</i>
<div
v-if="item.modified"
@@ -232,11 +232,14 @@ export default {
},
methods: {
async saveItem (item) {
// prepare the item's new value and path for being saved
this.hero.purchasedPath = item.path;
this.hero.purchasedVal = item.value;
await this.saveHero({ hero: this.hero, msg: item.path });
await this.saveHero({
hero: {
_id: this.hero._id,
purchasedPath: item.path,
purchasedVal: item.value,
},
msg: item.path,
});
item.modified = false;
},
enableValueChange (item) {

View File

@@ -15,10 +15,17 @@
<privileges-and-gems
:hero="hero"
:reset-counter="resetCounter"
:has-unsaved-changes="hasUnsavedChanges([hero.flags, unModifiedHero.flags],
[hero.auth, unModifiedHero.auth],
[hero.balance, unModifiedHero.balance],
[hero.secret, unModifiedHero.secret])"
/>
<subscription-and-perks
:hero="hero"
:group-plans="groupPlans"
:has-unsaved-changes="hasUnsavedChanges([hero.purchased.plan,
unModifiedHero.purchased.plan])"
/>
<cron-and-auth
@@ -29,6 +36,7 @@
<user-profile
:hero="hero"
:reset-counter="resetCounter"
:has-unsaved-changes="hasUnsavedChanges([hero.profile, unModifiedHero.profile])"
/>
<party-and-quest
@@ -47,6 +55,12 @@
:preferences="hero.preferences"
/>
<stats
:hero="hero"
:has-unsaved-changes="hasUnsavedChanges([hero.stats, unModifiedHero.stats])"
:reset-counter="resetCounter"
/>
<items-owned
:hero="hero"
:reset-counter="resetCounter"
@@ -67,8 +81,18 @@
:reset-counter="resetCounter"
/>
<user-history
:hero="hero"
:reset-counter="resetCounter"
/>
<contributor-details
:hero="hero"
:has-unsaved-changes="hasUnsavedChanges(
[hero.contributor, unModifiedHero.contributor],
[hero.permissions, unModifiedHero.permissions],
[hero.secret, unModifiedHero.secret],
)"
:reset-counter="resetCounter"
@clear-data="clearData"
/>
@@ -109,6 +133,7 @@
</style>
<script>
import isEqualWith from 'lodash/isEqualWith';
import BasicDetails from './basicDetails';
import ItemsOwned from './itemsOwned';
import CronAndAuth from './cronAndAuth';
@@ -121,8 +146,10 @@ import Transactions from './transactions';
import SubscriptionAndPerks from './subscriptionAndPerks';
import CustomizationsOwned from './customizationsOwned.vue';
import Achievements from './achievements.vue';
import UserHistory from './userHistory.vue';
import Stats from './stats.vue';
import { userStateMixin } from '../../../mixins/userState';
import { userStateMixin } from '../../../../mixins/userState';
export default {
components: {
@@ -135,6 +162,8 @@ export default {
PrivilegesAndGems,
ContributorDetails,
Transactions,
UserHistory,
Stats,
SubscriptionAndPerks,
UserProfile,
Achievements,
@@ -148,8 +177,10 @@ export default {
return {
userIdentifier: '',
resetCounter: 0,
unModifiedHero: {},
hero: {},
party: {},
groupPlans: [],
hasParty: false,
partyNotExistError: false,
adminHasPrivForParty: true,
@@ -168,6 +199,7 @@ export default {
},
methods: {
clearData () {
this.unModifiedHero = {};
this.hero = {};
},
@@ -176,6 +208,7 @@ export default {
this.$emit('changeUserIdentifier', id); // change user identifier in Admin Panel's form
this.hero = await this.$store.dispatch('hall:getHero', { uuid: id });
this.unModifiedHero = JSON.parse(JSON.stringify(this.hero));
if (!this.hero.flags) {
this.hero.flags = {
@@ -206,8 +239,38 @@ export default {
}
}
if (this.hero.purchased.plan.planId === 'group_plan_auto') {
try {
this.groupPlans = await this.$store.dispatch('hall:getHeroGroupPlans', { heroId: this.hero._id });
} catch (e) {
this.groupPlans = [];
}
}
this.resetCounter += 1; // tell child components to reinstantiate from scratch
},
hasUnsavedChanges (...comparisons) {
for (const index in comparisons) {
if (index && comparisons[index]) {
const objs = comparisons[index];
const obj1 = objs[0];
const obj2 = objs[1];
if (!isEqualWith(obj1, obj2, (x, y) => {
if (typeof x === 'object' && typeof y === 'object') {
return undefined;
}
if (x === false && y === undefined) {
// Special case for checkboxes
return true;
}
return x == y; // eslint-disable-line eqeqeq
})) {
return true;
}
}
}
return false;
},
},
};
</script>

View File

@@ -269,16 +269,19 @@ export default {
methods: {
async saveItem (item) {
// prepare the item's new value and path for being saved
this.hero.itemPath = item.path;
const toSave = {
_id: this.hero._id,
};
toSave.itemPath = item.path;
if (item.value === null) {
this.hero.itemVal = 'null';
toSave.itemVal = 'null';
} else if (item.value === false) {
this.hero.itemVal = 'false';
toSave.itemVal = 'false';
} else {
this.hero.itemVal = item.value;
toSave.itemVal = item.value;
}
await this.saveHero({ hero: this.hero, msg: item.key });
await this.saveHero({ hero: toSave, msg: item.key });
item.neverOwned = false;
item.modified = false;
},

View File

@@ -31,22 +31,46 @@
v-html="questErrors"
></p>
</div>
<div>
Party:
<span v-if="userHasParty">
yes: party ID {{ groupPartyData._id }},
member count {{ groupPartyData.memberCount }} (may be wrong)
<br>
<span v-if="userIsPartyLeader">User is the party leader</span>
<span v-else>Party leader is
<router-link :to="{'name': 'userProfile', 'params': {'userId': groupPartyData.leader}}">
{{ groupPartyData.leader }}
</router-link>
</span>
</span>
<span v-else>no</span>
<div v-if="userHasParty">
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Party ID
</label>
<strong class="col-sm-9 col-form-label">
{{ groupPartyData._id }}
</strong>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Estimated Member Count
</label>
<strong class="col-sm-9 col-form-label">
{{ groupPartyData.memberCount }}
</strong>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Leader
</label>
<strong class="col-sm-9 col-form-label">
<span v-if="userIsPartyLeader">User is the party leader</span>
<span v-else>Party leader is
<router-link
:to="{'name': 'userProfile', 'params': {'userId': groupPartyData.leader}}"
>
{{ groupPartyData.leader }}
</router-link>
</span>
</strong>
</div>
<div
class="btn btn-danger"
@click="removeFromParty()"
>
Remove from Party
</div>
</div>
<strong v-else>User is not in a party.</strong>
<div class="subsection-start">
<p v-html="questStatus"></p>
</div>
@@ -56,6 +80,7 @@
<script>
import * as quests from '@/../../common/script/content/quests';
import saveHero from '../mixins/saveHero';
function determineQuestStatus (self) {
// Quest data is in the user doc and party doc. They can be out of sync.
@@ -271,6 +296,7 @@ function resetData (self) {
}
export default {
mixins: [saveHero],
props: {
resetCounter: {
type: Number,
@@ -318,5 +344,14 @@ export default {
mounted () {
resetData(this);
},
methods: {
removeFromParty () {
this.saveHero({
hero: { _id: this.userId, removeFromParty: true },
msg: 'Removed from party',
reloadData: true,
});
},
},
};
</script>

View File

@@ -1,5 +1,13 @@
<template>
<form @submit.prevent="saveHero({hero, msg: 'Privileges or Gems or Moderation Notes'})">
<form
@submit.prevent="saveHero({hero: {
_id: hero._id,
flags: hero.flags,
balance: hero.balance,
auth: hero.auth,
secret: hero.secret,
}, msg: 'Privileges or Gems or Moderation Notes'})"
>
<div class="card mt-2">
<div class="card-header">
<h3
@@ -8,6 +16,12 @@
@click="expand = !expand"
>
Privileges, Gem Balance
<b
v-if="hasUnsavedChanges && !expand"
class="text-warning float-right"
>
Unsaved changes
</b>
</h3>
</div>
<div
@@ -117,13 +131,19 @@
</div>
<div
v-if="expand"
class="card-footer"
class="card-footer d-flex align-items-center justify-content-between"
>
<input
type="submit"
value="Save"
class="btn btn-primary mt-1"
>
<b
v-if="hasUnsavedChanges"
class="text-warning float-right"
>
Unsaved changes
</b>
</div>
</div>
</form>
@@ -169,6 +189,10 @@ export default {
type: Object,
required: true,
},
hasUnsavedChanges: {
type: Boolean,
required: true,
},
},
data () {
return {

View File

@@ -0,0 +1,72 @@
<template>
<div class="form-group row">
<label
class="col-sm-3 col-form-label"
:class="color"
>{{ label }}</label>
<div class="col-sm-9">
<input
:value="value"
class="form-control"
type="number"
:step="step"
:max="max"
:min="min"
@input="$emit('input', parseInt($event.target.value, 10))"
>
</div>
</div>
</template>
<style lang="scss" scoped>
@import '@/assets/scss/colors.scss';
.about-row {
margin-left: 0px;
margin-right: 0px;
}
.red-label {
color: $red_100;
}
.blue-label {
color: $blue_100;
}
.purple-label {
color: $purple_300;
}
.yellow-label {
color: $yellow_50;
}
</style>
<script>
export default {
model: {
prop: 'value',
event: 'input',
},
props: {
label: {
type: String,
required: true,
},
color: {
type: String,
default: 'text-label',
},
value: {
type: Number,
required: true,
},
step: {
type: String,
default: 'any',
},
min: {
},
max: {
},
},
};
</script>

View File

@@ -0,0 +1,319 @@
<template>
<form @submit.prevent="submitClicked()">
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{'open': expand}"
@click="expand = !expand"
>
Stats
<b
v-if="hasUnsavedChanges && !expand"
class="text-warning float-right"
>
Unsaved changes
</b>
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
<stats-row
v-model="hero.stats.hp"
label="Health"
color="red-label"
:max="maxHealth"
/>
<stats-row
v-model="hero.stats.exp"
label="Experience"
color="yellow-label"
min="0"
:max="maxFieldHardCap"
/>
<stats-row
v-model="hero.stats.mp"
label="Mana"
color="blue-label"
min="0"
:max="maxFieldHardCap"
/>
<stats-row
v-model="hero.stats.lvl"
label="Level"
step="1"
min="0"
:max="maxLevelHardCap"
/>
<stats-row
v-model="hero.stats.gp"
label="Gold"
min="0"
:max="maxFieldHardCap"
/>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Selected Class</label>
<div class="col-sm-9">
<select
id="selectedClass"
v-model="hero.stats.class"
class="form-control"
:disabled="hero.stats.lvl < 10"
>
<option value="warrior">
Warrior
</option>
<option value="wizard">
Mage
</option>
<option value="healer">
Healer
</option>
<option value="rogue">
Rogue
</option>
</select>
<small>
When changing class, players usually need stat points deallocated as well.
</small>
</div>
</div>
<h3>Stat Points</h3>
<stats-row
v-model="hero.stats.points"
label="Unallocated"
min="0"
step="1"
:max="maxStatPoints"
/>
<stats-row
v-model="hero.stats.str"
label="Strength"
color="red-label"
min="0"
:max="maxStatPoints"
step="1"
/>
<stats-row
v-model="hero.stats.int"
label="Intelligence"
color="blue-label"
min="0"
:max="maxStatPoints"
step="1"
/>
<stats-row
v-model="hero.stats.per"
label="Perception"
color="purple-label"
min="0"
:max="maxStatPoints"
step="1"
/>
<stats-row
v-model="hero.stats.con"
label="Constitution"
color="yellow-label"
min="0"
:max="maxStatPoints"
step="1"
/>
<div class="form-group row">
<div class="offset-sm-3 col-sm-9">
<button
type="button"
class="btn btn-warning btn-sm"
@click="deallocateStatPoints"
>
Deallocate all stat points
</button>
</div>
</div>
<div
v-if="statPointsIncorrect"
class="form-group row"
>
<div class="offset-sm-3 col-sm-9 text-danger">
Error: Sum of stat points should equal the users level
</div>
</div>
<h3>Buffs</h3>
<stats-row
v-model="hero.stats.buffs.str"
label="Strength"
color="red-label"
min="0"
step="1"
/>
<stats-row
v-model="hero.stats.buffs.int"
label="Intelligence"
color="blue-label"
min="0"
step="1"
/>
<stats-row
v-model="hero.stats.buffs.per"
label="Perception"
color="purple-label"
min="0"
step="1"
/>
<stats-row
v-model="hero.stats.buffs.con"
label="Constitution"
color="yellow-label"
min="0"
step="1"
/>
<div class="form-group row">
<div class="offset-sm-3 col-sm-9">
<button
type="button"
class="btn btn-warning btn-sm"
@click="resetBuffs"
>
Reset Buffs
</button>
</div>
</div>
</div>
<div
v-if="expand"
class="card-footer d-flex align-items-center justify-content-between"
>
<input
type="submit"
value="Save"
class="btn btn-primary mt-1"
>
<b
v-if="hasUnsavedChanges"
class="text-warning float-right"
>
Unsaved changes
</b>
</div>
</div>
</form>
</template>
<style lang="scss" scoped>
@import '@/assets/scss/colors.scss';
.about-row {
margin-left: 0px;
margin-right: 0px;
}
</style>
<script>
import {
MAX_HEALTH,
MAX_STAT_POINTS,
MAX_LEVEL_HARD_CAP,
MAX_FIELD_HARD_CAP,
} from '@/../../common/script/constants';
import markdownDirective from '@/directives/markdown';
import saveHero from '../mixins/saveHero';
import { mapState } from '@/libs/store';
import { userStateMixin } from '../../../../mixins/userState';
import StatsRow from './stats-row';
function resetData (self) {
self.expand = false;
}
export default {
directives: {
markdown: markdownDirective,
},
components: {
StatsRow,
},
mixins: [
userStateMixin,
saveHero,
],
computed: {
...mapState({ user: 'user.data' }),
statPointsIncorrect () {
if (this.hero.stats.lvl >= 10) {
return (parseInt(this.hero.stats.points, 10)
+ parseInt(this.hero.stats.str, 10)
+ parseInt(this.hero.stats.int, 10)
+ parseInt(this.hero.stats.per, 10)
+ parseInt(this.hero.stats.con, 10)
) !== this.hero.stats.lvl;
}
return false;
},
},
props: {
resetCounter: {
type: Number,
required: true,
},
hero: {
type: Object,
required: true,
},
hasUnsavedChanges: {
type: Boolean,
required: true,
},
},
data () {
return {
expand: false,
maxHealth: MAX_HEALTH,
maxStatPoints: MAX_STAT_POINTS,
maxLevelHardCap: MAX_LEVEL_HARD_CAP,
maxFieldHardCap: MAX_FIELD_HARD_CAP,
};
},
watch: {
resetCounter () {
resetData(this);
},
},
mounted () {
resetData(this);
},
methods: {
submitClicked () {
if (this.statPointsIncorrect) {
return;
}
this.saveHero({
hero: {
_id: this.hero._id,
stats: this.hero.stats,
},
msg: 'Stats',
});
},
resetBuffs () {
this.hero.stats.buffs = {
str: 0,
int: 0,
per: 0,
con: 0,
};
},
deallocateStatPoints () {
this.hero.stats.points = this.hero.stats.lvl;
this.hero.stats.str = 0;
this.hero.stats.int = 0;
this.hero.stats.per = 0;
this.hero.stats.con = 0;
},
},
};
</script>

View File

@@ -0,0 +1,606 @@
<template>
<form
@submit.prevent="saveHero({ hero: {
_id: hero._id,
purchased: hero.purchased
}, msg: 'Subscription Perks' })"
>
<div class="card mt-2">
<div
class="card-header"
@click="expand = !expand"
>
<h3
class="mb-0 mt-0"
:class="{ 'open': expand }"
>
Subscription, Monthly Perks
<b
v-if="hasUnsavedChanges && !expand"
class="text-warning float-right"
>
Unsaved changes
</b>
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
<div
class="form-group row"
>
<label class="col-sm-3 col-form-label">
Payment method:
</label>
<div class="col-sm-9">
<input
v-if="!isRegularPaymentMethod"
v-model="hero.purchased.plan.paymentMethod"
class="form-control"
type="text"
>
<select
v-else
v-model="hero.purchased.plan.paymentMethod"
class="form-control"
type="text"
>
<option value="groupPlan">
Group Plan
</option>
<option value="Stripe">
Stripe
</option>
<option value="Apple">
Apple
</option>
<option value="Google">
Google
</option>
<option value="Amazon Payments">
Amazon
</option>
<option value="PayPal">
PayPal
</option>
<option value="Gift">
Gift
</option>
<option value="">
Clear out
</option>
</select>
</div>
</div>
<div
class="form-group row"
>
<label class="col-sm-3 col-form-label">
Payment schedule:
</label>
<div class="col-sm-9">
<input
v-if="!isRegularPlanId"
v-model="hero.purchased.plan.planId"
class="form-control"
type="text"
>
<select
v-else
v-model="hero.purchased.plan.planId"
class="form-control"
type="text"
>
<option value="basic_earned">
Monthly recurring
</option>
<option value="basic_3mo">
3 Months recurring
</option>
<option value="basic_6mo">
6 Months recurring
</option>
<option value="basic_12mo">
12 Months recurring
</option>
<option value="group_monthly">
Group Plan (legacy)
</option>
<option value="group_plan_auto">
Group Plan (auto)
</option>
<option value="">
Clear out
</option>
</select>
</div>
</div>
<div
class="form-group row"
>
<label class="col-sm-3 col-form-label">
Customer ID:
</label>
<div class="col-sm-9">
<input
v-model="hero.purchased.plan.customerId"
class="form-control"
type="text"
>
</div>
</div>
<div
v-if="hero.purchased.plan.planId === 'group_plan_auto'"
class="form-group row"
>
<label class="col-sm-3 col-form-label">
Group Plan Memberships:
</label>
<div class="col-sm-9 col-form-label">
<loading-spinner
v-if="!groupPlans"
dark-color="true"
/>
<b
v-else-if="groupPlans.length === 0"
class="text-danger col-form-label"
>User is not part of an active group plan!</b>
<div
v-for="group in groupPlans"
v-else
:key="group._id"
class="card mb-2"
>
<div class="card-body">
<h6 class="card-title">
{{ group.name }}
<small class="float-right">{{ group._id }}</small>
</h6>
<p class="card-text">
<strong>Leader: </strong>
<a
v-if="group.leader !== hero._id"
@click="switchUser(group.leader)"
>{{ group.leader }}</a>
<strong
v-else
class="text-success"
>This user</strong>
</p>
<p class="card-text">
<strong>Members: </strong> {{ group.memberCount }}
</p>
</div>
</div>
</div>
</div>
<div
v-if="hero.purchased.plan.dateCreated"
class="form-group row"
>
<label class="col-sm-3 col-form-label">
Creation date:
</label>
<div class="col-sm-9">
<div class="input-group">
<input
v-model="hero.purchased.plan.dateCreated"
class="form-control"
type="text"
>
<div class="input-group-append">
<strong class="input-group-text">
{{ dateFormat(hero.purchased.plan.dateCreated) }}
</strong>
</div>
</div>
</div>
</div>
<div
v-if="hero.purchased.plan.dateCurrentTypeCreated"
class="form-group row"
>
<label class="col-sm-3 col-form-label">
Current sub start date:
</label>
<div class="col-sm-9">
<div class="input-group">
<input
v-model="hero.purchased.plan.dateCurrentTypeCreated"
class="form-control"
type="text"
>
<div class="input-group-append">
<strong class="input-group-text">
{{ dateFormat(hero.purchased.plan.dateCurrentTypeCreated) }}
</strong>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Termination date:
</label>
<div class="col-sm-9">
<div class="input-group">
<input
v-model="hero.purchased.plan.dateTerminated"
class="form-control"
type="text"
>
<div class="input-group-append">
<strong class="input-group-text">
{{ dateFormat(hero.purchased.plan.dateTerminated) }}
</strong>
<a
v-if="!hero.purchased.plan.dateTerminated && hero.purchased.plan.planId"
v-b-modal.sub_termination_modal
class="btn btn-danger"
href="#"
>
Terminate
</a>
</div>
</div>
<small
v-if="!hero.purchased.plan.dateTerminated
&& hero.purchased.plan.planId"
class="text-success"
>
The subscription does not have a termination date and is active.
</small>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Cumulative months:
</label>
<div class="col-sm-9">
<input
v-model="hero.purchased.plan.cumulativeCount"
class="form-control"
type="number"
min="0"
step="1"
>
<small class="text-secondary">
Cumulative subscribed months across subscription periods.
</small>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Extra months:
</label>
<div class="col-sm-9">
<div class="input-group">
<input
v-model="hero.purchased.plan.extraMonths"
class="form-control"
type="number"
min="0"
step="any"
>
<div class="input-group-append">
<a
v-if="hero.purchased.plan.dateTerminated && hero.purchased.plan.extraMonths > 0"
class="btn btn-warning"
@click="applyExtraMonths"
>
Apply Credit
</a>
</div>
</div>
<small class="text-secondary">
Additional credit that is applied if a subscription is cancelled.
</small>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Received hourglass bonus:
</label>
<div class="col-sm-9">
<div class="input-group">
<input
v-model="hero.purchased.plan.hourglassPromoReceived"
class="form-control"
type="text"
>
<div class="input-group-append">
<strong class="input-group-text">
{{ dateFormat(hero.purchased.plan.hourglassPromoReceived) }}
</strong>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Mystic Hourglasses:
</label>
<div class="col-sm-9">
<input
v-model="hero.purchased.plan.consecutive.trinkets"
class="form-control"
type="number"
min="0"
step="1"
>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Gem cap increase:
</label>
<div class="col-sm-9">
<input
v-model="hero.purchased.plan.consecutive.gemCapExtra"
class="form-control"
type="number"
min="0"
max="26"
step="2"
>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Total Gem cap:
</label>
<strong class="col-sm-9 col-form-label">
{{ Number(hero.purchased.plan.consecutive.gemCapExtra) + 24 }}
</strong>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Gems bought this month:
</label>
<div class="col-sm-9">
<input
v-model="hero.purchased.plan.gemsBought"
class="form-control"
type="number"
min="0"
:max="hero.purchased.plan.consecutive.gemCapExtra + 24"
step="1"
>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Mystery Items:
</label>
<div class="col-sm-9 col-form-label">
<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>
</div>
<div
v-if="!isConvertingToGroupPlan && hero.purchased.plan.planId !== 'group_plan_auto'"
class="form-group row"
>
<div class="offset-sm-3 col-sm-9">
<button
type="button"
class="btn btn-secondary btn-sm"
@click="beginGroupPlanConvert"
>
Begin converting to group plan subscription
</button>
</div>
</div>
<div
v-if="isConvertingToGroupPlan"
class="form-group row"
>
<label class="col-sm-3 col-form-label">
Group Plan group ID:
</label>
<div class="col-sm-9">
<input
v-model="groupPlanID"
class="form-control"
type="text"
>
</div>
</div>
</div>
<div
v-if="expand"
class="card-footer d-flex align-items-center justify-content-between"
>
<input
type="submit"
value="Save"
class="btn btn-primary mt-1"
@click="saveClicked"
>
<b
v-if="hasUnsavedChanges"
class="text-warning float-right"
>
Unsaved changes
</b>
</div>
</div>
<b-modal
id="sub_termination_modal"
title="Set Termination Date"
>
<p>
You can set the sub benefit termination date to today or to the last
day of the current billing cycle. Any extra subscription credit will
then be processed and automatically added onto the selected date.
</p>
<template #modal-footer>
<div
class="mt-3 btn btn-secondary"
@click="$bvModal.hide('sub_termination_modal')"
>
Close
</div>
<div
class="mt-3 btn btn-danger"
@click="terminateSubscription()"
>
Set to Today
</div>
<div
class="mt-3 btn btn-danger"
@click="terminateSubscription(todayWithRemainingCycle)"
>
Set to {{ todayWithRemainingCycle.utc().format('MM/DD/YYYY') }}
</div>
</template>
</b-modal>
</form>
</template>
<style lang="scss" scoped>
@import '@/assets/scss/colors.scss';
.input-group-append {
width: auto;
.input-group-text {
border-bottom-right-radius: 2px;
border-top-right-radius: 2px;
font-weight: 600;
font-size: 0.8rem;
color: $gray-200;
}
}
</style>
<script>
import isUUID from 'validator/es/lib/isUUID';
import moment from 'moment';
import { getPlanContext } from '@/../../common/script/cron';
import subscriptionBlocks from '@/../../common/script/content/subscriptionBlocks';
import saveHero from '../mixins/saveHero';
import LoadingSpinner from '@/components/ui/loadingSpinner';
export default {
components: {
LoadingSpinner,
},
mixins: [saveHero],
props: {
hero: {
type: Object,
required: true,
},
hasUnsavedChanges: {
type: Boolean,
required: true,
},
groupPlans: {
type: Array,
default: null,
},
},
data () {
return {
expand: false,
isConvertingToGroupPlan: false,
groupPlanID: '',
subscriptionBlocks,
};
},
computed: {
nextHourglassDate () {
const currentPlanContext = getPlanContext(this.hero, new Date());
if (!currentPlanContext.nextHourglassDate) return 'N/A';
return currentPlanContext.nextHourglassDate.format('MMMM YYYY');
},
isRegularPlanId () {
return this.subscriptionBlocks[this.hero.purchased.plan.planId] !== undefined;
},
isRegularPaymentMethod () {
return [
'groupPlan',
'Group Plan',
'Stripe',
'Apple',
'Google',
'Amazon Payments',
'PayPal',
'Gift',
].includes(this.hero.purchased.plan.paymentMethod);
},
todayWithRemainingCycle () {
const now = moment();
const monthCount = subscriptionBlocks[this.hero.purchased.plan.planId].months;
const terminationDate = moment(this.hero.purchased.plan.dateCurrentTypeCreated || new Date());
while (terminationDate.isBefore(now)) {
terminationDate.add(monthCount, 'months');
}
return terminationDate;
},
},
methods: {
dateFormat (date) {
if (!date) {
return '--';
}
return moment(date).format('YYYY/MM/DD');
},
terminateSubscription (terminationDate) {
if (terminationDate) {
this.hero.purchased.plan.dateTerminated = terminationDate.utc().format();
} else {
this.hero.purchased.plan.dateTerminated = moment(new Date()).utc().format();
}
this.applyExtraMonths();
this.saveHero({ hero: this.hero, msg: 'Subscription Termination', reloadData: true });
},
applyExtraMonths () {
if (this.hero.purchased.plan.extraMonths > 0 || this.hero.purchased.plan.extraMonths !== '0') {
const date = moment(this.hero.purchased.plan.dateTerminated || new Date());
const extraMonths = Math.max(this.hero.purchased.plan.extraMonths, 0);
const extraDays = Math.ceil(30.5 * extraMonths);
this.hero.purchased.plan.dateTerminated = date.add(extraDays, 'days').utc().format();
this.hero.purchased.plan.extraMonths = 0;
}
},
beginGroupPlanConvert () {
this.isConvertingToGroupPlan = true;
this.hero.purchased.plan.owner = '';
},
saveClicked (e) {
e.preventDefault();
if (this.isConvertingToGroupPlan) {
if (!isUUID(this.groupPlanID)) {
alert('Invalid group ID');
return;
}
this.hero.purchased.plan.convertToGroupPlan = this.groupPlanID;
this.saveHero({ hero: this.hero, msg: 'Group Plan Subscription', reloadData: true });
} else {
this.saveHero({ hero: this.hero, msg: 'Subscription Perks', reloadData: true });
}
},
switchUser (id) {
if (window.confirm('Switch to this user?')) {
this.$emit('changeUserIdentifier', id);
}
},
},
};
</script>

View File

@@ -22,8 +22,8 @@
</template>
<script>
import PurchaseHistoryTable from '../../ui/purchaseHistoryTable.vue';
import { userStateMixin } from '../../../mixins/userState';
import PurchaseHistoryTable from '../../../ui/purchaseHistoryTable.vue';
import { userStateMixin } from '../../../../mixins/userState';
export default {
components: {

View File

@@ -0,0 +1,263 @@
<template>
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{'open': expand}"
@click="toggleHistoryOpen"
>
User History
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
<div>
<div class="clearfix">
<div class="mb-4 float-left">
<button
class="page-header btn-flat tab-button textCondensed"
:class="{'active': selectedTab === 'armoire'}"
@click="selectTab('armoire')"
>
Armoire
</button>
<button
class="page-header btn-flat tab-button textCondensed"
:class="{'active': selectedTab === 'questInvites'}"
@click="selectTab('questInvites')"
>
Quest Invitations
</button>
<button
class="page-header btn-flat tab-button textCondensed"
:class="{'active': selectedTab === 'cron'}"
@click="selectTab('cron')"
>
Cron
</button>
</div>
</div>
<div class="row">
<div
v-if="selectedTab === 'armoire'"
class="col-12"
>
<table class="table">
<tr>
<th
v-once
>
{{ $t('timestamp') }}
</th>
<th v-once>
Client
</th>
<th
v-once
>
Received
</th>
</tr>
<tr
v-for="entry in armoire"
:key="entry.timestamp"
>
<td>
<span
v-b-tooltip.hover="entry.timestamp"
>{{ entry.timestamp | timeAgo }}</span>
</td>
<td>{{ entry.client }}</td>
<td>{{ entry.reward }}</td>
</tr>
</table>
</div>
<div
v-if="selectedTab === 'questInvites'"
class="col-12"
>
<table class="table">
<tr>
<th
v-once
>
{{ $t('timestamp') }}
</th>
<th v-once>
Client
</th>
<th v-once>
Quest Key
</th>
<th v-once>
Response
</th>
</tr>
<tr
v-for="entry in questInviteResponses"
:key="entry.timestamp"
>
<td>
<span
v-b-tooltip.hover="entry.timestamp"
>{{ entry.timestamp | timeAgo }}</span>
</td>
<td>{{ entry.client }}</td>
<td>{{ entry.quest }}</td>
<td>{{ questInviteResponseText(entry.response) }}</td>
</tr>
</table>
</div>
<div
v-if="selectedTab === 'cron'"
class="col-12"
>
<table class="table">
<tr>
<th
v-once
>
{{ $t('timestamp') }}
</th>
<th v-once>
Client
</th>
<th v-once>
Checkin Count
</th>
</tr>
<tr
v-for="entry in cron"
:key="entry.timestamp"
>
<td>
<span
v-b-tooltip.hover="entry.timestamp"
>{{ entry.timestamp | timeAgo }}</span>
</td>
<td>{{ entry.client }}</td>
<td>{{ entry.checkinCount }}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
@import '@/assets/scss/colors.scss';
.page-header.btn-flat {
background: transparent;
}
.tab-button {
height: 2rem;
font-size: 24px;
font-weight: bold;
font-stretch: condensed;
line-height: 1.33;
letter-spacing: normal;
color: $gray-10;
margin-right: 1.125rem;
padding-left: 0;
padding-right: 0;
padding-bottom: 2.5rem;
&.active, &:hover {
color: $purple-300;
box-shadow: 0px -0.25rem 0px $purple-300 inset;
outline: none;
}
}
</style>
<script>
import moment from 'moment';
import { userStateMixin } from '../../../../mixins/userState';
export default {
filters: {
timeAgo (value) {
return moment(value).fromNow();
},
},
mixins: [userStateMixin],
props: {
hero: {
type: Object,
required: true,
},
resetCounter: {
type: Number,
required: true,
},
},
data () {
return {
expand: false,
selectedTab: 'armoire',
armoire: [],
questInviteResponses: [],
cron: [],
};
},
watch: {
resetCounter () {
if (this.expand) {
this.retrieveUserHistory();
}
},
},
methods: {
selectTab (type) {
this.selectedTab = type;
},
async toggleHistoryOpen () {
this.expand = !this.expand;
if (this.expand) {
this.retrieveUserHistory();
}
},
async retrieveUserHistory () {
const history = await this.$store.dispatch('adminPanel:getUserHistory', { userIdentifier: this.hero._id });
this.armoire = history.armoire;
this.questInviteResponses = history.questInviteResponses;
this.cron = history.cron;
},
questInviteResponseText (response) {
if (response === 'accept') {
return 'Accepted';
}
if (response === 'reject') {
return 'Rejected';
}
if (response === 'leave') {
return 'Left active quest';
}
if (response === 'invite') {
return 'Accepted as owner';
}
if (response === 'abort') {
return 'Aborted by owner';
}
if (response === 'abortByLeader') {
return 'Aborted by party leader';
}
if (response === 'cancel') {
return 'Cancelled before start';
}
if (response === 'cancelByLeader') {
return 'Cancelled before start by party leader';
}
return response;
},
},
};
</script>

View File

@@ -1,5 +1,10 @@
<template>
<form @submit.prevent="saveHero({hero, msg: 'Users Profile'})">
<form
@submit.prevent="saveHero({hero: {
_id: hero._id,
profile: hero.profile
}, msg: 'Users Profile'})"
>
<div class="card mt-2">
<div class="card-header">
<h3
@@ -8,6 +13,12 @@
@click="expand = !expand"
>
User Profile
<b
v-if="hasUnsavedChanges && !expand"
class="text-warning float-right"
>
Unsaved changes
</b>
</h3>
</div>
<div
@@ -51,13 +62,19 @@
</div>
<div
v-if="expand"
class="card-footer"
class="card-footer d-flex align-items-center justify-content-between"
>
<input
type="submit"
value="Save"
class="btn btn-primary mt-1"
>
<b
v-if="hasUnsavedChanges"
class="text-warning float-right"
>
Unsaved changes
</b>
</div>
</div>
</form>
@@ -75,7 +92,7 @@ import markdownDirective from '@/directives/markdown';
import saveHero from '../mixins/saveHero';
import { mapState } from '@/libs/store';
import { userStateMixin } from '../../../mixins/userState';
import { userStateMixin } from '../../../../mixins/userState';
function resetData (self) {
self.expand = false;
@@ -101,6 +118,10 @@ export default {
type: Object,
required: true,
},
hasUnsavedChanges: {
type: Boolean,
required: true,
},
},
data () {
return {

View File

@@ -0,0 +1,133 @@
<template>
<div style="display: contents">
<td>
<select
v-model="blocker.type"
class="form-control"
@change="onTypeChanged"
>
<option value="ipaddress">
IP-Address
</option>
<option value="client">
Client Identifier
</option>
<option value="email">
E-Mail
</option>
</select>
</td>
<td>
<select
v-model="blocker.area"
class="form-control"
>
<option value="full">
Full
</option>
</select>
</td>
<td>
<input
v-model="blocker.value"
class="form-control"
autocorrect="off"
autocapitalize="off"
:class="{ 'is-invalid input-invalid': !isValid }"
@input="validateValue"
>
</td>
<td>
<input
v-model="blocker.reason"
class="form-control"
>
</td>
<td
colspan="3"
class="text-right"
>
<button
class="btn btn-primary mr-2"
:disabled="!isValid"
:class="{ disabled: !isValid }"
@click="$emit('save', blocker)"
>
<span>Save</span>
</button>
<button
class="btn btn-danger"
@click="$emit('cancel')"
>
<span>Cancel</span>
</button>
</td>
</div>
</template>
<style lang="scss" scoped>
.btn-primary.disabled {
background: #4F2A93;
color: white;
cursor: not-allowed;
opacity: 0.5;
}
</style>
<script>
import isIP from 'validator/es/lib/isIP';
export default {
name: 'BlockerForm',
props: {
isNew: {
type: Boolean,
default: false,
},
blocker: {
type: Object,
default: () => ({
type: '',
area: '',
value: '',
reason: '',
}),
},
},
data () {
return {
isValid: false,
};
},
mounted () {
this.validateValue();
},
methods: {
onTypeChanged () {
if (this.blocker.type === 'email') {
this.blocker.area = 'full';
}
this.validateValue();
},
validateValue () {
if (this.blocker.type === 'ipaddress') {
this.validateValueAsIpAddress();
} else if (this.blocker.type === 'client') {
this.validateValueAsClient();
} else if (this.blocker.type === 'email') {
this.validateValueAsEmail();
}
},
validateValueAsEmail () {
const emailRegex = /^([a-zA-Z0-9._%+-]*)@(?:[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})?$/;
this.isValid = emailRegex.test(this.blocker.value) && this.blocker.value.length > 3;
},
validateValueAsIpAddress () {
this.isValid = isIP(this.blocker.value);
},
validateValueAsClient () {
this.isValid = this.blocker.value.length > 0;
},
},
};
</script>

View File

@@ -0,0 +1,238 @@
<template>
<div class="row standard-page col-12 d-flex justify-content-center">
<div class="blocker-content">
<h1>
Blockers
<button
class="btn btn-primary float-right"
@click="showCreateForm = true"
>
Create
</button>
</h1>
<table class="table table-bordered">
<thead>
<tr>
<th>
Type <span
id="type_tooltip"
class="info-icon"
>?</span>
<b-tooltip
target="type_tooltip"
>
<b>IP-Address</b> - Block access for a specific IP-Address
<br>
<br>
<b>Client</b> - Block access for a client based on the "x-client" header.
<br>
<br>
<b>E-Mail</b> - Blocks e-mails from being used for signup.
</b-tooltip>
</th>
<th>
Area <span
id="area_tooltip"
class="info-icon"
>?</span>
<b-tooltip
target="area_tooltip"
>
<b>Full</b> - Block access to the entire site.
<br>
<br>
<b>Payments</b> - Block access to any payment related functionality.
</b-tooltip>
</th>
<th>Value</th>
<th>Reason</th>
<th>Source</th>
<th>Created at</th>
<th class="btncol"></th>
</tr>
</thead>
<tbody>
<tr v-if="showCreateForm">
<BlockerForm
:is-new="true"
:blocker="newBlocker"
@save="createBlocker"
@cancel="showCreateForm = false"
/>
</tr>
<tr
v-for="blocker in blockers"
:key="blocker._id"
>
<BlockerForm
v-if="blocker._id === editedBlockerId"
:blocker="blocker"
@save="saveBlocker(blocker)"
@cancel="editedBlockerId = null"
/>
<template v-else>
<td>{{ getTypeName(blocker.type) }}</td>
<td>{{ getAreaName(blocker.area) }}</td>
<td>{{ blocker.value }}</td>
<td>{{ blocker.reason || "--" }}</td>
<td>{{ blocker.blockSource }}</td>
<td>{{ blocker.createdAt }}</td>
<td>
<button
class="btn btn-primary mr-2"
@click="editBlocker(blocker._id)"
>
<span
v-once
class="svg-icon icon-16"
v-html="icons.editIcon"
></span>
</button>
<button
class="btn btn-danger"
@click="deleteBlocker(blocker._id)"
>
<span
v-once
class="svg-icon icon-16"
v-html="icons.deleteIcon"
></span>
</button>
</td>
</template>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<style lang="scss" scoped>
@import '@/assets/scss/colors.scss';
.blocker-content {
flex: 0 0 100%;
max-width: 1200px;
}
.btn {
padding: 0.4rem 0.75rem;
}
.btncol {
width: 123px;
}
td {
font-size: 1rem;
}
.info-icon {
font-size: 0.8rem;
color: $purple-400;
cursor: pointer;
margin-left: 0.5rem;
background-color: $gray-500;
padding: 0.1rem 0.3rem;
border-radius: 0.2rem;
}
.info-icon:hover {
background-color: $purple-400;
color: white;
}
</style>
<script>
import { mapState } from '@/libs/store';
import editIcon from '@/assets/svg/edit.svg?raw';
import deleteIcon from '@/assets/svg/delete.svg?raw';
import BlockerForm from './blocker_form.vue';
export default {
components: {
BlockerForm,
},
data () {
return {
showCreateForm: false,
newBlocker: {
type: '',
area: 'full',
value: '',
reason: '',
},
blockers: [],
editedBlockerId: null,
icons: Object.freeze({
editIcon,
deleteIcon,
}),
};
},
computed: {
...mapState({ user: 'user.data' }),
},
mounted () {
this.$store.dispatch('common:setTitle', {
section: this.$t('siteBlockers'),
});
this.loadBlockers();
},
methods: {
async loadBlockers () {
this.blockers = await this.$store.dispatch('blockers:getBlockers');
},
editBlocker (id) {
this.editedBlockerId = id;
},
async saveBlocker (blocker) {
await this.$store.dispatch('blockers:updateBlocker', { blocker });
this.editedBlockerId = null;
this.loadBlockers();
},
async deleteBlocker (blockerId) {
if (!window.confirm('Are you sure you want to delete this blocker?')) {
return;
}
await this.$store.dispatch('blockers:deleteBlocker', { blockerId });
this.loadBlockers();
},
async createBlocker (blocker) {
await this.$store.dispatch('blockers:createBlocker', { blocker });
this.showCreateForm = false;
this.newBlocker = {
type: '',
area: 'full',
value: '',
reason: '',
};
this.loadBlockers();
},
getTypeName (type) {
switch (type) {
case 'ipaddress':
return 'IP-Address';
case 'email':
return 'E-Mail';
case 'client':
return 'Client Identifier';
default:
return type;
}
},
getAreaName (area) {
switch (area) {
case 'full':
return 'Full';
case 'payments':
return 'Payments';
default:
return area;
}
},
},
};
</script>

View File

@@ -0,0 +1,40 @@
<template>
<div class="row">
<secondary-menu class="col-12">
<router-link
v-if="hasPermission(user, 'userSupport')"
class="nav-link"
:to="{name: 'adminPanel'}"
>
{{ $t('adminPanel') }}
</router-link>
<router-link
v-if="hasPermission(user, 'accessControl')"
class="nav-link"
:to="{name: 'blockers'}"
>
{{ $t('siteBlockers') }}
</router-link>
</secondary-menu><div class="col-12">
<router-view />
</div>
</div>
</template>
<script>
import { mapState } from '@/libs/store';
import SecondaryMenu from '@/components/secondaryMenu';
import { userStateMixin } from '../../mixins/userState';
export default {
components: {
SecondaryMenu,
},
mixins: [
userStateMixin,
],
computed: {
...mapState({ user: 'user.data' }),
},
};
</script>

View File

@@ -37,9 +37,9 @@
<h3>{{ $t('footerCompany') }}</h3>
<ul>
<li>
<router-link to="/static/contact">
<a href="mailto:admin@habitica.com">
{{ $t('contactUs') }}
</router-link>
</a>
</li>
<li>
<router-link to="/static/press-kit">
@@ -55,9 +55,9 @@
</li>
<li>
<a
href="https://habitica.fandom.com/wiki/Whats_New"
target="_blank"
>{{ $t('oldNews') }}
@click="showBailey()"
>
{{ $t('oldNews') }}
</a>
</li>
</ul>
@@ -80,7 +80,7 @@
</li>
<li>
<a
href="https://habitica.fandom.com/wiki/Contributing_to_Habitica"
href="https://github.com/HabitRPG/habitica/wiki/Contributing-to-Habitica"
target="_blank"
>{{ $t('companyContribute') }}
</a>
@@ -158,13 +158,6 @@
>{{ $t('guidanceForBlacksmiths') }}
</a>
</li>
<li>
<a
href="https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations"
target="_blank"
>{{ $t('communityExtensions') }}
</a>
</li>
</ul>
</div>
@@ -205,12 +198,12 @@
</a>
<a
class="social-circle"
href="https://twitter.com/habitica/"
href="https://bsky.app/profile/habitica.com"
target="_blank"
>
<div
class="social-icon svg-icon twitter"
v-html="icons.twitter"
class="social-icon svg-icon bluesky"
v-html="icons.bluesky"
></div>
</a>
<a
@@ -518,7 +511,7 @@ footer {
background-color: $gray-500;
color: $gray-50;
padding: 32px 142px 40px;
a {
a, a:not([href]) {
color: $gray-50;
}
a:hover {
@@ -807,7 +800,7 @@ h3 {
}
}
.twitter svg {
.bluesky svg {
background-color: #e1e0e3;
fill: #878190;
height: 24px;
@@ -846,7 +839,7 @@ import Vue from 'vue';
// images
import melior from '@/assets/svg/melior.svg?raw';
import twitter from '@/assets/svg/twitter.svg?raw';
import bluesky from '@/assets/svg/bluesky.svg?raw';
import facebook from '@/assets/svg/facebook.svg?raw';
import instagram from '@/assets/svg/instagram.svg?raw';
import tumblr from '@/assets/svg/tumblr.svg?raw';
@@ -858,13 +851,15 @@ import buyGemsModal from './payments/buyGemsModal.vue';
import reportBug from '@/mixins/reportBug.js';
import { worldStateMixin } from '@/mixins/worldState';
const DEBUG_ENABLED = import.meta.env.DEBUG_ENABLED === 'true'; // eslint-disable-line no-process-env
const TIME_TRAVEL_ENABLED = import.meta.env.TIME_TRAVEL_ENABLED === 'true'; // eslint-disable-line no-process-env
const DEBUG_ENABLED = import.meta.env.DEBUG_ENABLED === 'true';
const TIME_TRAVEL_ENABLED = import.meta.env.TIME_TRAVEL_ENABLED === 'true';
let sinon;
/* if (TIME_TRAVEL_ENABLED) {
// eslint-disable-next-line global-require
sinon = await import('sinon');
} */
if (import.meta.env.TIME_TRAVEL_ENABLED === 'true') {
(async () => {
sinon = await import('sinon');
})();
}
export default {
components: {
@@ -878,7 +873,7 @@ export default {
return {
icons: Object.freeze({
melior,
twitter,
bluesky,
facebook,
instagram,
tumblr,
@@ -951,24 +946,28 @@ export default {
},
async jumpTime (amount) {
const response = await axios.post('/api/v4/debug/jump-time', { offsetDays: amount });
if (amount > 0) {
Vue.config.clock.jump(amount * 24 * 60 * 60 * 1000);
} else {
Vue.config.clock.setSystemTime(moment().add(amount, 'days').toDate());
}
this.lastTimeJump = response.data.data.time;
this.triggerGetWorldState(true);
setTimeout(() => {
if (amount > 0) {
Vue.config.clock.jump(amount * 24 * 60 * 60 * 1000);
} else {
Vue.config.clock.setSystemTime(moment().add(amount, 'days').toDate());
}
this.lastTimeJump = response.data.data.time;
this.triggerGetWorldState(true);
}, 1000);
},
async resetTime () {
const response = await axios.post('/api/v4/debug/jump-time', { reset: true });
const time = new Date(response.data.data.time);
Vue.config.clock.restore();
Vue.config.clock = sinon.useFakeTimers({
now: time,
shouldAdvanceTime: true,
});
this.lastTimeJump = response.data.data.time;
this.triggerGetWorldState(true);
setTimeout(() => {
Vue.config.clock.restore();
Vue.config.clock = sinon.useFakeTimers({
now: time,
shouldAdvanceTime: true,
});
this.lastTimeJump = response.data.data.time;
this.triggerGetWorldState(true);
}, 1000);
},
addExp () {
// @TODO: Name these variables better
@@ -996,7 +995,6 @@ export default {
async bossRage () {
await axios.post('/api/v4/debug/boss-rage');
},
async makeAdmin () {
await axios.post('/api/v4/debug/make-admin');
// @TODO: Notification.text('You are now an admin!
@@ -1006,6 +1004,9 @@ export default {
donate () {
this.$root.$emit('bv::show::modal', 'buy-gems', { alreadyTracked: true });
},
showBailey () {
this.$root.$emit('bv::show::modal', 'new-stuff');
},
},
};
</script>

View File

@@ -220,7 +220,6 @@
v-if="forgotPassword"
id="forgot-form"
@submit.prevent="handleSubmit"
@keyup.enter="handleSubmit"
>
<div class="text-center">
<div>
@@ -268,12 +267,11 @@
v-if="resetPasswordSetNewOne"
id="reset-password-set-new-one-form"
@submit.prevent="handleSubmit"
@keyup.enter="handleSubmit"
>
<div class="text-center">
<div>
<div
class="svg-icon habitica-logo color"
class="svg-icon habitica-logo"
v-html="icons.habiticaIcon"
></div>
</div>
@@ -726,7 +724,11 @@ export default {
},
mounted () {
this.forgotPassword = this.$route.path.startsWith('/forgot-password');
if (this.forgotPassword) {
if (this.$route.query.email) {
this.username = this.$route.query.email;
}
}
hello.init({
google: import.meta.env.GOOGLE_CLIENT_ID, // eslint-disable-line
});

View File

@@ -1,100 +1,98 @@
<template>
<div class="avatar-wrapper">
<div
v-if="member.preferences"
class="avatar"
:style="{width, height, paddingTop}"
:class="topLevelClassList"
@click.prevent="castEnd()"
>
<div
v-if="member.preferences"
class="avatar"
:style="{width, height, paddingTop}"
:class="topLevelClassList"
@click.prevent="castEnd()"
class="character-sprites"
:style="{margin: spritesMargin}"
>
<div
class="character-sprites"
:style="{margin: spritesMargin}"
>
<template v-if="!avatarOnly">
<!-- Mount Body-->
<span
v-if="member.items.currentMount"
:class="'Mount_Body_' + member.items.currentMount"
></span>
</template>
<!-- Buffs that cause visual changes to avatar: Snowman, Ghost, Flower, etc-->
<template v-for="(klass, item) in visualBuffs">
<span
v-if="member.stats.buffs[item] && showVisualBuffs"
:key="item"
:class="klass"
></span>
</template>
<!-- Show flower ALL THE TIME!!!-->
<!-- See https://github.com/HabitRPG/habitica/issues/7133-->
<span :class="'hair_flower_' + member.preferences.hair.flower"></span>
<!-- Show avatar only if not currently affected by visual buff-->
<template v-if="showAvatar()">
<span :class="['chair_' + member.preferences.chair, specialMountClass]"></span>
<span :class="[getGearClass('back'), specialMountClass]"></span>
<span :class="[skinClass, specialMountClass]"></span>
<!-- eslint-disable max-len-->
<span
:class="[shirtClass, specialMountClass]"
></span>
<!-- eslint-enable max-len-->
<span :class="['head_0', specialMountClass]"></span>
<!-- eslint-disable max-len-->
<span :class="[member.preferences.size + '_' + getGearClass('armor'), specialMountClass]"></span>
<!-- eslint-enable max-len-->
<span :class="[getGearClass('back_collar'), specialMountClass]"></span>
<template
v-for="type in ['bangs', 'base', 'mustache', 'beard']"
>
<span
:key="type"
:class="[hairClass(type), specialMountClass]"
></span>
</template>
<span :class="[getGearClass('body'), specialMountClass]"></span>
<span :class="[getGearClass('eyewear'), specialMountClass]"></span>
<span :class="[getGearClass('head'), specialMountClass]"></span>
<span :class="[getGearClass('headAccessory'), specialMountClass]"></span>
<span
:class="[
'hair_flower_' + member.preferences.hair.flower, specialMountClass
]"
></span>
<span
v-if="!hideGear('shield')"
:class="[getGearClass('shield'), specialMountClass]"
></span>
<span
v-if="!hideGear('weapon')"
:class="[getGearClass('weapon'), specialMountClass]"
class="weapon"
></span>
</template>
<!-- Resting-->
<template v-if="!avatarOnly">
<!-- Mount Body-->
<span
v-if="member.preferences.sleep"
class="zzz"
v-if="member.items.currentMount"
:class="'Mount_Body_' + member.items.currentMount"
></span>
<template v-if="!avatarOnly">
<!-- Mount Head-->
</template>
<!-- Buffs that cause visual changes to avatar: Snowman, Ghost, Flower, etc-->
<template v-for="(klass, item) in visualBuffs">
<span
v-if="member.stats.buffs[item] && showVisualBuffs"
:key="item"
:class="klass"
></span>
</template>
<!-- Show flower ALL THE TIME!!!-->
<!-- See https://github.com/HabitRPG/habitica/issues/7133-->
<span :class="'hair_flower_' + member.preferences.hair.flower"></span>
<!-- Show avatar only if not currently affected by visual buff-->
<template v-if="showAvatar()">
<span :class="['chair_' + member.preferences.chair, specialMountClass]"></span>
<span :class="[getGearClass('back'), specialMountClass]"></span>
<span :class="[skinClass, specialMountClass]"></span>
<!-- eslint-disable max-len-->
<span
:class="[shirtClass, specialMountClass]"
></span>
<!-- eslint-enable max-len-->
<span :class="['head_0', specialMountClass]"></span>
<!-- eslint-disable max-len-->
<span :class="[member.preferences.size + '_' + getGearClass('armor'), specialMountClass]"></span>
<!-- eslint-enable max-len-->
<span :class="[getGearClass('back_collar'), specialMountClass]"></span>
<template
v-for="type in ['bangs', 'base', 'mustache', 'beard']"
>
<span
v-if="member.items.currentMount"
:class="'Mount_Head_' + member.items.currentMount"
></span>
<!-- Pet-->
<span
class="current-pet"
:class="petClass"
:key="type"
:class="[hairClass(type), specialMountClass]"
></span>
</template>
</div>
<class-badge
v-if="hasClass && !hideClassBadge"
class="under-avatar"
:member-class="member.stats.class"
/>
<span :class="[getGearClass('body'), specialMountClass]"></span>
<span :class="[getGearClass('eyewear'), specialMountClass]"></span>
<span :class="[getGearClass('head'), specialMountClass]"></span>
<span :class="[getGearClass('headAccessory'), specialMountClass]"></span>
<span
:class="[
'hair_flower_' + member.preferences.hair.flower, specialMountClass
]"
></span>
<span
v-if="!hideGear('shield')"
:class="[getGearClass('shield'), specialMountClass]"
></span>
<span
v-if="!hideGear('weapon')"
:class="[getGearClass('weapon'), specialMountClass]"
class="weapon"
></span>
</template>
<!-- Resting-->
<span
v-if="member.preferences.sleep"
class="zzz"
></span>
<template v-if="!avatarOnly">
<!-- Mount Head-->
<span
v-if="member.items.currentMount"
:class="'Mount_Head_' + member.items.currentMount"
></span>
<!-- Pet-->
<span
class="current-pet"
:class="petClass"
></span>
</template>
</div>
<class-badge
v-if="hasClass && !hideClassBadge"
class="under-avatar"
:member-class="member.stats.class"
/>
</div>
</template>
@@ -139,11 +137,6 @@
filter: invert(100%);
}
.weapon {
// the only one that is relative so that it fits into the parent div
position: relative !important;
}
.debug {
border: 1px solid red;
@@ -162,7 +155,6 @@
</style>
<script>
import some from 'lodash/some';
import moment from 'moment';
import { mapState } from '@/libs/store';
import foolPet from '../mixins/foolPet';
@@ -210,11 +202,11 @@ export default {
},
width: {
type: String,
default: '140px',
default: '141px',
},
height: {
type: String,
default: undefined,
default: '147px',
},
centerAvatar: {
type: Boolean,
@@ -329,11 +321,10 @@ export default {
return null;
},
petClass () {
if (some(
this.currentEventList,
event => moment().isBetween(event.start, event.end) && event.aprilFools && event.aprilFools === 'Fungi',
)) {
return this.foolPet(this.member.items.currentPet);
const foolEvent = this.currentEventList?.find(event => moment()
.isBetween(event.start, event.end) && event.aprilFools);
if (foolEvent) {
return this.foolPet(this.member.items.currentPet, foolEvent.aprilFools);
}
if (this.member?.items.currentPet) return `Pet-${this.member.items.currentPet}`;
return '';

View File

@@ -30,6 +30,7 @@
<script>
import markdownDirective from '@/directives/markdown';
import { LOCALSTORAGE_AUTH_KEY } from '@/libs/auth';
const COMMUNITY_MANAGER_EMAIL = import.meta.env.EMAILS_COMMUNITY_MANAGER_EMAIL; // eslint-disable-line
@@ -39,7 +40,7 @@ export default {
},
computed: {
bannedMessage () {
const AUTH_SETTINGS = localStorage.getItem('habit-mobile-settings');
const AUTH_SETTINGS = localStorage.getItem(LOCALSTORAGE_AUTH_KEY);
const parseSettings = JSON.parse(AUTH_SETTINGS);
const userId = parseSettings ? parseSettings.auth.apiId : '';

View File

@@ -31,6 +31,7 @@
<avatar
v-if="user._id !== msg.uuid && msg.uuid !== 'system'"
class="avatar-left"
:height="null"
:class="{ invisible: avatarUnavailable(msg) }"
:member="msg.userStyles || cachedProfileData[msg.uuid] || {}"
:avatar-only="true"
@@ -50,6 +51,7 @@
v-if="user._id === msg.uuid"
:class="{ invisible: avatarUnavailable(msg) }"
:member="msg.userStyles || cachedProfileData[msg.uuid] || {}"
:height="null"
:avatar-only="true"
:hide-class-badge="true"
:override-top-padding="'14px'"
@@ -96,11 +98,6 @@
}
}
.avatar-left {
margin-left: -1.5rem;
margin-right: 2rem;
}
.hr {
width: 100%;
height: 20px;

View File

@@ -45,6 +45,9 @@
<div class="col-5">
<select-list
:items="sortOptions"
:hide-icon="false"
:inline-dropdown="false"
:direct-select="true"
:value="optionEntryBySelectedValue"
key-prop="value"
@select="changeSortOption($event)"
@@ -61,6 +64,9 @@
<select-list
:items="sortDirections"
:value="directionEntryBySelectedValue"
:hide-icon="false"
:inline-dropdown="false"
:direct-select="true"
key-prop="value"
@select="changeSortDirection($event)"
>

View File

@@ -225,10 +225,10 @@
</a>
</div>
<div class="quest-icon">
<div
<Sprite
class="quest"
:class="`inventory_quest_scroll_${questData.key}`"
></div>
:image-name="`inventory_quest_scroll_${questData.key}`"
/>
</div>
</div>
<div

View File

@@ -286,7 +286,7 @@
:to="{ name: 'adminPanelUser',
params: { userIdentifier: hero._id } }"
>
admin panel
{{ $t("adminPanel") }}
</router-link>
</span>
</td>

View File

@@ -295,14 +295,6 @@
{{ $t('help') }}
</router-link>
<div class="topbar-dropdown">
<router-link
v-if="user.permissions.fullAccess ||
user.permissions.userSupport || user.permissions.newsPoster"
class="topbar-dropdown-item dropdown-item"
:to="{name: 'adminPanel'}"
>
Admin Panel
</router-link>
<router-link
class="topbar-dropdown-item dropdown-item"
:to="{name: 'faq'}"
@@ -336,6 +328,61 @@
>{{ $t('requestFeature') }}</a>
</div>
</li>
<li
v-if="hasElevatedPrivileges"
class="topbar-item droppable"
:class="{
'active': $route.path.startsWith('/admin')}"
>
<div
class="chevron rotate"
@click="dropdownMobile($event)"
>
<div
v-once
class="chevron-icon-down"
v-html="icons.chevronDown"
></div>
</div>
<router-link
v-if="hasPermission(user, 'userSupport')"
class="nav-link"
:to="{name: 'adminPanel'}"
>
{{ $t('admin') }}
</router-link>
<a
v-else
href="#"
class="nav-link"
>
{{ $t('admin') }}
</a>
<div class="topbar-dropdown">
<router-link
v-if="hasPermission(user, 'userSupport')"
class="topbar-dropdown-item dropdown-item"
:to="{name: 'adminPanel'}"
>
{{ $t("adminPanel") }}
</router-link>
<router-link
v-if="hasPermission(user, 'accessControl')"
class="topbar-dropdown-item dropdown-item"
:to="{name: 'blockers'}"
>
{{ $t("siteBlockers") }}
</router-link>
<a
v-if="hasPermission(user, 'news')"
class="topbar-dropdown-item dropdown-item"
target="_blank"
href="https://panel.habitica.com"
>
{{ $t('newsroom') }}
</a>
</div>
</li>
</b-navbar-nav>
<div class="currency-tray form-inline">
<div
@@ -757,6 +804,7 @@ import selectUserModal from '@/components/payments/selectUserModal';
import sync from '@/mixins/sync';
import userDropdown from './userDropdown';
import reportBug from '@/mixins/reportBug.js';
import { userStateMixin } from '../../mixins/userState';
export default {
components: {
@@ -769,7 +817,7 @@ export default {
selectUserModal,
userDropdown,
},
mixins: [sync, reportBug],
mixins: [sync, reportBug, userStateMixin],
data () {
return {
isUserDropdownOpen: false,
@@ -802,6 +850,12 @@ export default {
params: { groupId: this.groupPlans[0]._id },
};
},
hasElevatedPrivileges () {
return this.user.permissions.fullAccess
|| this.user.permissions.userSupport
|| this.user.permissions.accessControl
|| this.user.permissions.news;
},
},
async mounted () {
await this.getUserGroupPlans();

View File

@@ -12,20 +12,22 @@
<strong> {{ notification.data.title }} </strong>
<span> {{ notification.data.text }} </span>
</div>
<div
<Sprite
slot="icon"
class="mt-3"
:class="notification.data.icon"
></div>
:image-name="notification.data.icon"
/>
</base-notification>
</template>
<script>
import BaseNotification from './base';
import Sprite from '@/components/ui/sprite.vue';
export default {
components: {
BaseNotification,
Sprite,
},
props: {
notification: {

View File

@@ -10,20 +10,22 @@
slot="content"
v-html="$t('newSubscriberItem')"
></div>
<div
<Sprite
slot="icon"
:class="mysteryClass"
></div>
:image-name="mysteryClass"
/>
</base-notification>
</template>
<script>
import moment from 'moment';
import BaseNotification from './base';
import Sprite from '@/components/ui/sprite.vue';
export default {
components: {
BaseNotification,
Sprite,
},
props: ['notification', 'canRemove'],
computed: {

View File

@@ -120,6 +120,7 @@
<div
slot="drawer-slider"
class="equipment items items-one-line"
:class="getContainerClass()"
>
<item
v-for="(label, group) in gearTypesToStrings"
@@ -238,6 +239,86 @@
background: $gray-10 !important;
}
@media (max-width: 768px) {
.equipment.items.items-one-line {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 12px;
padding: 0 8px;
.item-wrapper {
flex: 0 0 auto;
margin-right: 0;
margin-bottom: 8px;
}
&.equipment-scale-default .item-wrapper {
.item {
width: 94px;
height: 92px;
}
.item-label {
width: 94px;
font-size: 12px;
}
}
&.equipment-scale-small .item-wrapper {
.item {
width: 70px;
height: 70px;
}
.item-label {
width: 70px;
font-size: 10px;
}
}
.item-wrapper:nth-child(4n+1) {
clear: left;
}
}
}
@media (min-width: 769px) and (max-width: 1024px) {
.equipment.items.items-one-line {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 12px;
padding: 0 12px;
.item-wrapper {
flex: 0 0 auto;
margin-right: 0;
margin-bottom: 8px;
}
&.equipment-scale-default .item-wrapper {
.item {
width: 84px;
height: 82px;
}
.item-label {
width: 84px;
font-size: 11px;
}
}
&.equipment-scale-small .item-wrapper {
.item {
width: 65px;
height: 65px;
}
.item-label {
width: 65px;
font-size: 10px;
}
}
}
}
</style>
<style lang="scss" scoped>
@@ -351,6 +432,7 @@ export default {
searchText: null,
searchTextThrottled: null,
costumeMode: false,
windowWidth: window.innerWidth,
groupByItems: [
'type', 'class',
],
@@ -410,7 +492,8 @@ export default {
const ownedItem = this.flatGear[gearKey];
const isSearched = !searchText
|| ownedItem.text().toLowerCase().indexOf(searchText) !== -1;
|| ownedItem.text().toLowerCase().indexOf(searchText) !== -1
|| ownedItem.notes().toLowerCase().indexOf(searchText) !== -1;
if (ownedItem.klass !== 'base' && isSearched) {
const { type } = ownedItem;
@@ -522,8 +605,27 @@ export default {
subSection: this.$t('equipment'),
section: this.$t('inventory'),
});
this.handleResize = throttle(() => {
this.windowWidth = window.innerWidth;
}, 250);
window.addEventListener('resize', this.handleResize);
},
beforeDestroy () {
window.removeEventListener('resize', this.handleResize);
},
methods: {
getContainerClass () {
const equippedCount = Object.keys(this.gearTypesToStrings).filter(group => {
const item = this.flatGear[this.activeItems[group]];
return item && item.key.indexOf('_base_0') === -1;
}).length;
if (this.windowWidth <= 1024) {
return equippedCount > 4 ? 'equipment-scale-small' : 'equipment-scale-default';
}
return '';
},
selectDrawerTab (tabName) {
let tabNameValue;
if (tabName === 'costume') {

View File

@@ -343,9 +343,10 @@ export default {
if (itemQuantity > 0 && isAllowed) {
const item = contentItems[itemKey];
const isSearched = !searchText || item.text()
.toLowerCase()
.indexOf(searchText) !== -1;
const isSearched = !searchText
|| item.text().toLowerCase().indexOf(searchText) !== -1
|| item.notes().toLowerCase().indexOf(searchText) !== -1;
if (isSearched && item) {
itemsArray.push({
...item,

View File

@@ -628,6 +628,8 @@ export default {
animals.push({
key: specialKey,
eggKey,
eggName: text(),
mountName: text(),
potionKey,
name: text(),
canFind,
@@ -658,7 +660,6 @@ export default {
}
this.cachedAnimalList[key] = animals;
return animals;
},
listAnimals (animalGroup, type, hideMissing, sort, searchText) {
@@ -671,7 +672,9 @@ export default {
}
if (searchText && searchText !== '') {
animals = _filter(animals, a => a.name.toLowerCase().indexOf(searchText) !== -1);
animals = _filter(animals, a => a.name.toLowerCase().indexOf(searchText) !== -1
|| a.eggName.toLowerCase().indexOf(searchText) !== -1
|| a.mountName.toLowerCase().indexOf(searchText) !== -1);
}
// 2. Sort
@@ -694,7 +697,6 @@ export default {
}
this.viewOptions[animalGroup.key].animalCount = animals.length;
return animals;
},
countOwnedAnimals (animalGroup, type) {

View File

@@ -14,7 +14,7 @@
<div class="pet-background">
<Sprite
class="mount"
:image-name="`Mount_Icon_${mount.key}`"
:image-name="`stable_Mount_Icon_${mount.key}`"
/>
</div>
<h4 class="title">
@@ -66,6 +66,7 @@
.mount {
margin: 0 auto;
margin-top: 22px;
}
.dialog-header {

View File

@@ -114,7 +114,6 @@
</style>
<script>
import some from 'lodash/some';
import moment from 'moment';
import { v4 as uuid } from 'uuid';
import { mapState } from '@/libs/store';
@@ -183,13 +182,12 @@ export default {
return 'GreyedOut';
},
imageName () {
if (this.isOwned() && some(
this.currentEventList,
event => moment().isBetween(event.start, event.end) && event.aprilFools && event.aprilFools === 'Fungi',
)) {
if (this.isSpecial()) return `stable_${this.foolPet(this.item.key)}`;
const foolEvent = this.currentEventList?.find(event => moment()
.isBetween(event.start, event.end) && event.aprilFools);
if (this.isOwned() && foolEvent) {
if (this.isSpecial()) return `stable_${this.foolPet(this.item.key, foolEvent.aprilFools)}`;
const petString = `${this.item.eggKey}-${this.item.key}`;
return `stable_${this.foolPet(petString)}`;
return `stable_${this.foolPet(petString, foolEvent.aprilFools)}`;
}
if (this.isOwned() || (this.mountOwned() && this.isHatchable())) {

View File

@@ -15,7 +15,7 @@
<div
class="svg-icon mr-1"
:title="$t('liked')"
v-html="icons.liked"
v-html="likedIcon"
></div>
+{{ likeCount }}
</div>
@@ -47,7 +47,7 @@
width: 16px;
}
&.isLiked {
&.isLiked.currentUserLiked {
color: $purple-200;
font-weight: bold;
@@ -95,7 +95,11 @@ export default {
}),
};
},
computed: {
likedIcon () {
return this.likedByCurrentUser ? this.icons.liked : this.icons.like;
},
},
methods: {
async like () {
this.$emit('toggle-like');

View File

@@ -39,8 +39,9 @@
class="avatar-left"
:member="conversationOpponentUser"
:avatar-only="true"
:show-weapon="false"
:show-weapon="true"
:debug-mode="false"
:height="null"
:override-top-padding="'0'"
:hide-class-badge="true"
@click.native="showMemberModal(msg.uuid)"
@@ -59,8 +60,9 @@
v-if="user && user._id === msg.uuid"
class="avatar-right"
:member="user"
:height="null"
:avatar-only="true"
:show-weapon="false"
:show-weapon="true"
:debug-mode="false"
:hide-class-badge="true"
:override-top-padding="'0'"
@@ -89,19 +91,6 @@
}
}
.avatar-left {
margin-right: 1.5rem;
}
.avatar-right {
overflow: clip;
margin-left: 1.5rem;
::v-deep .character-sprites {
margin-right: 1rem !important;
}
}
.card {
border: 0px;
margin-bottom: 1rem;

View File

@@ -692,7 +692,7 @@
<div class="form-inline clearfix">
<Sprite
class="pull-left"
:class="'inventory_quest_scroll_' + item.key"
:image-name="'inventory_quest_scroll_' + item.key"
style="margin-right: 10px"
/>
<p>{{ item.text() }}</p>

View File

@@ -81,9 +81,10 @@ import moment from 'moment';
import habiticaMarkdown from 'habitica-markdown';
import { mapState } from '@/libs/store';
import seasonalNPC from '@/mixins/seasonalNPC';
import { userStateMixin } from '../../mixins/userState';
export default {
mixins: [seasonalNPC],
mixins: [seasonalNPC, userStateMixin],
data () {
return {
posts: [],
@@ -107,7 +108,7 @@ export default {
if (lastPublishedPost) this.posts.push(lastPublishedPost);
// If the user is authorized, show any draft
if (this.user && this.user.contributor.newsPoster) {
if (this.user && (this.hasPermission(this.user, 'news'))) {
this.posts.unshift(
...postsFromServer
.filter(p => !p.published || moment().isBefore(p.publishDate)),

View File

@@ -872,7 +872,6 @@ export default {
purchasedPlanIdInfo () {
if (!this.subscriptionBlocks[this.user.purchased.plan.planId]) {
// @TODO: find which subs are in the common
// console.log(this.subscriptionBlocks
// [this.user.purchased.plan.planId]); // eslint-disable-line
return {
price: 0,

View File

@@ -3,6 +3,7 @@
<div
v-for="currency of currencies"
:key="currency.key"
:needed-currency-only="neededCurrencyOnly"
class="d-flex align-items-center"
>
<div
@@ -54,6 +55,9 @@ export default {
amountNeeded: {
type: Number,
},
neededCurrencyOnly: {
type: Boolean,
},
},
data () {
return {
@@ -66,34 +70,34 @@ export default {
},
computed: {
currencies () {
const currencies = [];
currencies.push({
const currencies = [{
type: 'hourglasses',
icon: this.icons.hourglasses,
value: this.userHourglasses,
});
},
currencies.push({
{
type: 'gems',
icon: this.icons.gem,
value: this.userGems,
});
},
currencies.push({
{
type: 'gold',
icon: this.icons.gold,
value: this.userGold,
});
}];
for (const currency of currencies) {
if (
currency.type === this.currencyNeeded
&& !this.enoughCurrency(this.currencyNeeded, this.amountNeeded)
if (currency.type === this.currencyNeeded
&& !this.enoughCurrency(this.currencyNeeded, this.amountNeeded)
) {
currency.notEnough = true;
}
}
if (this.neededCurrencyOnly) {
return currencies.filter(curr => curr.type === this.currencyNeeded);
}
return currencies;
},
},

View File

@@ -19,7 +19,7 @@ export const QuestHelperMixin = {
case 'quests':
return `inventory_quest_scroll_${drop.key}`;
case 'mounts':
return `Mount_Icon_${drop.key}`;
return `stable_Mount_Icon_${drop.key}`;
case 'pets':
return `stable_Pet-${drop.key}`;
default:

View File

@@ -41,30 +41,58 @@
<div class="standard-page">
<div class="featuredItems">
<div
v-if="isSubscribed || (hasTrinket && !isSubscribed)"
class="background"
:class="{'background-closed': closed, 'background-open': !closed }"
:style="{'background-image': imageURLs.background}"
>
<div
class="npc"
:class="{'closed': closed }"
:style="{'background-image': imageURLs.npc}"
>
<div class="featured-label">
<span class="rectangle"></span><span
<span class="rectangle"></span>
<span
v-once
class="text"
>{{ $t('timeTravelers') }}</span><span class="rectangle"></span>
>
{{ $t('timeTravelers') }}
</span>
<span class="rectangle"></span>
</div>
</div><div
v-if="closed"
class="content"
</div>
</div>
<div class="content">
<div
class="background"
:style="{'background-image': imageURLs.background}"
>
<div class="featured-label with-border closed">
<span class="rectangle"></span><span
<div
class="npc"
:style="{'background-image': imageURLs.npc}"
>
<div class="featured-label">
<span class="rectangle"></span>
<span
v-once
class="text"
>
{{ $t('timeTravelers') }}
</span>
<span class="rectangle"></span>
</div>
</div>
<div
v-if="!isSubscribed && !hasTrinket"
class="shop-message featured-label with-border closed"
>
<span class="rectangle"></span>
<span
v-once
class="text"
>{{ $t('timeTravelersPopoverNoSubMobile') }}</span><span class="rectangle"></span>
>
{{ $t('timeTravelersPopoverNoSubMobile') }}
</span>
<span class="rectangle"></span>
</div>
</div>
</div>
@@ -126,7 +154,8 @@
</template>
</itemRows>
</div>
</div><buyQuestModal
</div>
<buyQuestModal
:item="selectedItemToBuy || {}"
:price-type="selectedItemToBuy ? selectedItemToBuy.currency : ''"
:with-pin="true"
@@ -163,6 +192,7 @@ import _throttle from 'lodash/throttle';
import _groupBy from 'lodash/groupBy';
import _map from 'lodash/map';
import _find from 'lodash/find';
import moment from 'moment';
import isPinned from '@/../../common/script/libs/isPinned';
import shops from '@/../../common/script/libs/shops';
import { mapState } from '@/libs/store';
@@ -233,15 +263,18 @@ export default {
userItems: 'user.data.items',
currentEventList: 'worldState.data.currentEventList',
}),
closed () {
return this.user.purchased.plan.consecutive.trinkets === 0;
isSubscribed () {
const now = new Date();
const { plan } = this.user.purchased;
return plan && plan.customerId
&& (!plan.dateTerminated || moment(plan.dateTerminated).isAfter(now));
},
hasTrinket () {
return this.user.purchased.plan.consecutive.trinkets > 0;
},
shop () {
return shops.getTimeTravelersShop(this.user);
},
categories () {
const apiCategories = this.shop.categories;
@@ -302,10 +335,8 @@ export default {
}
});
this.currentEvent = _find(this.currentEventList, event => Boolean(['winter', 'spring', 'summer', 'fall'].includes(event.season)));
if (!this.currentEvent || !this.currentEvent.season || this.currentEvent.season === 'thanksgiving' || this.closed) {
if (!this.currentEvent || !this.currentEvent.season || this.currentEvent.season === 'thanksgiving') {
this.imageURLs.background = 'url(/static/npc/normal/time_travelers_background.png)';
this.imageURLs.npc = this.closed ? 'url(/static/npc/normal/time_travelers_closed_banner.png)'
: 'url(/static/npc/normal/time_travelers_open_banner.png)';
} else {
this.imageURLs.background = `url(/static/npc/${this.currentEvent.season}/time_travelers_background.png)`;
this.imageURLs.npc = `url(/static/npc/${this.currentEvent.season}/time_travelers_open_banner.png)`;

View File

@@ -31,13 +31,6 @@
&colon;&nbsp;
<a href="mailto:admin@habitica.com">admin&commat;habitica&period;com</a>
<br>
{{ $t('generalQuestionsSite') }}
&colon;&nbsp;
<a
target="_blank"
@click.prevent="openBugReportModal(true)"
> {{ $t('askQuestion') }}</a>
<br>
{{ $t('businessInquiries') }}
&colon;&nbsp;
<a href="mailto:admin@habitica.com">admin@habitica.com</a>
@@ -54,10 +47,8 @@
<script>
import { mapState } from '@/libs/store';
import { goToModForm } from '@/libs/modform';
import reportBug from '@/mixins/reportBug.js';
export default {
mixins: [reportBug],
computed: {
...mapState({
user: 'user.data',

View File

@@ -6,127 +6,171 @@
</div>
</div>
<div class="row">
<div class="col-md-6">
<img src="@/assets/images/marketing/screenshot.png">
<h2>{{ $t('marketing1Lead1Title') }}</h2>
<p>{{ $t('marketing1Lead1') }}</p>
</div>
<div class="col-md-6">
<img src="@/assets/images/marketing/gear.png">
<h2>{{ $t('marketing1Lead2Title') }}</h2>
<p>{{ $t('marketing1Lead2') }}</p>
<div class="col-12 mb-5 mb-md-0">
<img
src="@/assets/images/marketing/drops.png"
style="max-height: 200px;"
:src="makeUrl('features_taskboard.png')"
class="img-fluid"
>
<h2>{{ $t('marketing1Lead1Title') }}</h2>
<div class="row justify-content-md-center">
<p class="col col-lg-8 col-xl-6 margin-auto description">
{{ $t('marketing1Lead1') }}
</p>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-5 mb-md-0">
<img :src="makeUrl('features_gear.png')">
<h2>{{ $t('marketing1Lead2Title') }}</h2>
<p class="description">
{{ $t('marketing1Lead2') }}
</p>
</div>
<div class="col-md-6 mb-5 mb-md-0">
<img :src="makeUrl('features_items.png')">
<h2>{{ $t('marketing1Lead3Title') }}</h2>
<p>{{ $t('marketing1Lead3') }}</p>
<p class="description">
{{ $t('marketing1Lead3') }}
</p>
</div>
</div>
<hr>
<div class="row">
<div class="col-md-12">
<div class="col-12">
<h1>{{ $t('marketing2Header') }}</h1>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="row mb-5">
<div class="col-12">
<h2>{{ $t('marketing2Lead1Title') }}</h2>
<p>{{ $t('marketing2Lead1') }}</p>
<img src="@/assets/images/marketing/vice3.png">
<h2>{{ $t('marketing2Lead2Title') }}</h2>
<p v-markdown="$t('marketing2Lead2')"></p>
<p class="description">
{{ $t('marketing2Lead1') }}
</p>
</div>
<div class="col-md-6">
<img src="@/assets/images/marketing/challenge.png">
</div>
<div class="row">
<div class="col-md-6 mb-5 mb-md-0">
<img :src="makeUrl('features_monsters.png')">
<h2>{{ $t('marketing2Lead2Title') }}</h2>
<p
v-markdown="$t('marketing2Lead2')"
class="description"
></p>
</div>
<div class="col-md-6 mb-5 mb-md-0">
<img :src="makeUrl('features_challenges.png')">
<h2>{{ $t('marketing2Lead3Title') }}</h2>
<p>{{ $t('marketing2Lead3') }}</p>
<p class="description">
{{ $t('marketing2Lead3') }}
</p>
</div>
</div>
<hr>
<div class="row">
<div class="col-md-12">
<div class="col-12">
<h1>{{ $t('marketing3Header') }}</h1>
</div>
</div>
<div class="row">
<div class="col-md-6">
<img src="@/assets/images/marketing/android_iphone.png">
<h2>{{ $t('footerMobile') }}</h2>
<p v-markdown="$t('marketing3Lead1')"></p>
<div class="col-md-6 mb-5 mb-md-0">
<img :src="makeUrl('features_mobile.png')">
<h2>{{ $t('marketing3Lead1Title') }}</h2>
<p
v-markdown="$t('marketing3Lead1')"
class="description"
></p>
</div>
<div class="col-md-6">
<img src="@/assets/images/marketing/integration.png">
<div class="col-md-6 mb-5 mb-md-0">
<img :src="makeUrl('features_opensource.png')">
<h2>{{ $t('marketing3Lead2Title') }}</h2>
<p v-markdown="$t('marketing3Lead2')"></p>
<p
v-markdown="$t('marketing3Lead2')"
class="description"
></p>
</div>
</div>
<hr>
<div class="row">
<div class="col-md-12">
<div class="col-12">
<h1>{{ $t('marketing4Header') }}</h1>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="col-md-6 mb-5 mb-md-0">
<div class="media">
<img src="@/assets/images/marketing/education.png">
<div class="media-body">
<h2>{{ $t('marketing4Lead1Title') }}</h2>
<p>{{ $t('marketing4Lead1') }}</p>
<p class="description">
{{ $t('marketing4Lead1') }}
</p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="col-md-6 mb-5 mb-md-0">
<div class="media">
<img src="@/assets/images/marketing/wellness.png">
<div class="media-body">
<h2>{{ $t('marketing4Lead2Title') }}</h2>
<p>{{ $t('marketing4Lead2') }}</p>
<p class="description">
{{ $t('marketing4Lead2') }}
</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 offset-md-3">
<h2>{{ $t('marketing4Lead3Title') }}</h2>
<img src="@/assets/images/marketing/lefnire.png">
<p class="span">
<span>{{ $t('marketing4Lead3-1') }}</span>
<div class="row mt-5">
<div class="col-md-6 offset-md-3 mb-5 mb-md-0">
<h1>{{ $t('marketing4Lead3Title') }}</h1>
<img :src="makeUrl('features_gamify.png')">
<div class="cta-container">
<h2>{{ $t('marketing4Lead3-1') }}</h2>
<button
class="btn btn-primary"
class="btn btn-lg btn-primary"
@click="playButtonClick()"
>
{{ $t('playButton') }}
{{ $t('marketing4Lead3Button') }}
</button>
</p>
<p class="span">
<span>{{ $t('marketing4Lead3-2') }}</span>
<a
class="btn btn-primary"
href="/static/plans"
target="_blank"
>{{ $t('contactUs') }}</a>
</p>
</div>
</div>
</div>
</div>
</template>
<style lang='scss' scoped>
.btn {
margin-left: 1em;
}
img {
box-shadow: 0 0 10px 5px #888;
margin: 0.5em;
max-width: 500px;
max-width: 350px;
@media (max-width: 1200px) {
max-width: 100%;
width: 100%;
}
}
.img-fluid {
width: 100%;
max-width: 900px;
}
.cta-container {
text-align: center;
margin: 2em auto;
}
h1 {
font-size: 2rem;
}
h2 {
font-size: 1.5rem;
}
.description {
max-width: 600px;
margin: auto;
font-size: 1.1rem;
line-height: 1.6rem;
}
</style>
<script>
@@ -145,6 +189,9 @@ export default {
playButtonClick () {
this.$router.push('/register');
},
makeUrl (filename) {
return `https://habitica-assets.s3.amazonaws.com/assets/${filename}`;
},
},
};
</script>

View File

@@ -66,16 +66,13 @@
class="nav-link"
>{{ $t('presskit') }}</a>
</router-link>
<router-link
class="nav-item"
tag="li"
to="/static/contact"
>
<li class="nav-item">
<a
v-once
class="nav-link"
href="mailto:admin@habitica.com"
>{{ $t('contactUs') }}</a>
</router-link>
</li>
</ul>
<ul
v-else

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