Compare commits

...

193 Commits

Author SHA1 Message Date
Kalista Payne
7d8d456103 fix(lint): leave out unused imports 2024-12-17 16:10:44 -06:00
negue
82445d2360 working mongo docker compose 🎉 2024-12-03 20:09:25 +01:00
negue
7e4bf86ee7 try newer action version 2024-11-21 21:55:42 +01:00
negue
59427b65f4 try gh action again 2024-11-21 21:46:33 +01:00
negue
16aed7fc5c disable mongo bootstrap from build 2024-11-21 21:29:00 +01:00
negue
0ee61ee990 try "docker compose" 2024-11-21 21:11:49 +01:00
negue
98dacaa4c2 try npm -> docker instead of services 2024-11-21 21:03:39 +01:00
negue
e940d8211e remove matrix for mongo 2024-11-21 20:51:18 +01:00
negue
5a16cd5ab3 Merge remote-tracking branch 'origin/sabrecat/mongodb-upgrade' into sabrecat/mongodb-upgrade 2024-11-21 20:49:12 +01:00
negue
bbf13f0380 run mongo as gh action service 2024-11-21 20:49:03 +01:00
Kalista Payne
ade793e869 fix(mongo): start replica set 2024-11-05 14:56:24 -06:00
negue
cce4363319 WIP mongodb-memory-serve 2024-10-29 22:43:03 +01:00
Kalista Payne
7c9b7be012 Revert "test(api): attempt to gather more detail on failures"
This reverts commit 215e768e90.
2024-10-03 16:21:27 -05:00
Kalista Payne
215e768e90 test(api): attempt to gather more detail on failures 2024-10-03 16:18:14 -05:00
Kalista Payne
551b4569dd fix(github): update mongodb-github-action 2024-10-03 15:59:19 -05:00
Kalista Payne
1526c3fd10 chore(mongodb): update local dev MongoDB versions 2024-10-03 15:49:28 -05:00
Kalista Payne
af17930314 5.28.8 2024-10-03 14:27:03 -05:00
Weblate
094b19f289 Translated using Weblate (Indonesian)
Currently translated at 100.0% (237 of 237 strings)

Translated using Weblate (Hungarian)

Currently translated at 62.7% (271 of 432 strings)

Translated using Weblate (Indonesian)

Currently translated at 94.3% (834 of 884 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Hungarian)

Currently translated at 74.5% (193 of 259 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.8% (137 of 140 strings)

Translated using Weblate (Korean)

Currently translated at 55.7% (1784 of 3201 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (Korean)

Currently translated at 48.3% (88 of 182 strings)

Translated using Weblate (Korean)

Currently translated at 79.6% (106 of 133 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.5% (138 of 140 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (393 of 393 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.2% (179 of 188 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 94.0% (764 of 812 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (237 of 237 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.5% (3187 of 3201 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 94.3% (766 of 812 strings)

Translated using Weblate (German)

Currently translated at 97.9% (423 of 432 strings)

Translated using Weblate (Hungarian)

Currently translated at 58.5% (157 of 268 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 94.2% (407 of 432 strings)

Translated using Weblate (German)

Currently translated at 97.6% (422 of 432 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.9% (3168 of 3201 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (812 of 812 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.9% (3168 of 3201 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.6% (267 of 268 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.9% (3168 of 3201 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.2% (179 of 188 strings)

Translated using Weblate (Spanish)

Currently translated at 98.3% (799 of 812 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (392 of 393 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (German)

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (237 of 237 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 6.3% (12 of 188 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Spanish)

Currently translated at 98.0% (796 of 812 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (393 of 393 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (French)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (French)

Currently translated at 100.0% (237 of 237 strings)

Translated using Weblate (German)

Currently translated at 98.7% (234 of 237 strings)

Translated using Weblate (German)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3201 of 3201 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Russian)

Currently translated at 98.9% (389 of 393 strings)

Translated using Weblate (French)

Currently translated at 100.0% (393 of 393 strings)

Translated using Weblate (Czech)

Currently translated at 95.2% (159 of 167 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (236 of 236 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3201 of 3201 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 75.0% (141 of 188 strings)

Translated using Weblate (Spanish)

Currently translated at 97.9% (795 of 812 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (390 of 390 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2935 of 3201 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 7.4% (14 of 188 strings)

Translated using Weblate (French)

Currently translated at 100.0% (812 of 812 strings)

Translated using Weblate (German)

Currently translated at 93.7% (761 of 812 strings)

Translated using Weblate (French)

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (Italian)

Currently translated at 94.2% (830 of 881 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3201 of 3201 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2935 of 3201 strings)

Translated using Weblate (Czech)

Currently translated at 11.7% (22 of 188 strings)

Translated using Weblate (Czech)

Currently translated at 74.8% (601 of 803 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 90.9% (100 of 110 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 70.5% (2254 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.8% (2933 of 3193 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 6.9% (13 of 188 strings)

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

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

    fix(event): remove UTC wording

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

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

* Add armoire

* Add subscriber gear

* add new pet

* improve quest tests

* Fixes from gear switchup

* use new sprite system for quest images

* fixes

* fix quest image alignment

* add missing string

* fix(style): lint warnings and typo

---------

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

Translated using Weblate (Russian)

Currently translated at 92.9% (2969 of 3193 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.0% (3066 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.7% (2931 of 3193 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3193 of 3193 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (French)

Currently translated at 100.0% (432 of 432 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (French)

Currently translated at 99.8% (3187 of 3193 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.5% (775 of 803 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.4% (376 of 390 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (German)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (German)

Currently translated at 99.6% (267 of 268 strings)

Translated using Weblate (German)

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (German)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Hungarian)

Currently translated at 67.5% (175 of 259 strings)

Translated using Weblate (French)

Currently translated at 99.4% (3175 of 3193 strings)

Translated using Weblate (Hungarian)

Currently translated at 71.3% (573 of 803 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (French)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (German)

Currently translated at 91.7% (2929 of 3193 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (390 of 390 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.4% (388 of 390 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.4% (388 of 390 strings)

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

    fix(groups): remove outdated group FAQ modal

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

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

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

    fix(groups): correct static page account creation flow

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

    chore(payments): start retiring Amazon Payments

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

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

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

    refactor(groups): rearrange some CSS for better semantics

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

    fix(groups): add missing upgrade workflow pieces

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

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

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

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

    update mixin to add onlyOwner and quest title

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

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

Translated using Weblate (French)

Currently translated at 100.0% (236 of 236 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (French)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (French)

Currently translated at 99.3% (3172 of 3193 strings)

Translated using Weblate (French)

Currently translated at 100.0% (803 of 803 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2927 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2926 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2926 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2926 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2926 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2926 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2926 of 3193 strings)

Translated using Weblate (German)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (French)

Currently translated at 100.0% (390 of 390 strings)

Translated using Weblate (French)

Currently translated at 100.0% (881 of 881 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.3% (774 of 803 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (390 of 390 strings)

Translated using Weblate (Hungarian)

Currently translated at 92.3% (360 of 390 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Hungarian)

Currently translated at 80.8% (76 of 94 strings)

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

Translated using Weblate (Hungarian)

Currently translated at 91.5% (357 of 390 strings)

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

* fix(lint): whitespace and commas

* Add tests for drop cap

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

---------

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

Translated using Weblate (Danish)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Danish)

Currently translated at 92.9% (106 of 114 strings)

Translated using Weblate (Danish)

Currently translated at 87.5% (7 of 8 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Danish)

Currently translated at 67.9% (290 of 427 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2925 of 3193 strings)

Translated using Weblate (Danish)

Currently translated at 57.2% (1829 of 3193 strings)

Translated using Weblate (Danish)

Currently translated at 84.1% (202 of 240 strings)

Translated using Weblate (Danish)

Currently translated at 91.7% (167 of 182 strings)

Translated using Weblate (Danish)

Currently translated at 6.3% (12 of 188 strings)

Translated using Weblate (Danish)

Currently translated at 75.0% (603 of 803 strings)

Translated using Weblate (Danish)

Currently translated at 53.8% (49 of 91 strings)

Translated using Weblate (Hungarian)

Currently translated at 90.0% (351 of 390 strings)

Translated using Weblate (Danish)

Currently translated at 95.7% (45 of 47 strings)

Translated using Weblate (Danish)

Currently translated at 94.3% (182 of 193 strings)

Translated using Weblate (Danish)

Currently translated at 62.5% (162 of 259 strings)

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

* remove only

* don’t actually need to check notification count

* fix tests

---------

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

* remove only

---------

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

* add emoji to server name

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

* make script use bash

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

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

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

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

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

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

    feat(promo): add canonical dates for gem sales

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

    fix(gems): correct Gem amounts during sale

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

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

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

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

Translated using Weblate (German)

Currently translated at 91.5% (2923 of 3193 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 2.6% (5 of 188 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 86.9% (766 of 881 strings)

Translated using Weblate (German)

Currently translated at 91.4% (2919 of 3193 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (German)

Currently translated at 91.3% (2917 of 3193 strings)

Translated using Weblate (German)

Currently translated at 91.3% (2917 of 3193 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (German)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (German)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (German)

Currently translated at 100.0% (387 of 387 strings)

Translated using Weblate (German)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 98.6% (869 of 881 strings)

Translated using Weblate (Russian)

Currently translated at 29.7% (56 of 188 strings)

Translated using Weblate (Russian)

Currently translated at 98.9% (386 of 390 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (German)

Currently translated at 91.1% (2912 of 3193 strings)

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

* Make Admin panel design more consistent

* fix test

* fix width of items

* escape regex for searching users

* load own user when pressing enter on empty field

* add styling for warning buttons

* improve sub styling

* fix checkbox alignment in admin panel

* Unify date preview display

* Fix bottom button display

* admin panel display improvements

* remove autocannon file

* search improvements

* time travel button display fix

* fix loading spinner

* fix sorting

* Split email search into multiple queries

* fix email search

* remove console

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

Translated using Weblate (German)

Currently translated at 99.2% (266 of 268 strings)

Translated using Weblate (German)

Currently translated at 91.1% (2910 of 3193 strings)

Translated using Weblate (Hungarian)

Currently translated at 77.4% (103 of 133 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 91.8% (238 of 259 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Polish)

Currently translated at 65.1% (172 of 264 strings)

Translated using Weblate (Polish)

Currently translated at 55.2% (1765 of 3193 strings)

Translated using Weblate (Polish)

Currently translated at 99.4% (181 of 182 strings)

Translated using Weblate (German)

Currently translated at 98.7% (233 of 236 strings)

Translated using Weblate (German)

Currently translated at 100.0% (881 of 881 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Japanese)

Currently translated at 97.8% (3085 of 3153 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (235 of 235 strings)

Translated using Weblate (Japanese)

Currently translated at 96.0% (763 of 794 strings)

Translated using Weblate (Japanese)

Currently translated at 99.5% (874 of 878 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (390 of 390 strings)

Translated using Weblate (Japanese)

Currently translated at 97.8% (184 of 188 strings)

Translated using Weblate (German)

Currently translated at 99.4% (187 of 188 strings)

Translated using Weblate (Turkish)

Currently translated at 94.7% (108 of 114 strings)

Translated using Weblate (Turkish)

Currently translated at 69.0% (295 of 427 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Turkish)

Currently translated at 92.8% (155 of 167 strings)

Translated using Weblate (German)

Currently translated at 98.9% (90 of 91 strings)

Translated using Weblate (German)

Currently translated at 96.7% (88 of 91 strings)

Translated using Weblate (German)

Currently translated at 94.5% (86 of 91 strings)

Translated using Weblate (German)

Currently translated at 82.4% (75 of 91 strings)

Translated using Weblate (German)

Currently translated at 76.9% (70 of 91 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (German)

Currently translated at 69.2% (63 of 91 strings)

Translated using Weblate (German)

Currently translated at 92.2% (2908 of 3153 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Hungarian)

Currently translated at 92.3% (168 of 182 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (German)

Currently translated at 92.1% (2906 of 3153 strings)

Translated using Weblate (Hungarian)

Currently translated at 81.4% (136 of 167 strings)

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

* attempt to fix t.response

* removing attempt to fix t.response

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

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

* 2024-09 enchanted armoire gear

* 2024 fall festival gear

* 2024-09 background

* 2024-09 pet quest

* typos and update featuredItems.js

* quest and typo fix

* fix subscriber set name

* text amendments

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

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

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

Translated using Weblate (Russian)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (German)

Currently translated at 92.1% (2904 of 3153 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Russian)

Currently translated at 99.7% (876 of 878 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Hungarian)

Currently translated at 79.0% (132 of 167 strings)

Translated using Weblate (Hungarian)

Currently translated at 99.1% (113 of 114 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Hungarian)

Currently translated at 78.4% (131 of 167 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Hungarian)

Currently translated at 71.2% (119 of 167 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Indonesian)

Currently translated at 90.7% (235 of 259 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 96.0% (763 of 794 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.9% (762 of 794 strings)

Translated using Weblate (German)

Currently translated at 92.0% (2902 of 3153 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (3153 of 3153 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.7% (760 of 794 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.5% (3108 of 3153 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (235 of 235 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3153 of 3153 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Russian)

Currently translated at 98.2% (863 of 878 strings)

Translated using Weblate (German)

Currently translated at 91.9% (2898 of 3153 strings)

Translated using Weblate (Russian)

Currently translated at 97.2% (854 of 878 strings)

Translated using Weblate (Indonesian)

Currently translated at 95.7% (225 of 235 strings)

Translated using Weblate (Indonesian)

Currently translated at 96.4% (110 of 114 strings)

Translated using Weblate (Indonesian)

Currently translated at 99.2% (132 of 133 strings)

Translated using Weblate (Indonesian)

Currently translated at 99.4% (187 of 188 strings)

Translated using Weblate (French)

Currently translated at 100.0% (794 of 794 strings)

Translated using Weblate (Indonesian)

Currently translated at 97.9% (189 of 193 strings)

Translated using Weblate (German)

Currently translated at 91.7% (2894 of 3153 strings)

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

* correctly load equipped gear

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

* tinkering

* remove if statement wrapper and modify error message

* add test cases

* more work on test cases

* adding contexts to test cases

* test(debug): fix up tests

* fix(lint): whisepate

---------

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

* Only load necessary user data for group-plans call

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

* Only load necessary user fields for in app rewards

* Optimize updateStore by not checking every item

* Only load necessary user data for task scoring

* improve performance of inbox request calls

* merge fix

* fix scoring task call

* add quests to scoring call

* fix showing official pinned items

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

Translated using Weblate (French)

Currently translated at 100.0% (387 of 387 strings)

Translated using Weblate (German)

Currently translated at 91.7% (2892 of 3153 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Turkish)

Currently translated at 92.2% (154 of 167 strings)

Translated using Weblate (Russian)

Currently translated at 99.5% (425 of 427 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.2% (764 of 794 strings)

Translated using Weblate (Russian)

Currently translated at 94.1% (2969 of 3153 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3153 of 3153 strings)

Translated using Weblate (French)

Currently translated at 99.2% (788 of 794 strings)

Translated using Weblate (German)

Currently translated at 98.7% (232 of 235 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2890 of 3153 strings)

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

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

* Only include the needed parts of BootstrapVue

* remove yargs from client

* treeshake validator library

* formatting

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

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

* fix lint

* fix hiding loading screen

* fix showing snackbars when not logged in

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

* make rate limiter config names more consistent

* fix tests and add new one

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

* fix(lint): remove unused const

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

* fix(lint): remove extra whitespace

* fix(groups): remove more broken inviter logic

---------

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

* fix broken test

* fix switchover for time based matchers

* Fix scheduling issue related to timezones

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

Translated using Weblate (Spanish)

Currently translated at 100.0% (3153 of 3153 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 96.2% (181 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 88.8% (167 of 188 strings)

Translated using Weblate (French)

Currently translated at 100.0% (235 of 235 strings)

Translated using Weblate (French)

Currently translated at 99.8% (3148 of 3153 strings)

Translated using Weblate (Indonesian)

Currently translated at 87.7% (165 of 188 strings)

Translated using Weblate (French)

Currently translated at 99.1% (787 of 794 strings)

Translated using Weblate (German)

Currently translated at 95.3% (757 of 794 strings)

Translated using Weblate (German)

Currently translated at 99.4% (385 of 387 strings)

Translated using Weblate (German)

Currently translated at 100.0% (878 of 878 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Russian)

Currently translated at 96.0% (763 of 794 strings)

Translated using Weblate (Portuguese)

Currently translated at 35.1% (66 of 188 strings)

Translated using Weblate (Portuguese)

Currently translated at 25.5% (48 of 188 strings)

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

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

* Handle mongodb timeouts better

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

* Fix serving memoized content

* add missing info to mystery item strings

* add missing string info to July mystery items

* fix food

* melior updates - loading screen & menu bar

* updates to currency tray spacing

* table styling

* one last table style

* add margin adjustment

* fix spacing

* clean up spacing styles

* remove table rounded corners

* correct faq tyops

* spacing updates

* fix web challenge instructions

* beach umbrella you are not shady except when you are

* fix mobile margin weirdness

* update wording on answers

* mobile spacing

* add chart showing details of item 1.4

* revert unrelated changes

* melior centering and icon spacing

* remove unnecessary files

* rejiggering spacing

* remove errant comma

* remove unnecessary files

* remove duplicated class

* remove duplicated class

* ...and more spacing

* notification bubble

* notification icon & currency alignment

* fix(CSS): clean up some unnecessary classes

---------

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

* Fix serving memoized content

* add missing info to mystery item strings

* add missing string info to July mystery items

* fix food

* melior updates - loading screen & menu bar

* updates to currency tray spacing

* table styling

* one last table style

* add margin adjustment

* fix spacing

* clean up spacing styles

* remove table rounded corners

* correct faq tyops

* spacing updates

* fix web challenge instructions

* update wording on answers

* revert unrelated changes

---------

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

Translated using Weblate (Japanese)

Currently translated at 97.8% (3085 of 3153 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.6% (109 of 114 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.4% (3105 of 3153 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 79.2% (149 of 188 strings)

Translated using Weblate (Portuguese)

Currently translated at 23.4% (44 of 188 strings)

Translated using Weblate (Portuguese)

Currently translated at 23.4% (44 of 188 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 94.7% (752 of 794 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (387 of 387 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese)

Currently translated at 95.3% (184 of 193 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (878 of 878 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (878 of 878 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.2% (764 of 794 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Indonesian)

Currently translated at 76.5% (144 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 76.5% (144 of 188 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

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

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

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

    fix hatched dialog

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

    fix popover not showing

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

    move item popover to own component

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

    make scaled sprites look nice

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

    fix hatching dialog

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

    fix popover alignment

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

    fix item display

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

    fix pet display

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

    fix background icon display

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

    fix sprites for notifications

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

    fix gear display in profile

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

    fix sprites for keys to the kennel

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

    fix sprites on avatar customization

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

    fix hover icons

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

    remove console

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

    more lint fixes

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

    fix lint

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

    support gifs

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

    move avatar customization to sprites

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

    Move more sprites out of css

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

    add new sprite to item and shopItem component

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

    use new sprites for pets list

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

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

* assign first winter seasonal gear right season

* add missing winter definition

* Fix enddate for winter galas

* fix lint

* fix halloween sprites

* set season

* fix loading habitoween sprites

* add missing customization shop sprites

* Fix test

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

Translated using Weblate (French)

Currently translated at 100.0% (878 of 878 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (235 of 235 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3153 of 3153 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (794 of 794 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (387 of 387 strings)

Translated using Weblate (Spanish)

Currently translated at 99.7% (876 of 878 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.9% (129 of 133 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.5% (3067 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 78.1% (147 of 188 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.2% (764 of 794 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.7% (108 of 114 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.8% (261 of 264 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.2% (3055 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 76.5% (144 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 39.3% (74 of 188 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.2% (764 of 794 strings)

Translated using Weblate (Spanish)

Currently translated at 99.3% (789 of 794 strings)

Translated using Weblate (Indonesian)

Currently translated at 79.1% (72 of 91 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (384 of 384 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (377 of 377 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.6% (163 of 167 strings)

Translated using Weblate (Japanese)

Currently translated at 96.8% (760 of 785 strings)

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

* 2024-08 content build

* fixing stuff

* update boss rage effects

* update quest rage text and releaseDates.js

* update shop-featuredItems.js

* fricken fracken beach umbrella

* more beach umbrella

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

Translated using Weblate (Slovak)

Currently translated at 97.2% (177 of 182 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (785 of 785 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Slovak)

Currently translated at 92.3% (12 of 13 strings)

Translated using Weblate (German)

Currently translated at 91.7% (2883 of 3143 strings)

Translated using Weblate (Slovak)

Currently translated at 96.7% (176 of 182 strings)

Translated using Weblate (Indonesian)

Currently translated at 33.5% (63 of 188 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 2.6% (5 of 188 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.4% (765 of 785 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (384 of 384 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (377 of 377 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Indonesian)

Currently translated at 32.9% (62 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 29.2% (55 of 188 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2881 of 3143 strings)

Translated using Weblate (Slovak)

Currently translated at 93.9% (171 of 182 strings)

Translated using Weblate (French)

Currently translated at 100.0% (785 of 785 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Turkish)

Currently translated at 80.4% (107 of 133 strings)

Translated using Weblate (Turkish)

Currently translated at 7.9% (15 of 188 strings)

Translated using Weblate (Russian)

Currently translated at 95.6% (751 of 785 strings)

Translated using Weblate (French)

Currently translated at 98.3% (772 of 785 strings)

Translated using Weblate (German)

Currently translated at 91.6% (2879 of 3143 strings)

Translated using Weblate (French)

Currently translated at 98.0% (770 of 785 strings)

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

* fix potion availability

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

Translated using Weblate (German)

Currently translated at 91.5% (2877 of 3143 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Spanish)

Currently translated at 98.2% (771 of 785 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.9% (3046 of 3143 strings)

Translated using Weblate (Portuguese)

Currently translated at 57.0% (1793 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.4% (765 of 785 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Italian)

Currently translated at 87.5% (7 of 8 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Italian)

Currently translated at 86.1% (368 of 427 strings)

Translated using Weblate (Italian)

Currently translated at 11.1% (21 of 188 strings)

Translated using Weblate (Italian)

Currently translated at 94.2% (740 of 785 strings)

Translated using Weblate (Italian)

Currently translated at 54.9% (50 of 91 strings)

Translated using Weblate (Italian)

Currently translated at 95.7% (45 of 47 strings)

Translated using Weblate (Italian)

Currently translated at 97.4% (188 of 193 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Italian)

Currently translated at 74.9% (194 of 259 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (German)

Currently translated at 91.4% (2875 of 3143 strings)

Translated using Weblate (Italian)

Currently translated at 94.6% (828 of 875 strings)

Translated using Weblate (Italian)

Currently translated at 94.0% (823 of 875 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 76.5% (144 of 188 strings)

Translated using Weblate (German)

Currently translated at 91.4% (2873 of 3143 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (Czech)

Currently translated at 85.7% (114 of 133 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (German)

Currently translated at 91.3% (2871 of 3143 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (1 of 1 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.4% (21 of 22 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (German)

Currently translated at 91.2% (2869 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (French)

Currently translated at 100.0% (133 of 133 strings)

Translated using Weblate (German)

Currently translated at 98.4% (131 of 133 strings)

Translated using Weblate (French)

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (French)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3143 of 3143 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.5% (239 of 240 strings)

Translated using Weblate (French)

Currently translated at 100.0% (240 of 240 strings)

Translated using Weblate (German)

Currently translated at 98.7% (237 of 240 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (384 of 384 strings)

Translated using Weblate (Slovak)

Currently translated at 97.9% (189 of 193 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Hebrew)

Currently translated at 92.8% (155 of 167 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.4% (781 of 785 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Portuguese)

Currently translated at 65.3% (153 of 234 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.8% (261 of 264 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.9% (3046 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 76.5% (144 of 188 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 76.5% (144 of 188 strings)

Translated using Weblate (Indonesian)

Currently translated at 28.1% (53 of 188 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.4% (781 of 785 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (377 of 377 strings)

Translated using Weblate (Portuguese)

Currently translated at 98.1% (377 of 384 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.6% (163 of 167 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.6% (258 of 259 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (German)

Currently translated at 91.2% (2867 of 3143 strings)

Translated using Weblate (Ukrainian)

Currently translated at 98.4% (773 of 785 strings)

Translated using Weblate (French)

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (French)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Ukrainian)

Currently translated at 61.7% (1940 of 3143 strings)

Translated using Weblate (Portuguese)

Currently translated at 57.0% (1793 of 3143 strings)

Translated using Weblate (German)

Currently translated at 91.1% (2865 of 3143 strings)

Translated using Weblate (Japanese)

Currently translated at 99.4% (3085 of 3102 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (384 of 384 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Ukrainian)

Currently translated at 61.7% (1940 of 3143 strings)

Translated using Weblate (German)

Currently translated at 91.0% (2863 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (Slovak)

Currently translated at 74.9% (588 of 785 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (German)

Currently translated at 95.6% (109 of 114 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (427 of 427 strings)

Translated using Weblate (German)

Currently translated at 91.0% (2861 of 3143 strings)

Translated using Weblate (German)

Currently translated at 98.7% (231 of 234 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (113 of 113 strings)

Translated using Weblate (German)

Currently translated at 99.2% (262 of 264 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (234 of 234 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (264 of 264 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3143 of 3143 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (384 of 384 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (233 of 233 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (286 of 286 strings)

Translated using Weblate (German)

Currently translated at 90.9% (2857 of 3143 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.4% (382 of 384 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.4% (382 of 384 strings)

Translated using Weblate (Slovak)

Currently translated at 97.6% (375 of 384 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (German)

Currently translated at 99.7% (383 of 384 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.8% (3043 of 3143 strings)

Translated using Weblate (German)

Currently translated at 90.8% (2855 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.7% (3041 of 3143 strings)

Translated using Weblate (French)

Currently translated at 99.6% (3132 of 3143 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (377 of 377 strings)

Translated using Weblate (French)

Currently translated at 100.0% (384 of 384 strings)

Translated using Weblate (Indonesian)

Currently translated at 91.3% (799 of 875 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (German)

Currently translated at 98.9% (380 of 384 strings)

Translated using Weblate (Indonesian)

Currently translated at 95.3% (184 of 193 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.7% (377 of 378 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Indonesian)

Currently translated at 94.0% (157 of 167 strings)

Translated using Weblate (Japanese)

Currently translated at 99.4% (192 of 193 strings)

Translated using Weblate (French)

Currently translated at 99.0% (3114 of 3143 strings)

Translated using Weblate (German)

Currently translated at 90.7% (2853 of 3143 strings)

Translated using Weblate (French)

Currently translated at 100.0% (785 of 785 strings)

Translated using Weblate (Indonesian)

Currently translated at 94.0% (157 of 167 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (German)

Currently translated at 98.5% (421 of 427 strings)

Translated using Weblate (French)

Currently translated at 98.7% (3105 of 3143 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (French)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (German)

Currently translated at 98.4% (190 of 193 strings)

Translated using Weblate (French)

Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (140 of 140 strings)

Translated using Weblate (Turkish)

Currently translated at 5.8% (11 of 188 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (785 of 785 strings)

Translated using Weblate (German)

Currently translated at 98.4% (773 of 785 strings)

Translated using Weblate (German)

Currently translated at 65.9% (60 of 91 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (189 of 189 strings)

Translated using Weblate (Spanish)

Currently translated at 99.7% (873 of 875 strings)

Translated using Weblate (German)

Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (773 of 773 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (868 of 868 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3143 of 3143 strings)

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

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

View File

@@ -19,7 +19,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -41,7 +42,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -63,7 +65,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -72,7 +75,7 @@ jobs:
CI: true
NODE_ENV: test
- run: npm run test:sanity
common:
runs-on: ubuntu-latest
strategy:
@@ -86,7 +89,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -108,7 +112,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -117,13 +122,13 @@ jobs:
CI: true
NODE_ENV: test
- run: npm run test:content
api-unit:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
mongodb-version: [4.2]
mongodb-version: [7.0]
steps:
- uses: actions/checkout@v4
with:
@@ -132,12 +137,15 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Start MongoDB ${{ matrix.mongodb-version }} Replica Set
uses: supercharge/mongodb-github-action@1.3.0
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: ${{ matrix.mongodb-version }}
mongodb-replica-set: rs
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -145,15 +153,19 @@ jobs:
env:
CI: true
NODE_ENV: test
#- run: npm run mongo:dev -- -d
- run: npm run test:api:unit
env:
REQUIRES_SERVER=true: true
#- run: npm run mongo:dev:down
api-v3-integration:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
mongodb-version: [4.2]
mongodb-version: [7.0]
steps:
- uses: actions/checkout@v4
with:
@@ -163,11 +175,13 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- name: Start MongoDB ${{ matrix.mongodb-version }} Replica Set
uses: supercharge/mongodb-github-action@1.3.0
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: ${{ matrix.mongodb-version }}
mongodb-replica-set: rs
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -175,15 +189,20 @@ jobs:
env:
CI: true
NODE_ENV: test
#- run: npm run mongo:dev -- -d
- run: npm run test:api-v3:integration
env:
REQUIRES_SERVER=true: true
#- run: npm run mongo:dev:down
api-v4-integration:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
mongodb-version: [4.2]
mongodb-version: [7.0]
steps:
- uses: actions/checkout@v4
with:
@@ -193,11 +212,13 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- name: Start MongoDB ${{ matrix.mongodb-version }} Replica Set
uses: supercharge/mongodb-github-action@1.3.0
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: ${{ matrix.mongodb-version }}
mongodb-replica-set: rs
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -205,10 +226,13 @@ jobs:
env:
CI: true
NODE_ENV: test
#- run: npm run mongo:dev -- -d
- run: npm run test:api-v4:integration
env:
REQUIRES_SERVER=true: true
#- run: npm run mongo:dev:down
client-unit:
runs-on: ubuntu-latest
strategy:
@@ -222,7 +246,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
@@ -246,7 +271,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |

2
.gitignore vendored
View File

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

25
.heroku/report_deploy.sh Executable file
View File

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

View File

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

View File

@@ -34,9 +34,10 @@
"LOGGLY_TOKEN": "example-token",
"LOG_REQUESTS_EXCESSIVE_MODE": "false",
"MAINTENANCE_MODE": "false",
"NODE_DB_URI": "mongodb://localhost:27017/habitica-dev?replicaSet=rs",
"TEST_DB_URI": "mongodb://localhost:27017/habitica-test?replicaSet=rs",
"NODE_DB_URI": "mongodb://localhost:27017/habitica-dev?replicaSet=rs&directConnection=true&readPreference=secondary",
"TEST_DB_URI": "mongodb://localhost:27017/habitica-test?replicaSet=rs&directConnection=true&readPreference=secondary",
"MONGODB_POOL_SIZE": "10",
"MONGODB_SOCKET_TIMEOUT": "20000",
"NODE_ENV": "development",
"PATH": "bin:node_modules/.bin:/usr/local/bin:/usr/bin:/bin",
"PAYPAL_BILLING_PLANS_basic_12mo": "basic_12mo",

View File

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

View File

@@ -0,0 +1,37 @@
# big thanks to https://pakisan.github.io/posts/docker-compose-mongodb-single-node-replica-set/ <3
version: "3.9"
networks:
mongodb-network:
name: "mongodb-network"
driver: bridge
services:
mongodb:
image: "mongo:7.0"
container_name: "mongodb"
networks:
- mongodb-network
hostname: "mongodb"
ports:
- "27017:27017"
restart: "unless-stopped"
volumes:
- "./mongodb-data:/data/db"
entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs" ]
mongoinit:
image: "mongo:7.0"
container_name: "mongodb_replSet_initializer"
restart: "on-failure" # WITHOUT THIS, the initializer tries only once and fails
depends_on:
- mongodb
networks:
- mongodb-network
command: >
mongosh --host mongodb:27017 --eval "rs.initiate({
_id: \"rs\",
members: [
{_id: 0, host: \"mongodb\"}
]
})"

View File

@@ -25,7 +25,7 @@ services:
- mongo
mongo:
image: mongo:3.6
image: mongo:7.0
ports:
- "27017:27017"
networks:

View File

@@ -5,8 +5,8 @@ import path from 'path';
import babel from 'gulp-babel';
import os from 'os';
import fs from 'fs';
import spawn from 'cross-spawn'; // eslint-disable-line import/no-extraneous-dependencies
import clean from 'rimraf';
/* import spawn from 'cross-spawn'; // eslint-disable-line import/no-extraneous-dependencies
import clean from 'rimraf'; */
gulp.task('build:babel:server', () => gulp.src('website/server/**/*.js')
.pipe(babel())
@@ -51,7 +51,7 @@ gulp.task('build:prepare-mongo', async () => {
console.log('MongoDB data folder is missing, setting up.'); // eslint-disable-line no-console
// use run-rs without --keep, kill it as soon as the replica set starts
const runRsProcess = spawn('run-rs', ['-v', '4.1.1', '-l', 'ubuntu1804', '--dbpath', 'mongodb-data', '--number', '1', '--quiet']);
/* const runRsProcess = spawn('node', ['../scripts/start-local-mongo.mjs --build-db']);
for await (const chunk of runRsProcess.stdout) {
const stringChunk = chunk.toString();
@@ -79,6 +79,8 @@ gulp.task('build:prepare-mongo', async () => {
throw new Error(`Error running run-rs: ${error}`);
}
*/
});
gulp.task('build:dev', gulp.series(

View File

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

View File

@@ -59,6 +59,11 @@ gulp.task('test:prepare:mongo', cb => {
const mongooseOptions = getDefaultConnectionOptions();
const connectionUrl = getDevelopmentConnectionUrl(TEST_DB_URI);
console.info({
mongooseOptions,
connectionUrl,
});
mongoose.connect(connectionUrl, mongooseOptions)
.then(() => mongoose.connection.dropDatabase())
.then(() => mongoose.connection.close()).then(() => {

View File

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

View File

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

1451
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "5.26.0",
"version": "5.28.8",
"main": "./website/server/index.js",
"dependencies": {
"@babel/core": "^7.22.10",
@@ -15,6 +15,7 @@
"amplitude": "^6.0.0",
"apidoc": "^0.54.0",
"apple-auth": "^1.0.9",
"babel-preset-env": "^1.7.0",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"bootstrap": "^4.6.2",
@@ -35,6 +36,7 @@
"got": "^11.8.6",
"gulp": "^4.0.0",
"gulp-babel": "^8.0.0",
"gulp-filter": "^7.0.0",
"gulp-imagemin": "^7.1.0",
"gulp-nodemon": "^2.5.0",
"gulp.spritesmith": "^6.13.0",
@@ -74,6 +76,7 @@
"useragent": "^2.1.9",
"uuid": "^9.0.0",
"validator": "^13.11.0",
"webpack-bundle-analyzer": "^4.10.2",
"winston": "^3.10.0",
"winston-loggly-bulk": "^3.3.0",
"xml2js": "^0.6.2"
@@ -104,24 +107,28 @@
"client:build": "cd website/client && npm run build",
"client:unit": "cd website/client && npm run test:unit",
"start": "gulp nodemon",
"start:simple": "node ./website/server/index.js",
"debug": "gulp nodemon --inspect",
"mongo:dev": "run-rs -v 5.0.23 -l ubuntu1804 --keep --dbpath mongodb-data --number 1 --quiet",
"mongo:dev_": "node scripts/start-local-mongo.mjs",
"mongo:dev": "docker compose -f docker-compose.mongo-local.yml up",
"mongo:dev:down": "docker compose -f docker-compose.mongo-local.yml down",
"mongo:test": "node scripts/start-local-mongo.mjs --test-db",
"postinstall": "git config --global url.\"https://\".insteadOf git:// && gulp build && cd website/client && npm install",
"apidoc": "gulp apidoc",
"heroku-postbuild": "npm run client:build"
"heroku-postbuild": ".heroku/report_deploy.sh"
},
"devDependencies": {
"axios": "^1.4.0",
"axios": "^1.7.4",
"chai": "^4.3.7",
"chai-as-promised": "^7.1.1",
"chai-moment": "^0.1.0",
"chalk": "^5.3.0",
"cross-spawn": "^7.0.3",
"mocha": "^5.1.1",
"mongodb-memory-server": "^10.1.2",
"monk": "^7.3.4",
"nyc": "^15.1.0",
"require-again": "^2.0.0",
"run-rs": "^0.7.7",
"sinon-chai": "^3.7.0",
"sinon-stub-promise": "^4.0.0"
}

View File

@@ -0,0 +1,54 @@
/* eslint-disable-file */
import { MongoMemoryReplSet } from 'mongodb-memory-server';
import { dirname } from 'node:path';
import { existsSync, mkdirSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
const TEST_MODE = process.argv.includes('--test-db');
const BUILD_MODE = process.argv.includes('--build-db');
const __dirname = dirname(fileURLToPath(import.meta.url));
const pathSuffix = TEST_MODE ? '-test' : BUILD_MODE ? '-build' : '';
const mongoDbPath = `${__dirname}/../mongodb-data`+pathSuffix;
if (!existsSync(mongoDbPath)) {
mkdirSync(mongoDbPath);
}
(async () => {
// async code in where
// This will create an new instance of "MongoMemoryServer" and automatically start it
const mongod = await MongoMemoryReplSet.create({
replSet: {
count: 3,
dbName: TEST_MODE ? 'habitica-test' : 'habitica-dev',
dbPath: mongoDbPath,
name: 'rs',
},
instanceOpts: [
{
port: 27017,
},
{
port: 27018,
},
{
port: 27019,
},
],
dispose: {
cleanup: !BUILD_MODE && !TEST_MODE,
}
});
const uri = mongod.getUri();
console.log(`MongoDB running at ${uri}`, mongod.state);
// Keep the Node.js process running
setInterval(() => {}, 1000);
})();

View File

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

View File

@@ -3,6 +3,7 @@ import {
createAndPopulateGroup,
translate as t,
} from '../../../../helpers/api-integration/v3';
import { model as Group } from '../../../../../website/server/models/group';
describe('GET /groups/:groupId/chat', () => {
let user;
@@ -37,4 +38,34 @@ describe('GET /groups/:groupId/chat', () => {
});
});
});
context('public Guild', () => {
let group;
before(async () => {
({ group } = await createAndPopulateGroup({
groupDetails: {
name: 'test group',
type: 'guild',
privacy: 'private',
},
members: 1,
upgradeToGroupPlan: true,
chat: [
'Hello',
'Welcome to the Guild',
],
}));
// Creation API is shut down, we need to simulate an extant public group
await Group.updateOne({ _id: group._id }, { $set: { privacy: 'public' }, $unset: { 'purchased.plan': 1 } }).exec();
});
it('returns error if user attempts to fetch a sunset Guild', async () => {
await expect(user.get(`/groups/${group._id}/chat`)).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('featureRetired'),
});
});
});
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ import {
import QUEST_PETS from '../../website/common/script/content/quests/pets';
import QUEST_HATCHINGPOTIONS from '../../website/common/script/content/quests/potions';
import QUEST_BUNDLES from '../../website/common/script/content/bundles';
import { premium } from '../../website/common/script/content/hatching-potions';
import potions from '../../website/common/script/content/hatching-potions';
import SPELLS from '../../website/common/script/content/spells';
import QUEST_SEASONAL from '../../website/common/script/content/quests/seasonal';
@@ -18,12 +18,19 @@ function validateMatcher (matcher, checkedDate) {
describe('Content Schedule', () => {
let switchoverTime;
let clock;
beforeEach(() => {
switchoverTime = nconf.get('CONTENT_SWITCHOVER_TIME_OFFSET') || 0;
clearCachedMatchers();
});
afterEach(() => {
if (clock) {
clock.restore();
}
});
it('assembles scheduled items on january 15th', () => {
const date = new Date('2024-01-15');
const matchers = getAllScheduleMatchingGroups(date);
@@ -105,8 +112,14 @@ describe('Content Schedule', () => {
expect(matchers.backgrounds.end).to.eql(moment.utc(`2024-05-07T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
it('sets the end date if its on the release day', () => {
const date = new Date('2024-05-07T07:00:00.000Z');
it('sets the end date if its on the release day before switchover', () => {
const date = new Date('2024-05-07T07:00:00.000+00:00');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.backgrounds.end).to.eql(moment.utc(`2024-05-07T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
it('sets the end date if its on the release day after switchover', () => {
const date = new Date('2024-05-07T09:00:00.000+00:00');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.backgrounds.end).to.eql(moment.utc(`2024-06-07T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
@@ -123,12 +136,54 @@ describe('Content Schedule', () => {
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2024-06-21T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
it('sets the end date for a winter gala', () => {
const date = new Date('2024-12-22');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2025-03-21T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
});
it('uses correct date for first hours of the month', () => {
// if the date is checked before CONTENT_SWITCHOVER_TIME_OFFSET,
// it should be considered the previous month
const date = new Date('2024-05-01T02:00:00.000Z');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.petQuests.items).to.contain('snake');
expect(matchers.petQuests.items).to.not.contain('horse');
expect(matchers.timeTravelers.match('202304'), '202304').to.be.true;
expect(matchers.timeTravelers.match('202404'), '202404').to.be.false;
expect(matchers.timeTravelers.match('202305'), '202305').to.be.false;
});
it('uses correct date after switchover time', () => {
// if the date is checked after CONTENT_SWITCHOVER_TIME_OFFSET,
// it should be considered the current
const date = new Date('2024-05-01T09:00:00.000Z');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.petQuests.items).to.contain('snake');
expect(matchers.petQuests.items).to.not.contain('horse');
expect(matchers.timeTravelers.match('202304'), '202304').to.be.false;
expect(matchers.timeTravelers.match('202305'), '202305').to.be.true;
expect(matchers.timeTravelers.match('202405'), '202405').to.be.false;
});
it('uses UTC timezone', () => {
// if the date is checked after CONTENT_SWITCHOVER_TIME_OFFSET,
// it should be considered the current
clock = sinon.useFakeTimers(new Date('2024-05-01T05:00:00.000-04:00'));
const matchers = getAllScheduleMatchingGroups();
expect(matchers.petQuests.items).to.contain('snake');
expect(matchers.petQuests.items).to.not.contain('horse');
expect(matchers.timeTravelers.match('202304'), '202304').to.be.false;
expect(matchers.timeTravelers.match('202305'), '202305').to.be.true;
expect(matchers.timeTravelers.match('202405'), '202405').to.be.false;
});
it('contains content for repeating events', () => {
const date = new Date('2024-04-15');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.premiumHatchingPotions).to.exist;
expect(matchers.premiumHatchingPotions.items.length).to.equal(4);
expect(matchers.premiumHatchingPotions.items.indexOf('Garden')).to.not.equal(-1);
expect(matchers.premiumHatchingPotions.items.length).to.equal(5);
expect(matchers.premiumHatchingPotions.items.indexOf('Veggie')).to.not.equal(-1);
expect(matchers.premiumHatchingPotions.items.indexOf('Porcelain')).to.not.equal(-1);
});
@@ -167,7 +222,7 @@ describe('Content Schedule', () => {
});
it('premium hatching potions', () => {
const potionKeys = Object.keys(premium);
const potionKeys = Object.keys(potions.premium);
Object.keys(MONTHLY_SCHEDULE).forEach(key => {
const monthlyPotions = MONTHLY_SCHEDULE[key][21].find(item => item.type === 'premiumHatchingPotions');
for (const potion of monthlyPotions.items) {
@@ -245,27 +300,33 @@ describe('Content Schedule', () => {
it('allows sets matching the month', () => {
const date = new Date('2024-07-08');
const matcher = getAllScheduleMatchingGroups(date).timeTravelers;
expect(matcher.match('202307')).to.be.true;
expect(matcher.match('202207')).to.be.true;
expect(matcher.match('202307'), '202307').to.be.true;
expect(matcher.match('202207'), '202207').to.be.true;
});
it('disallows sets not matching the month', () => {
const date = new Date('2024-07-08');
const matcher = getAllScheduleMatchingGroups(date).timeTravelers;
expect(matcher.match('202306')).to.be.false;
expect(matcher.match('202402')).to.be.false;
expect(matcher.match('202306'), '202306').to.be.false;
expect(matcher.match('202402'), '202402').to.be.false;
});
it('disallows sets from current month', () => {
const date = new Date('2024-07-08');
const matcher = getAllScheduleMatchingGroups(date).timeTravelers;
expect(matcher.match('202407')).to.be.false;
expect(matcher.match('202407'), '202407').to.be.false;
});
it('disallows sets from the future', () => {
const date = new Date('2024-07-08');
const matcher = getAllScheduleMatchingGroups(date).backgrounds;
expect(matcher.match('202507')).to.be.false;
const matcher = getAllScheduleMatchingGroups(date).timeTravelers;
expect(matcher.match('202507'), '202507').to.be.false;
});
it('matches sets released in the earlier half of the year', () => {
const date = new Date('2024-07-08');
const matcher = getAllScheduleMatchingGroups(date).timeTravelers;
expect(matcher.match('202401'), '202401').to.be.true;
});
});
});

View File

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

View File

@@ -21,6 +21,7 @@ export async function getProperty (collectionName, id, path) {
// Specifically helpful for the GET /groups tests,
// resets the db to an empty state and creates a tavern document
export async function resetHabiticaDB () {
console.info('Resetting Habitica DB');
const groups = mongoose.connection.db.collection('groups');
const users = mongoose.connection.db.collection('users');
return mongoose.connection.dropDatabase()

View File

@@ -23,7 +23,6 @@
"bootstrap": "^4.6.0",
"bootstrap-vue": "^2.23.1",
"core-js": "^3.33.1",
"dompurify": "^3.0.3",
"eslint": "7.32.0",
"eslint-config-habitrpg": "6.2.0",
"eslint-plugin-mocha": "5.3.0",
@@ -39,7 +38,6 @@
"sass": "^1.63.4",
"sass-loader": "^14.1.1",
"sinon": "^17.0.1",
"smartbanner.js": "^1.19.3",
"stopword": "^2.0.8",
"timers-browserify": "^2.0.12",
"uuid": "^9.0.1",
@@ -59,7 +57,7 @@
"chai": "^5.1.0",
"inspectpack": "^4.7.1",
"terser-webpack-plugin": "^5.3.10",
"webpack": "^5.89.0"
"webpack": "^5.94.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -2307,15 +2305,6 @@
"@types/json-schema": "*"
}
},
"node_modules/@types/eslint-scope": {
"version": "3.7.7",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
"integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
"dependencies": {
"@types/eslint": "*",
"@types/estree": "*"
}
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
@@ -3129,9 +3118,9 @@
"integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA=="
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
"integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz",
"integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==",
"dependencies": {
"@webassemblyjs/helper-numbers": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6"
@@ -3148,9 +3137,9 @@
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q=="
},
"node_modules/@webassemblyjs/helper-buffer": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
"integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA=="
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz",
"integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw=="
},
"node_modules/@webassemblyjs/helper-numbers": {
"version": "1.11.6",
@@ -3168,14 +3157,14 @@
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA=="
},
"node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
"integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz",
"integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6"
"@webassemblyjs/wasm-gen": "1.12.1"
}
},
"node_modules/@webassemblyjs/ieee754": {
@@ -3200,26 +3189,26 @@
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA=="
},
"node_modules/@webassemblyjs/wasm-edit": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
"integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz",
"integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/helper-wasm-section": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6",
"@webassemblyjs/wasm-opt": "1.11.6",
"@webassemblyjs/wasm-parser": "1.11.6",
"@webassemblyjs/wast-printer": "1.11.6"
"@webassemblyjs/helper-wasm-section": "1.12.1",
"@webassemblyjs/wasm-gen": "1.12.1",
"@webassemblyjs/wasm-opt": "1.12.1",
"@webassemblyjs/wasm-parser": "1.12.1",
"@webassemblyjs/wast-printer": "1.12.1"
}
},
"node_modules/@webassemblyjs/wasm-gen": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
"integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz",
"integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
"@webassemblyjs/leb128": "1.11.6",
@@ -3227,22 +3216,22 @@
}
},
"node_modules/@webassemblyjs/wasm-opt": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
"integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz",
"integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6",
"@webassemblyjs/wasm-parser": "1.11.6"
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/wasm-gen": "1.12.1",
"@webassemblyjs/wasm-parser": "1.12.1"
}
},
"node_modules/@webassemblyjs/wasm-parser": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
"integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz",
"integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-api-error": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
@@ -3251,11 +3240,11 @@
}
},
"node_modules/@webassemblyjs/wast-printer": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
"integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz",
"integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@xtuc/long": "4.2.2"
}
},
@@ -3326,10 +3315,10 @@
"node": ">=0.4.0"
}
},
"node_modules/acorn-import-assertions": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
"integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
"node_modules/acorn-import-attributes": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
"integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
"peerDependencies": {
"acorn": "^8"
}
@@ -4046,11 +4035,11 @@
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dependencies": {
"fill-range": "^7.0.1"
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@@ -5398,11 +5387,6 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/dompurify": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz",
"integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w=="
},
"node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
@@ -5496,9 +5480,9 @@
}
},
"node_modules/enhanced-resolve": {
"version": "5.15.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
"integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
"version": "5.17.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
"integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -6871,9 +6855,9 @@
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -9008,11 +8992,11 @@
}
},
"node_modules/micromatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dependencies": {
"braces": "^3.0.2",
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
@@ -12095,17 +12079,6 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/smartbanner.js": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/smartbanner.js/-/smartbanner.js-1.22.0.tgz",
"integrity": "sha512-JhERLgwEPuzVdwAHds1J6txWBVq9BwmlAn+5VicrAfIOMO3ehNA7VHu8IIJNnW1LsElSCaLWxjdLjlEwLDqAvA==",
"engines": {
"node": ">=10.24.1 <22.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ain"
}
},
"node_modules/sockjs": {
"version": "0.3.24",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
@@ -13342,9 +13315,9 @@
}
},
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
"dependencies": {
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.1.2"
@@ -13378,33 +13351,32 @@
}
},
"node_modules/webpack": {
"version": "5.89.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
"integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
"version": "5.94.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
"integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
"dependencies": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^1.0.0",
"@webassemblyjs/ast": "^1.11.5",
"@webassemblyjs/wasm-edit": "^1.11.5",
"@webassemblyjs/wasm-parser": "^1.11.5",
"@types/estree": "^1.0.5",
"@webassemblyjs/ast": "^1.12.1",
"@webassemblyjs/wasm-edit": "^1.12.1",
"@webassemblyjs/wasm-parser": "^1.12.1",
"acorn": "^8.7.1",
"acorn-import-assertions": "^1.9.0",
"browserslist": "^4.14.5",
"acorn-import-attributes": "^1.9.5",
"browserslist": "^4.21.10",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.15.0",
"enhanced-resolve": "^5.17.1",
"es-module-lexer": "^1.2.1",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.2.9",
"graceful-fs": "^4.2.11",
"json-parse-even-better-errors": "^2.3.1",
"loader-runner": "^4.2.0",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
"schema-utils": "^3.2.0",
"tapable": "^2.1.1",
"terser-webpack-plugin": "^5.3.7",
"watchpack": "^2.4.0",
"terser-webpack-plugin": "^5.3.10",
"watchpack": "^2.4.1",
"webpack-sources": "^3.2.3"
},
"bin": {

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

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

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 B

View File

@@ -174,6 +174,30 @@
}
}
.btn-warning {
background: $orange-10;
color: $white !important;
&:hover:not(:disabled):not(.disabled) {
background: $orange-100;
color: $white;
}
&:focus {
background: $orange-10;
border-color: $purple-400;
}
&:not(:disabled):not(.disabled):active:focus, &:not(:disabled):not(.disabled).active:focus {
box-shadow: none;
border-color: $purple-400;
}
&:not(:disabled):not(.disabled):active, &:not(:disabled):not(.disabled).active {
background: $orange-10;
}
}
.btn-success {
background: $green-50;
border: 1px solid transparent;

View File

@@ -78,3 +78,15 @@ $gold-color: #FFA624;
$hourglass-color: #2995CD;
$purple-task: #925cf3;
.gray-200 {
color: $gray-200 !important;
}
.purple-300 {
color: $purple-300 !important;
}
.white {
color: $white !important;
}

View File

@@ -23,16 +23,14 @@
{{ $t('foundNewItems') }}
</h2>
<div class="d-flex justify-content-center">
<div
<Sprite
class="item-box ml-auto mr-3"
:class="eggClass"
>
</div>
<div
:image-name="eggClass"
/>
<Sprite
class="item-box mr-auto"
:class="potionClass"
>
</div>
:image-name="potionClass"
/>
</div>
<p
v-once
@@ -103,8 +101,12 @@
<script>
import closeIcon from '@/assets/svg/close.svg';
import Sprite from '@/components/ui/sprite.vue';
export default {
components: {
Sprite,
},
data () {
return {
icons: Object.freeze({

View File

@@ -19,10 +19,10 @@
</div>
<div class="inner-content">
<div class="achievement-background d-flex align-items-center">
<div
<Sprite
class="icon"
:class="achievementClass"
></div>
:image-name="achievementClass"
/>
</div>
<h4
class="title"
@@ -99,8 +99,12 @@
import achievements from '@/../../common/script/content/achievements';
import { mapState } from '@/libs/store';
import svgClose from '@/assets/svg/close.svg';
import Sprite from '@/components/ui/sprite.vue';
export default {
components: {
Sprite,
},
props: ['data'],
data () {
return {

View File

@@ -10,10 +10,10 @@
@hide="hide"
>
<div class="modal-body text-center">
<div
<Sprite
class="quest"
:class="`quest_${user.party.quest.completed}`"
></div>
:image-name="`quest_${user.party.quest.completed}`"
/>
<p
v-if="questData.completion && typeof questData.completion === 'function'"
v-html="questData.completion()"
@@ -58,10 +58,12 @@ import percent from '@/../../common/script/libs/percent';
import { MAX_HEALTH as maxHealth } from '@/../../common/script/constants';
import { mapState } from '@/libs/store';
import QuestRewards from '../shops/quests/questRewards';
import Sprite from '../ui/sprite';
export default {
components: {
QuestRewards,
Sprite,
},
data () {
return {

View File

@@ -11,10 +11,11 @@
</div>
<div class="modal-body">
<div class="pull-right-sm text-center">
<div
class="col-centered"
:class="`quest_${quests[user.party.quest.key].key}`"
></div>
<div class="col-centered">
<Sprite
:image-name="`quest_${quests[user.party.quest.key].key}`"
/>
</div>
<div ng-if="quests[user.party.quest.key].boss">
<h4>{{ quests[user.party.quest.key].boss.name() }}</h4>
<p>
@@ -93,8 +94,12 @@ import * as quests from '@/../../common/script/content/quests';
import percent from '@/../../common/script/libs/percent';
import { MAX_HEALTH as maxHealth } from '@/../../common/script/constants';
import { mapState } from '@/libs/store';
import Sprite from '@/components/ui/sprite';
export default {
components: [
Sprite,
],
data () {
return {
maxHealth,

View File

@@ -1,30 +1,41 @@
<template>
<div class="row standard-page">
<div class="well col-12">
<div class="row standard-page col-12 d-flex justify-content-center">
<div class="admin-panel-content">
<h1>Admin Panel</h1>
<div>
<form
class="form-inline"
@submit.prevent="loadHero(userIdentifier)"
>
<form
class="form-inline"
@submit.prevent="searchUsers(userIdentifier)"
>
<div class="input-group col pl-0 pr-0">
<input
v-model="userIdentifier"
class="form-control uidField"
class="form-control"
type="text"
:placeholder="'User ID or Username; blank for your account'"
:placeholder="'UserID, username, email, or leave blank for your account'"
>
<input
type="submit"
value="Load User"
class="btn btn-secondary"
>
</form>
</div>
<div class="input-group-append">
<button
class="btn btn-primary"
type="button"
@click="loadUser(userIdentifier)"
>
Load User
</button>
<button
class="btn btn-secondary"
type="button"
@click="searchUsers(userIdentifier)"
>
Search
</button>
</div>
</div>
</form>
<div>
<router-view @changeUserIdentifier="changeUserIdentifier" />
</div>
<router-view
class="mt-3"
@changeUserIdentifier="changeUserIdentifier"
/>
</div>
</div>
</template>
@@ -33,6 +44,15 @@
.uidField {
min-width: 45ch;
}
.input-group-append {
width:auto;
}
.admin-panel-content {
flex: 0 0 800px;
max-width: 800px;
}
</style>
<script>
@@ -62,7 +82,24 @@ export default {
// (useful if we want to re-fetch the user after making changes).
this.userIdentifier = newId;
},
async loadHero (userIdentifier) {
async searchUsers (userIdentifier) {
if (!userIdentifier || userIdentifier === '') {
this.loadUser();
return;
}
this.$router.push({
name: 'adminPanelSearch',
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();
}
});
},
async loadUser (userIdentifier) {
const id = userIdentifier || this.user._id;
this.$router.push({

View File

@@ -0,0 +1,155 @@
<template>
<div>
<div
v-if="noUsersFound"
class="alert alert-warning"
role="alert"
>
Could not find any matching users.
</div>
<loading-spinner class="mx-auto mb-2" dark-color="true" v-if="isSearching" />
<div
v-if="users.length > 0"
class="list-group"
>
<a
v-for="user in users"
:key="user._id"
href="#"
class="list-group-item list-group-item-action"
@click="loadUser(user._id)"
>
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{{ user.profile.name }}</h5>
<small>{{ user._id }}</small>
</div>
<p
class="mb-1"
:class="{'highlighted-value': matchValueToIdentifier(user.auth.local.username)}"
>
@{{ user.auth.local.username }}</p>
<p class="mb-0">
<span
v-for="email in userEmails(user)"
:key="email"
:class="{'highlighted-value': matchValueToIdentifier(email)}"
>
{{ email }}
</span>
</p>
</a>
</div>
</div>
</template>
<style lang="scss" scoped>
.highlighted-value {
font-weight: bold;
}
</style>
<script>
import VueRouter from 'vue-router';
import { mapState } from '@/libs/store';
import LoadingSpinner from '../ui/loadingSpinner';
const { isNavigationFailure, NavigationFailureType } = VueRouter;
export default {
components: {
LoadingSpinner,
},
data () {
return {
userIdentifier: '',
users: [],
noUsersFound: false,
isSearching: false,
};
},
computed: {
...mapState({ user: 'user.data' }),
},
beforeRouteUpdate (to, from, next) {
this.userIdentifier = to.params.userIdentifier;
next();
},
watch: {
userIdentifier () {
this.isSearching = true;
this.$store.dispatch('adminPanel:searchUsers', { userIdentifier: this.userIdentifier }).then(users => {
this.isSearching = false;
if (users.length === 1) {
this.loadUser(users[0]._id);
} else {
const matchIndex = users.findIndex(user => this.isExactMatch(user));
if (matchIndex !== -1) {
users.splice(0, 0, users.splice(matchIndex, 1)[0]);
}
this.users = users;
this.noUsersFound = users.length === 0;
}
});
this.$emit('changeUserIdentifier', this.userIdentifier); // change user identifier in Admin Panel's form
},
},
mounted () {
this.userIdentifier = this.$route.params.userIdentifier;
},
methods: {
matchValueToIdentifier (value) {
return value.toLowerCase().includes(this.userIdentifier.toLowerCase());
},
userEmails (user) {
const allEmails = [];
if (user.auth.local.email) allEmails.push(user.auth.local.email);
if (user.auth.google && user.auth.google.emails) {
const emails = user.auth.google.emails;
allEmails.push(...this.findSocialEmails(emails));
}
if (user.auth.apple && user.auth.apple.emails) {
const emails = user.auth.apple.emails;
allEmails.push(...this.findSocialEmails(emails));
}
if (user.auth.facebook && user.auth.facebook.emails) {
const emails = user.auth.facebook.emails;
allEmails.push(...this.findSocialEmails(emails));
}
return allEmails;
},
findSocialEmails (emails) {
if (typeof emails === 'string') return [emails];
if (Array.isArray(emails)) return emails.map(email => email.value);
if (typeof emails === 'object') return [emails.value];
return [];
},
async loadUser (userIdentifier) {
const id = userIdentifier || this.user._id;
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();
}
});
},
isExactMatch (user) {
return user._id === this.userIdentifier
|| user.auth.local.username === this.userIdentifier
|| (user.auth.google && user.auth.google.emails && user.auth.google.emails.findIndex(
email => email.value === this.userIdentifier,
) !== -1)
|| (user.auth.apple && user.auth.apple.emails && user.auth.apple.emails.findIndex(
email => email.value === this.userIdentifier,
) !== -1)
|| (user.auth.facebook && user.auth.facebook.emails && user.auth.facebook.emails.findIndex(
email => email.value === this.userIdentifier,
) !== -1);
},
},
};
</script>

View File

@@ -1,13 +1,18 @@
<template>
<div class="accordion-group">
<h3
class="expand-toggle"
:class="{'open': expand}"
@click="expand = !expand"
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{'open': expand}"
@click="expand = !expand"
>
Achievements
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
Achievements
</h3>
<div v-if="expand">
<ul>
<li
v-for="item in achievements"

View File

@@ -1,13 +1,18 @@
<template>
<div class="accordion-group">
<h3
class="expand-toggle"
:class="{'open': expand}"
@click="expand = !expand"
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{'open': expand}"
@click="expand = !expand"
>
Current Avatar Appearance, Drop Count Today
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
Current Avatar Appearance, Drop Count Today
</h3>
<div v-if="expand">
<div>Drops Today: {{ items.lastDrop.count }}</div>
<div>Most Recent Drop: {{ items.lastDrop.date | formatDate }}</div>
<div>Use Costume: {{ preferences.costume ? 'on' : 'off' }}</div>

View File

@@ -1,160 +1,129 @@
<template>
<div class="accordion-group">
<h3
class="expand-toggle"
:class="{'open': expand}"
@click="expand = !expand"
>
Contributor Details
</h3>
<div v-if="expand">
<form @submit.prevent="saveHero({hero, msg: 'Contributor details', clearData: true})">
<div>
<label>Permissions</label>
<div class="checkbox">
<label>
<input
v-model="hero.permissions.fullAccess"
:disabled="!hasPermission(user, 'fullAccess')"
type="checkbox"
>
Full Admin Access (Allows access to everything. EVERYTHING)
</label>
</div>
<div class="checkbox">
<label>
<input
v-model="hero.permissions.userSupport"
:disabled="!hasPermission(user, 'fullAccess')"
type="checkbox"
>
User Support (Access this form, access purchase history)
</label>
</div>
<div class="checkbox">
<label>
<input
v-model="hero.permissions.news"
:disabled="!hasPermission(user, 'fullAccess')"
type="checkbox"
>
News poster (Bailey CMS)
</label>
</div>
<div class="checkbox">
<label>
<input
v-model="hero.permissions.moderator"
:disabled="!hasPermission(user, 'fullAccess')"
type="checkbox"
>
Community Moderator (ban and mute users, access chat flags, manage social spaces)
</label>
</div>
<div class="checkbox">
<label>
<input
v-model="hero.permissions.challengeAdmin"
:disabled="!hasPermission(user, 'fullAccess')"
type="checkbox"
>
Challenge Admin (can create official habitica challenges and admin all challenges)
</label>
</div>
<div class="checkbox">
<label>
<input
v-model="hero.permissions.coupons"
:disabled="!hasPermission(user, 'fullAccess')"
type="checkbox"
>
Coupon Creator (can manage coupon codes)
</label>
</div>
</div>
<div class="form-group">
<label>Title</label>
<input
v-model="hero.contributor.text"
class="form-control textField"
type="text"
>
<small>
Common titles:
<strong>Ambassador, Artisan, Bard, Blacksmith, Challenger, Comrade, Fletcher,
Linguist, Linguistic Scribe, Scribe, Socialite, Storyteller</strong>.
<br>
Rare titles:
Advisor, Chamberlain, Designer, Mathematician, Shirtster, Spokesperson,
Statistician, Tinker, Transcriber, Troubadour.
</small>
</div>
<div class="form-group form-inline">
<label>Tier</label>
<input
v-model="hero.contributor.level"
class="form-control levelField"
type="number"
>
<small>
1-7 for normal contributors, 8 for moderators, 9 for staff.
This determines which items, pets, mounts are available, and name-tag coloring.
Tiers 8 and 9 are automatically given admin status.
</small>
</div>
<div
v-if="hero.secret.text"
class="form-group"
<form @submit.prevent="saveHero({ hero, msg: 'Contributor details', clearData: true })">
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{ 'open': expand }"
@click="expand = !expand"
>
<label>Moderation Notes</label>
Contributor Details
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
<div class="mb-4">
<h3 class="mt-0">
Permissions
</h3>
<div
v-markdown="hero.secret.text"
class="markdownPreview"
></div>
v-for="permission in permissionList"
:key="permission.key"
class="col-sm-9 offset-sm-3"
>
<div class="custom-control custom-checkbox">
<input
v-model="hero.permissions[permission.key]"
:disabled="!hasPermission(user, permission.key)"
class="custom-control-input"
type="checkbox"
>
<label class="custom-control-label">
{{ permission.name }}<br>
<small class="text-secondary">{{ permission.description }}</small>
</label>
</div>
</div>
</div>
<div class="form-group">
<label>Contributions</label>
<textarea
v-model="hero.contributor.contributions"
class="form-control"
cols="5"
rows="5"
></textarea>
<div
v-markdown="hero.contributor.contributions"
class="markdownPreview"
></div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Title</label>
<div class="col-sm-9">
<input
v-model="hero.contributor.text"
class="form-control textField"
type="text"
>
<small>
Common titles:
<strong>Ambassador, Artisan, Bard, Blacksmith, Challenger, Comrade, Fletcher,
Linguist, Linguistic Scribe, Scribe, Socialite, Storyteller</strong>.
<br>
Rare titles:
Advisor, Chamberlain, Designer, Mathematician, Shirtster, Spokesperson,
Statistician, Tinker, Transcriber, Troubadour.
</small>
</div>
</div>
<div class="form-group">
<label>Edit Moderation Notes</label>
<textarea
v-model="hero.secret.text"
class="form-control"
cols="5"
rows="3"
></textarea>
<div
v-markdown="hero.secret.text"
class="markdownPreview"
></div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Tier</label>
<div class="col-sm-9">
<input
v-model="hero.contributor.level"
class="form-control levelField"
type="number"
>
<small>
1-7 for normal contributors, 8 for moderators, 9 for staff.
This determines which items, pets, mounts are available, and name-tag coloring.
</small>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Contributions</label>
<div class="col-sm-9">
<textarea
v-model="hero.contributor.contributions"
class="form-control"
cols="5"
rows="5"
>
</textarea>
<div
v-markdown="hero.contributor.contributions"
class="markdownPreview"
></div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Moderation Notes</label>
<div class="col-sm-9">
<textarea
v-model="hero.secret.text"
class="form-control"
cols="5"
rows="3"
></textarea>
<div
v-markdown="hero.secret.text"
class="markdownPreview"
></div>
</div>
</div>
</div>
<div
v-if="expand"
class="card-footer"
>
<input
type="submit"
value="Save and Clear Data"
class="btn btn-primary"
value="Save"
class="btn btn-primary mt-1"
>
</form>
</div>
</div>
</div>
</form>
</template>
<style lang="scss" scoped>
.levelField {
min-width: 10ch;
}
.textField {
min-width: 50ch;
}
.levelField {
min-width: 10ch;
}
.textField {
min-width: 50ch;
}
</style>
<script>
@@ -164,6 +133,39 @@ import saveHero from '../mixins/saveHero';
import { mapState } from '@/libs/store';
import { userStateMixin } from '../../../mixins/userState';
const permissionList = [
{
key: 'fullAccess',
name: 'Full Admin Access',
description: 'Allows access to everything. EVERYTHING',
},
{
key: 'userSupport',
name: 'User Support',
description: 'Access this form, access purchase history',
},
{
key: 'news',
name: 'News Poster',
description: 'Bailey CMS',
},
{
key: 'moderator',
name: 'Community Moderator',
description: 'Ban and mute users, access chat flags, manage social spaces',
},
{
key: 'challengeAdmin',
name: 'Challenge Admin',
description: 'Can create official habitica challenges and admin all challenges',
},
{
key: 'coupons',
name: 'Coupon Creator',
description: 'Can manage coupon codes',
},
];
function resetData (self) {
self.expand = self.hero.contributor.level;
}
@@ -192,6 +194,7 @@ export default {
data () {
return {
expand: false,
permissionList,
};
},
watch: {

View File

@@ -1,145 +1,187 @@
<template>
<div class="accordion-group">
<h3
class="expand-toggle"
:class="{'open': expand}"
@click="expand = !expand"
>
Timestamps, Time Zone, Authentication, Email Address
<span
v-if="errorsOrWarningsExist"
>- ERRORS / WARNINGS EXIST</span>
</h3>
<div v-if="expand">
<p
v-if="errorsOrWarningsExist"
class="errorMessage"
<form @submit.prevent="saveHero({ hero, msg: 'Authentication' })">
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{'open': expand}"
@click="expand = !expand"
>
Timestamps, Time Zone, Authentication, Email Address
<span
v-if="errorsOrWarningsExist"
>- ERRORS / WARNINGS EXIST</span>
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
See error(s) below.
</p>
<div>
Account created:
<strong>{{ hero.auth.timestamps.created | formatDate }}</strong>
</div>
<div v-if="hero.flags.thirdPartyTools">
User has employed <strong>third party tools</strong>. Last known usage:
<strong>{{ hero.flags.thirdPartyTools | formatDate }}</strong>
</div>
<div v-if="cronError">
"lastCron" value:
<strong>{{ hero.lastCron | formatDate }}</strong>
<br>
<span class="errorMessage">
ERROR: cron probably crashed before finishing
("auth.timestamps.loggedin" and "lastCron" dates are different).
</span>
</div>
<div class="form-inline">
<div>
Most recent cron:
<strong>{{ hero.auth.timestamps.loggedin | formatDate }}</strong>
("auth.timestamps.loggedin")
</div>
<button
class="btn btn-primary ml-2"
@click="resetCron()"
<p
v-if="errorsOrWarningsExist"
class="errorMessage"
>
Reset Cron to Yesterday
</button>
</div>
<div class="subsection-start">
Time zone:
<strong>{{ hero.preferences.timezoneOffset | formatTimeZone }}</strong>
</div>
<div>
Custom Day Start time (CDS):
<strong>{{ hero.preferences.dayStart }}</strong>
</div>
<div v-if="timezoneDiffError || timezoneMissingError">
Time zone at previous cron:
<strong>{{ hero.preferences.timezoneOffsetAtLastCron | formatTimeZone }}</strong>
See error(s) below.
</p>
<div class="errorMessage">
<div v-if="timezoneDiffError">
ERROR: the player's current time zone is different than their time zone when
their previous cron ran. This can be because:
<ul>
<li>daylight savings started or stopped <sup>*</sup></li>
<li>the player changed zones due to travel <sup>*</sup></li>
<li>the player has devices set to different zones <sup>**</sup></li>
<li>the player uses a VPN with varying zones <sup>**</sup></li>
<li>something similarly unpleasant is happening. <sup>**</sup></li>
</ul>
<p>
<em>* The problem should fix itself in about a day.</em><br>
<em>** One of these causes is probably happening if the time zones stay
different for more than a day.</em>
</p>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Account created:</label>
<strong class="col-sm-9 col-form-label">
{{ hero.auth.timestamps.created | formatDate }}</strong>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Used third party tools:</label>
<div v-if="timezoneMissingError">
ERROR: One of the player's time zones is missing.
This is expected and okay if it's the "Time zone at previous cron"
AND if it's their first day in Habitica.
Otherwise an error has occurred.
<div class="col-sm-9 col-form-label">
<strong v-if="hero.flags.thirdPartyTools">
Yes - {{ hero.flags.thirdPartyTools | formatDate }}</strong>
<strong v-else>No</strong>
</div>
</div>
</div>
<div class="subsection-start form-inline">
API Token: &nbsp;
<form @submit.prevent="changeApiToken()">
<input
type="submit"
value="Change API Token"
class="btn btn-primary"
>
</form>
<div
v-if="tokenModified"
class="form-inline"
>
<strong>API Token has been changed. Tell the player something like this:</strong>
<div v-if="cronError" class="form-group row">
<label class="col-sm-3 col-form-label">lastCron value:</label>
<strong>{{ hero.lastCron | formatDate }}</strong>
<br>
I've given you a new API Token.
You'll need to log out of the website and mobile app then log back in
otherwise they won't work correctly.
If you have trouble logging out, for the website go to
https://habitica.com/static/clear-browser-data and click the red button there,
and for the Android app, clear its data.
For the iOS app, if you can't log out you might need to uninstall it,
reboot your phone, then reinstall it.
<span class="errorMessage">
ERROR: cron probably crashed before finishing
("auth.timestamps.loggedin" and "lastCron" dates are different).
</span>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Most recent cron:</label>
<div class="col-sm-9 col-form-label">
<strong>
{{ hero.auth.timestamps.loggedin | formatDate }}</strong>
<button
class="btn btn-warning btn-sm ml-4"
@click="resetCron()"
>
Reset Cron to Yesterday
</button>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Time zone:</label>
<strong class="col-sm-9 col-form-label">
{{ hero.preferences.timezoneOffset | formatTimeZone }}</strong>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Custom Day Start time (CDS)</label>
<div class="col-sm-9">
<input
v-model="hero.preferences.dayStart"
class="form-control levelField"
type="number"
>
</div>
</div>
<div v-if="timezoneDiffError || timezoneMissingError">
Time zone at previous cron:
<strong>{{ hero.preferences.timezoneOffsetAtLastCron | formatTimeZone }}</strong>
<div class="errorMessage">
<div v-if="timezoneDiffError">
ERROR: the player's current time zone is different than their time zone when
their previous cron ran. This can be because:
<ul>
<li>daylight savings started or stopped <sup>*</sup></li>
<li>the player changed zones due to travel <sup>*</sup></li>
<li>the player has devices set to different zones <sup>**</sup></li>
<li>the player uses a VPN with varying zones <sup>**</sup></li>
<li>something similarly unpleasant is happening. <sup>**</sup></li>
</ul>
<p>
<em>* The problem should fix itself in about a day.</em><br>
<em>** One of these causes is probably happening if the time zones stay
different for more than a day.</em>
</p>
</div>
<div v-if="timezoneMissingError">
ERROR: One of the player's time zones is missing.
This is expected and okay if it's the "Time zone at previous cron"
AND if it's their first day in Habitica.
Otherwise an error has occurred.
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">API Token</label>
<div class="col-sm-9">
<button
value="Change API Token"
class="btn btn-danger"
@click="changeApiToken()"
>
Change API Token
</button>
<div
v-if="tokenModified"
>
<strong>API Token has been changed. Tell the player something like this:</strong>
<br>
I've given you a new API Token.
You'll need to log out of the website and mobile app then log back in
otherwise they won't work correctly.
If you have trouble logging out, for the website go to
https://habitica.com/static/clear-browser-data and click the red button there,
and for the Android app, clear its data.
For the iOS app, if you can't log out you might need to uninstall it,
reboot your phone, then reinstall it.
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Local Authentication E-Mail</label>
<div class="col-sm-9">
<input
v-model="hero.auth.local.email"
class="form-control"
type="text"
>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Google authentication</label>
<div class="col-sm-9">
<pre v-if="authMethodExists('google')">{{ hero.auth.google }}</pre>
<span v-else><strong>None</strong></span>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Facebook authentication</label>
<div class="col-sm-9">
<pre v-if="authMethodExists('facebook')">{{ hero.auth.facebook }}</pre>
<span v-else><strong>None</strong></span>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Apple ID authentication</label>
<div class="col-sm-9">
<pre v-if="authMethodExists('apple')">{{ hero.auth.apple }}</pre>
<span v-else><strong>None</strong></span>
</div>
</div>
<div class="subsection-start">
Full "auth" object for checking above is correct:
<pre>{{ hero.auth }}</pre>
</div>
</div>
<div class="subsection-start">
Local authentication:
<span v-if="hero.auth.local.email">Yes, &nbsp;
<strong>{{ hero.auth.local.email }}</strong></span>
<span v-else><strong>None</strong></span>
</div>
<div>
Google authentication:
<pre v-if="authMethodExists('google')">{{ hero.auth.google }}</pre>
<span v-else><strong>None</strong></span>
</div>
<div>
Facebook authentication:
<pre v-if="authMethodExists('facebook')">{{ hero.auth.facebook }}</pre>
<span v-else><strong>None</strong></span>
</div>
<div>
Apple ID authentication:
<pre v-if="authMethodExists('apple')">{{ hero.auth.apple }}</pre>
<span v-else><strong>None</strong></span>
</div>
<div class="subsection-start">
Full "auth" object for checking above is correct:
<pre>{{ hero.auth }}</pre>
<div
v-if="expand"
class="card-footer"
>
<input
type="submit"
value="Save"
class="btn btn-primary mt-1"
>
</div>
</div>
</div>
</form>
</template>
<script>

View File

@@ -1,13 +1,18 @@
<template>
<div class="accordion-group">
<h3
class="expand-toggle"
:class="{'open': expand}"
@click="expand = !expand"
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{'open': expand}"
@click="expand = !expand"
>
Customizations
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
Customizations
</h3>
<div v-if="expand">
<div
v-for="itemType in itemTypes"
:key="itemType"

View File

@@ -1,13 +1,18 @@
<template>
<div class="accordion-group">
<h3
class="expand-toggle"
:class="{'open': expand}"
@click="expand = !expand"
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{'open': expand}"
@click="expand = !expand"
>
Items
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
Items
</h3>
<div v-if="expand">
<div>
The sections below display each item's key (bolded if the player has ever owned it),
followed by the item's English name.

View File

@@ -1,16 +1,21 @@
<template>
<div class="accordion-group">
<h3
class="expand-toggle"
:class="{'open': expand}"
@click="expand = !expand"
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{'open': expand}"
@click="expand = !expand"
>
Party, Quest
<span
v-if="errorsOrWarningsExist"
>- ERRORS / WARNINGS EXIST</span>
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
Party, Quest
<span
v-if="errorsOrWarningsExist"
>- ERRORS / WARNINGS EXIST</span>
</h3>
<div v-if="expand">
<div
v-if="errorsOrWarningsExist"
class="errorMessage"

View File

@@ -1,87 +1,132 @@
<template>
<div class="accordion-group">
<h3
class="expand-toggle"
:class="{'open': expand}"
@click="expand = !expand"
>
Privileges, Gem Balance
</h3>
<div v-if="expand">
<p
v-if="errorsOrWarningsExist"
class="errorMessage"
<form @submit.prevent="saveHero({hero, msg: 'Privileges or Gems or Moderation Notes'})">
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{'open': expand}"
@click="expand = !expand"
>
Priviliges, Gem Balance
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
Player has had privileges removed or has moderation notes.
</p>
<form @submit.prevent="saveHero({hero, msg: 'Privileges or Gems or Moderation Notes'})">
<div class="checkbox">
<label>
<input
v-if="hero.flags"
v-model="hero.flags.chatShadowMuted"
type="checkbox"
> Shadow Mute
</label>
<p
v-if="errorsOrWarningsExist"
class="errorMessage"
>
Player has had privileges removed or has moderation notes.
</p>
<div
v-if="hero.flags"
class="form-group row"
>
<div class="col-sm-9 offset-sm-3">
<div class="custom-control custom-checkbox">
<input
id="chatShadowMuted"
v-model="hero.flags.chatShadowMuted"
class="custom-control-input"
type="checkbox"
>
<label
class="custom-control-label"
for="chatShadowMuted"
>
Shadow Mute
</label>
</div>
</div>
</div>
<div class="checkbox">
<label>
<input
v-if="hero.flags"
v-model="hero.flags.chatRevoked"
type="checkbox"
> Mute (Revoke Chat Privileges)
</label>
<div
v-if="hero.flags"
class="form-group row"
>
<div class="col-sm-9 offset-sm-3">
<div class="custom-control custom-checkbox">
<input
id="chatRevoked"
v-model="hero.flags.chatRevoked"
class="custom-control-input"
type="checkbox"
>
<label
class="custom-control-label"
for="chatRevoked"
>
Mute (Revoke Chat Privileges)
</label>
</div>
</div>
</div>
<div class="checkbox">
<label>
<input
v-model="hero.auth.blocked"
type="checkbox"
> Ban / Block
</label>
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
<div class="custom-control custom-checkbox">
<input
id="blocked"
v-model="hero.auth.blocked"
class="custom-control-input"
type="checkbox"
>
<label
class="custom-control-label"
for="blocked"
>
Ban / Block
</label>
</div>
</div>
</div>
<div class="form-inline">
<label>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Balance
</label>
<div class="col-sm-9">
<input
v-model="hero.balance"
class="form-control balanceField"
type="number"
step="0.25"
>
</label>
<span>
<small>
Balance is in USD, not in Gems.
E.g., if this number is 1, it means 4 Gems.
Arrows change Balance by 0.25 (i.e., 1 Gem per click).
Do not use when awarding tiers; tier gems are automatic.
</small>
</span>
</div>
</div>
<div class="form-group">
<label>Moderation Notes</label>
<textarea
v-model="hero.secret.text"
class="form-control"
cols="5"
rows="5"
></textarea>
<div
v-markdown="hero.secret.text"
class="markdownPreview"
></div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Moderation Notes</label>
<div class="col-sm-9">
<textarea
v-model="hero.secret.text"
class="form-control"
cols="5"
rows="5"
></textarea>
<div
v-markdown="hero.secret.text"
class="markdownPreview"
></div>
</div>
</div>
</div>
<div
v-if="expand"
class="card-footer"
>
<input
type="submit"
value="Save"
class="btn btn-primary"
class="btn btn-primary mt-1"
>
</form>
</div>
</div>
</div>
</form>
</template>
<style lang="scss" scoped>

View File

@@ -1,14 +1,19 @@
<template>
<div class="accordion-group">
<h3
class="expand-toggle"
:class="{'open': expand}"
@click="expand = !expand"
>
Subscription, Monthly Perks
</h3>
<div v-if="expand">
<form @submit.prevent="saveHero({ hero, msg: 'Subscription Perks' })">
<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>
@@ -23,46 +28,72 @@
</div>
<div
v-if="hero.purchased.plan.dateCreated"
class="form-inline"
class="form-group row"
>
<label>
<label class="col-sm-3 col-form-label">
Creation date:
<input
v-model="hero.purchased.plan.dateCreated"
class="form-control"
type="text"
> <strong class="ml-2">{{ dateFormat(hero.purchased.plan.dateCreated) }}</strong>
</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-inline"
class="form-group row"
>
<label>
Start date for current subscription type:
<input
v-model="hero.purchased.plan.dateCurrentTypeCreated"
class="form-control"
type="text"
>
<label class="col-sm-3 col-form-label">
Current sub start date:
</label>
<strong class="ml-2">{{ dateFormat(hero.purchased.plan.dateCurrentTypeCreated) }}</strong>
<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-inline">
<label>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Termination date:
<div>
</label>
<div class="col-sm-9">
<div class="input-group">
<input
v-model="hero.purchased.plan.dateTerminated"
class="form-control"
type="text"
> <strong class="ml-2">{{ dateFormat(hero.purchased.plan.dateTerminated) }}</strong>
>
<div class="input-group-append">
<strong class="input-group-text">
{{ dateFormat(hero.purchased.plan.dateTerminated) }}
</strong>
</div>
</div>
</label>
</div>
</div>
<div class="form-inline">
<label>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Consecutive months:
</label>
<div class="col-sm-9">
<input
v-model="hero.purchased.plan.consecutive.count"
class="form-control"
@@ -70,11 +101,13 @@
min="0"
step="1"
>
</label>
</div>
</div>
<div class="form-inline">
<label>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Perk offset months:
</label>
<div class="col-sm-9">
<input
v-model="hero.purchased.plan.consecutive.offset"
class="form-control"
@@ -82,26 +115,34 @@
min="0"
step="1"
>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Perk month count:
</label>
<div class="col-sm-9">
<input
v-model="hero.purchased.plan.perkMonthCount"
class="form-control"
type="number"
min="0"
max="2"
step="1"
>
</div>
</div>
<div class="form-inline">
Perk month count:
<input
v-model="hero.purchased.plan.perkMonthCount"
class="form-control"
type="number"
min="0"
max="2"
step="1"
>
<div class="form-group row">
<label class="col-sm-3 col-form-label">
Next Mystic Hourglass:
</label>
<strong class="col-sm-9 col-form-label">{{ nextHourglassDate }}</strong>
</div>
<div>
Next Mystic Hourglass:
<strong>{{ nextHourglassDate }}</strong>
</div>
<div class="form-inline">
<label>
<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"
@@ -109,11 +150,13 @@
min="0"
step="1"
>
</label>
</div>
</div>
<div class="form-inline">
<label>
<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"
@@ -122,15 +165,21 @@
max="25"
step="5"
>
</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) + 25 }}
</strong>
</div>
<div>
Total Gem cap:
<strong>{{ Number(hero.purchased.plan.consecutive.gemCapExtra) + 25 }}</strong>
</div>
<div class="form-inline">
<label>
<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"
@@ -139,43 +188,64 @@
:max="hero.purchased.plan.consecutive.gemCapExtra + 25"
step="1"
>
</label>
</div>
</div>
<div
v-if="hero.purchased.plan.extraMonths > 0"
>
<div v-if="hero.purchased.plan.extraMonths > 0">
Additional credit (applied upon cancellation):
<strong>{{ hero.purchased.plan.extraMonths }}</strong>
</div>
<div>
Mystery Items:
<span
v-if="hero.purchased.plan.mysteryItems.length > 0"
>
<span
v-for="(item, index) in hero.purchased.plan.mysteryItems"
:key="index"
>
<strong v-if="index < hero.purchased.plan.mysteryItems.length - 1">
{{ item }},
</strong>
<strong v-else> {{ item }} </strong>
<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>
<span v-else>
<strong>None</strong>
</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"
>
</form>
</div>
</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';

View File

@@ -1,13 +1,18 @@
<template>
<div class="accordion-group">
<h3
class="expand-toggle"
:class="{'open': expand}"
@click="toggleTransactionsOpen"
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{'open': expand}"
@click="toggleTransactionsOpen"
>
Transactions
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
Transactions
</h3>
<div v-if="expand">
<purchase-history-table
:gem-transactions="gemTransactions"
:hourglass-transactions="hourglassTransactions"

View File

@@ -1,52 +1,66 @@
<template>
<div class="accordion-group">
<h3
class="expand-toggle"
:class="{'open': expand}"
@click="expand = !expand"
>
Users Profile
</h3>
<div v-if="expand">
<form @submit.prevent="saveHero({hero, msg: 'Users Profile'})">
<div class="form-group">
<label>Display name</label>
<input
v-model="hero.profile.name"
class="form-control textField"
type="text"
>
<form @submit.prevent="saveHero({hero, msg: 'Users Profile'})">
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{'open': expand}"
@click="expand = !expand"
>
User Profile
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Display name</label>
<div class="col-sm-9">
<input
v-model="hero.profile.name"
class="form-control"
type="text"
>
</div>
</div>
<div class="form-group">
<label>Photo URL</label>
<input
v-model="hero.profile.imageUrl"
class="form-control textField"
type="text"
>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Photo URL</label>
<div class="col-sm-9">
<input
v-model="hero.profile.imageUrl"
class="form-control"
type="text"
>
</div>
</div>
<div class="form-group">
<label>About</label>
<div class="row about-row">
<div class="form-group row">
<label class="col-sm-3 col-form-label">About</label>
<div class="col-sm-9">
<textarea
v-model="hero.profile.blurb"
class="form-control col"
class="form-control"
rows="10"
></textarea>
<div
v-markdown="hero.profile.blurb"
class="markdownPreview col"
class="markdownPreview"
></div>
</div>
</div>
</div>
<div
v-if="expand"
class="card-footer"
>
<input
type="submit"
value="Save"
class="btn btn-primary"
class="btn btn-primary mt-1"
>
</form>
</div>
</div>
</div>
</form>
</template>
<style lang="scss" scoped>

View File

@@ -1,7 +1,6 @@
<template>
<div>
<buy-gems-modal v-if="user" />
<!--modify-inventory(v-if="isUserLoaded")-->
<footer>
<!-- Product -->
<div class="product">
@@ -22,7 +21,7 @@
</a>
</li>
<li>
<router-link to="/group-plans">
<router-link :to="user ? '/group-plans' : '/static/group-plans'">
{{ $t('groupPlans') }}
</router-link>
</li>
@@ -291,7 +290,8 @@
</div>
<div
v-if="TIME_TRAVEL_ENABLED && user.permissions && user.permissions.fullAccess"
class="time-travel"
v-if="TIME_TRAVEL_ENABLED && user?.permissions?.fullAccess"
:key="lastTimeJump"
>
<a
@@ -309,9 +309,11 @@
<div class="my-2">
Time Traveling! It is {{ new Date().toLocaleDateString() }}
<a
class="btn btn-warning mr-1"
class="btn btn-warning btn-small"
@click="resetTime()"
>Reset</a>
>
Reset
</a>
</div>
<a
class="btn btn-secondary mr-1"
@@ -399,6 +401,10 @@
tooltip="+1000 to boss quests. 300 items to collection quests"
@click="addQuestProgress()"
>Quest Progress Up</a>
<a
class="btn btn-secondary"
@click="bossRage()"
>+ Boss Rage 😡</a>
<a
class="btn btn-secondary"
@click="makeAdmin()"
@@ -506,6 +512,8 @@ li {
grid-area: debug-pop;
}
.time-travel { grid-area: time-travel;}
footer {
background-color: $gray-500;
color: $gray-50;
@@ -526,7 +534,8 @@ footer {
"donate-text donate-text donate-text donate-button social"
"hr hr hr hr hr"
"copyright copyright melior privacy-terms privacy-terms"
"debug-toggle debug-toggle debug-toggle blank blank";
"time-travel time-travel time-travel time-travel time-travel"
"debug-toggle debug-toggle debug-toggle debug-toggle debug-toggle";
grid-template-columns: repeat(5, 1fr);
grid-template-rows: auto;
@@ -730,6 +739,7 @@ h3 {
"privacy-policy privacy-policy"
"mobile-terms mobile-terms"
"melior melior"
"time-travel time-travel"
"debug-toggle debug-toggle";
grid-template-columns: repeat(2, 2fr);
grid-template-rows: auto;
@@ -960,6 +970,10 @@ export default {
// @TODO: Notification.text('Quest progress increased');
// @TODO: User.sync();
},
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!

View File

@@ -224,7 +224,7 @@
<script>
import hello from 'hellojs';
import debounce from 'lodash/debounce';
import isEmail from 'validator/lib/isEmail';
import isEmail from 'validator/es/lib/isEmail';
import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants';
import { setUpAxios, buildAppleAuthUrl } from '@/libs/auth';
import googleIcon from '@/assets/svg/google.svg';

View File

@@ -607,11 +607,10 @@
import axios from 'axios';
import hello from 'hellojs';
import debounce from 'lodash/debounce';
import isEmail from 'validator/lib/isEmail';
import DOMPurify from 'dompurify';
import isEmail from 'validator/es/lib/isEmail';
import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants';
import { buildAppleAuthUrl } from '../../libs/auth';
import sanitizeRedirect from '@/mixins/sanitizeRedirect';
import exclamation from '@/assets/svg/exclamation.svg';
import gryphon from '@/assets/svg/gryphon.svg';
import habiticaIcon from '@/assets/svg/logo-horizontal.svg';
@@ -619,6 +618,7 @@ import googleIcon from '@/assets/svg/google.svg';
import appleIcon from '@/assets/svg/apple_black.svg';
export default {
mixins: [sanitizeRedirect],
data () {
const data = {
username: '',
@@ -747,11 +747,6 @@ export default {
}
});
}, 500),
sanitizeRedirect (redirect) {
if (!redirect) return '/';
const sanitizedString = DOMPurify.sanitize(redirect).replace(/\\|\/\/|\./g, '');
return sanitizedString;
},
async register () {
// @TODO do not use alert
if (!this.email) {

View File

@@ -6,6 +6,7 @@
<div
v-for="option in items"
:key="option.key"
:id="option.imageName"
class="outer-option-background"
:class="{
premium: Boolean(option.gem),
@@ -14,18 +15,28 @@
hide: option.hide }"
@click="option.click(option)"
>
<b-popover
:target="option.imageName"
triggers="hover focus"
placement="bottom"
:prevent-overflow="false"
>
<strong> {{ option.text }} </strong>
</b-popover>
<div class="option">
<div
class="sprite customize-option"
:class="option.class"
>
<Sprite
v-if="!option.none"
class="sprite"
:prefix="option.isGear ? 'shop' : 'icon'"
:imageName="option.imageName"
:image-name="option.imageName"
/>
<div
v-if="option.none"
v-else
class="redline-outer"
>
<div class="redline"></div>
</div>
</div>
</div>
</div>
</div>
@@ -35,8 +46,12 @@
import gem from '@/assets/svg/gem.svg';
import gold from '@/assets/svg/gold.svg';
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
import Sprite from '@/components/ui/sprite.vue';
export default {
components: {
Sprite,
},
mixins: [
avatarEditorUtilities,
],
@@ -75,7 +90,7 @@ export default {
cursor: pointer;
&.premium {
height: 112px;
height: 120px;
width: 96px;
margin-left: 8px;
margin-right: 8px;
@@ -92,21 +107,9 @@ export default {
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
background-color: $white;
.sprite.customize-option.shirt {
margin-left: -3px !important;
// otherwise its overriden by the .outer-option-background:not(.none) { rules
}
.sprite.customize-option.skin {
margin-left: -8px !important;
// otherwise its overriden by the .outer-option-background:not(.none) { rules
}
.option {
border: none;
border-radius: 2px;
padding-left: 6px;
padding-top: 4px;
}
&:hover {
@@ -132,14 +135,14 @@ export default {
}
.redline-outer {
height: 60px;
width: 60px;
height: 68px;
width: 68px;
position: absolute;
bottom: 0;
margin: 0 auto 0 0;
.redline {
width: 60px;
width: 68px;
height: 4px;
display: block;
background: red;
@@ -148,7 +151,6 @@ export default {
top: 0;
margin-top: 30px;
margin-bottom: 20px;
margin-left: -1px;
}
}
}
@@ -164,10 +166,9 @@ export default {
}
.option {
vertical-align: bottom;
height: 64px;
width: 64px;
height: 76px;
width: 76px;
margin: 12px 8px;
border: 4px solid transparent;
border-radius: 10px;
position: relative;
@@ -182,44 +183,6 @@ export default {
.sprite.customize-option {
margin-top: 0;
margin-left: 0;
&.skin {
margin-top: -4px;
margin-left: -4px;
}
&.chair {
margin-left: -1px;
margin-top: -1px;
&.button_chair_black {
// different sprite margin?
margin-top: -3px;
}
&.handleless {
margin-left: -5px;
margin-top: -5px;
}
}
&.color, &.bangs, &.beard, &.flower, &.mustache {
background-position-x: -6px;
background-position-y: -12px;
}
&.hair.base {
background-position-x: -6px;
background-position-y: -4px;
}
&.headAccessory {
margin-top: 0;
margin-left: -4px;
}
&.headband {
margin-top: -6px;
margin-left: -27px;
}
}
}
</style>

View File

@@ -75,6 +75,7 @@
<script>
import appearance from '@/../../common/script/content/appearance';
import upperFirst from 'lodash/upperFirst';
import { subPageMixin } from '../../mixins/subPage';
import { userStateMixin } from '../../mixins/userState';
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
@@ -82,9 +83,6 @@ import customizeBanner from './customize-banner';
import customizeOptions from './customize-options';
import subMenu from './sub-menu';
const freeShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price === 0);
const specialShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price !== 0);
export default {
components: {
customizeBanner,
@@ -106,17 +104,6 @@ export default {
headAccessory: ['bearEars', 'cactusEars', 'foxEars', 'lionEars', 'pandaEars', 'pigEars', 'tigerEars', 'wolfEars'],
},
chairKeys: ['none', 'black', 'blue', 'green', 'pink', 'red', 'yellow', 'handleless_black', 'handleless_blue', 'handleless_green', 'handleless_pink', 'handleless_red', 'handleless_yellow'],
specialShirtKeys,
items: [
{
id: 'size',
label: this.$t('size'),
},
{
id: 'shirt',
label: this.$t('shirt'),
},
],
};
},
computed: {
@@ -167,6 +154,7 @@ export default {
];
const noneOption = this.createGearItem(0, 'eyewear', 'base');
noneOption.none = true;
noneOption.text = this.$t('none');
const options = [
noneOption,
];
@@ -178,42 +166,36 @@ export default {
option.active = this.user.preferences.costume
? this.user.items.gear.costume.eyewear === newKey
: this.user.items.gear.equipped.eyewear === newKey;
option.class = `eyewear_special_${key}`;
option.imageName = `eyewear_special_${key}`;
option.isGear = true;
option.click = () => {
const type = this.user.preferences.costume ? 'costume' : 'equipped';
return this.equip(newKey, type);
};
option.text = this.$t(`eyewearSpecial${upperFirst(key)}Text`);
options.push(option);
}
return options;
},
freeShirts () {
return freeShirtKeys.map(s => this.mapKeysToFreeOption(s, 'shirt'));
},
specialShirts () {
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
const keys = this.specialShirtKeys;
const options = keys.map(key => this.mapKeysToOption(key, 'shirt'));
return options;
},
headbands () {
const keys = ['blackHeadband', 'blueHeadband', 'greenHeadband', 'pinkHeadband', 'redHeadband', 'whiteHeadband', 'yellowHeadband'];
const noneOption = this.createGearItem(0, 'headAccessory', 'base', 'headband');
const noneOption = this.createGearItem(0, 'headAccessory', 'base');
noneOption.none = true;
noneOption.text = this.$t('none');
const options = [
noneOption,
];
for (const key of keys) {
const option = this.createGearItem(key, 'headAccessory', 'special', 'headband');
const option = this.createGearItem(key, 'headAccessory', 'special');
const newKey = `headAccessory_special_${key}`;
option.click = () => {
const type = this.user.preferences.costume ? 'costume' : 'equipped';
return this.equip(newKey, type);
};
option.text = this.$t(`headAccessory${upperFirst(key)}Text`);
options.push(option);
}
@@ -227,8 +209,9 @@ export default {
option.none = true;
}
option.active = this.user.preferences.chair === key;
option.class = `button_chair_${key} chair ${key.includes('handleless_') ? 'handleless' : ''}`;
option.imageName = `chair_${key}`;
option.click = () => this.set({ 'preferences.chair': key });
option.text = appearance.chair[key].text();
return option;
});
return options;
@@ -242,8 +225,11 @@ export default {
option.none = true;
}
option.active = this.user.preferences.hair.flower === key;
option.class = `icon_hair_flower_${key} flower`;
if (key !== 0) {
option.imageName = `hair_flower_${key}`;
}
option.click = () => this.set({ 'preferences.hair.flower': key });
option.text = appearance.hair.flower[key].text();
return option;
});
return options;
@@ -271,6 +257,7 @@ export default {
const noneOption = this.createGearItem(0, category, 'base', category);
noneOption.none = true;
noneOption.text = this.$t('none');
const options = [
noneOption,
];
@@ -284,10 +271,15 @@ export default {
option.active = this.user.preferences.costume
? this.user.items.gear.costume[category] === newKey
: this.user.items.gear.equipped[category] === newKey;
option.class = `headAccessory_special_${option.key} ${category}`;
if (category === 'back') {
option.class = `icon_back_special_${option.key} back`;
option.text = this.$t(`back${upperFirst(key)}Text`);
option.imageName = `back_special_${option.key}`;
} else {
option.text = this.$t(`headAccessory${upperFirst(key)}Text`);
option.imageName = `headAccessory_special_${option.key}`;
}
option.isGear = true;
option.click = () => {
const type = this.user.preferences.costume ? 'costume' : 'equipped';
return this.equip(newKey, type);
@@ -303,7 +295,7 @@ export default {
return keys.join(',');
},
createGearItem (key, gearType, subGearType, additionalClass) {
createGearItem (key, gearType, subGearType) {
const newKey = `${gearType}_${subGearType ? `${subGearType}_` : ''}${key}`;
const option = {};
option.key = key;
@@ -311,6 +303,7 @@ export default {
const currentlyEquippedValue = this.user.items.gear[visibleGearType][gearType];
option.active = currentlyEquippedValue === newKey;
option.isGear = true;
if (key === 0) {
// if key is the "none" option check if a property
@@ -318,7 +311,7 @@ export default {
option.active = option.active || !currentlyEquippedValue;
}
option.class = `${newKey} ${additionalClass}`;
option.imageName = `${newKey}`;
option.click = () => {
const type = this.user.preferences.costume ? 'costume' : 'equipped';
const currentlyEquipped = this.user.items.gear[type][gearType];

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