mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
Add Customizations and Achievements to admin panel (#15114)
* allow owned customizations to be edited in admin panel * Allow subscription termination date to be edited more flexibly * begin adding achievements to admin panel page * better display for customizations in admin panel * allow achievements to be modified in admin panel * fix lint * fix errors * Improve how achievements, customizations and items are listed in admin panel * fix naming * fix lint error * Fix issues with achievements in admin panel and add some tests * handle some edgecases better * Fix lint * Fix sort/search on member selection modal (#15066) * fix(birthday): correct birthday robe ownership check * feat(content): add February items (#15090) * update(content): add February 2024 items * feat(content): add October content * feat(content):update February Content * feat(content): finish up February content * fix(backgrounds): tweak consistency * fix(strings): remove extra whitespace * fix(event): add missing Valentine features --------- Co-authored-by: Sabe Jones <sabrecat@gmail.com> Co-authored-by: Sabe Jones <sabe@habitica.com> * 5.17.0 * Translated using Weblate (Ukrainian) Currently translated at 63.6% (1918 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (189 of 189 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (94 of 94 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (131 of 131 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (61 of 61 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (427 of 427 strings) Translated using Weblate (Spanish) Currently translated at 88.5% (2668 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (239 of 239 strings) Translated using Weblate (Bulgarian) Currently translated at 15.3% (21 of 137 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.9% (748 of 764 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 88.0% (96 of 109 strings) Translated using Weblate (Bulgarian) Currently translated at 98.9% (187 of 189 strings) Translated using Weblate (Bulgarian) Currently translated at 59.8% (503 of 840 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 99.6% (282 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Spanish) Currently translated at 87.7% (2643 of 3013 strings) Translated using Weblate (Spanish) Currently translated at 87.7% (2643 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.9% (748 of 764 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (427 of 427 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 98.3% (235 of 239 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (239 of 239 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.9% (748 of 764 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (47 of 47 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (189 of 189 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (22 of 22 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (8 of 8 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 48.1% (66 of 137 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (54 of 54 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.9% (748 of 764 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (109 of 109 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 97.8% (822 of 840 strings) Deleted translation using Weblate (Chinese (Simplified) (zh_HK)) Deleted translation using Weblate (Chinese (Simplified) (zh_HK)) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (113 of 113 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (22 of 22 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (239 of 239 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (256 of 256 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (256 of 256 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (256 of 256 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (140 of 140 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Spanish) Currently translated at 94.2% (215 of 228 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (56 of 56 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (94 of 94 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (113 of 113 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (131 of 131 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (8 of 8 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (61 of 61 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 98.5% (279 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (22 of 22 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 91.3% (390 of 427 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (239 of 239 strings) Translated using Weblate (Italian) Currently translated at 94.5% (226 of 239 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 37.9% (52 of 137 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 38.6% (53 of 137 strings) Translated using Weblate (Italian) Currently translated at 1.4% (2 of 137 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (15 of 15 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.9% (748 of 764 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 56.0% (51 of 91 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (376 of 376 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (189 of 189 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (158 of 158 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (256 of 256 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (256 of 256 strings) Translated using Weblate (Japanese) Currently translated at 98.5% (2970 of 3013 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Japanese) Currently translated at 99.2% (424 of 427 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (109 of 109 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Ukrainian) Currently translated at 63.0% (1900 of 3013 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (109 of 109 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 97.7% (821 of 840 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (427 of 427 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 94.4% (2847 of 3013 strings) Translated using Weblate (Ukrainian) Currently translated at 62.9% (1897 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (239 of 239 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (376 of 376 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (189 of 189 strings) Translated using Weblate (Ukrainian) Currently translated at 99.0% (108 of 109 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (840 of 840 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.8% (223 of 228 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 94.4% (2845 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 37.9% (52 of 137 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (140 of 140 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.8% (223 of 228 strings) Translated using Weblate (Spanish (Latin America)) Currently translated at 91.9% (148 of 161 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 94.4% (2845 of 3013 strings) Translated using Weblate (Korean) Currently translated at 2.1% (3 of 137 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (13 of 13 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (2 of 2 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (1 of 1 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.9% (748 of 764 strings) Translated using Weblate (Spanish) Currently translated at 60.4% (55 of 91 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (94 of 94 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.9% (748 of 764 strings) Translated using Weblate (Italian) Currently translated at 98.6% (225 of 228 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (94 of 94 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 94.2% (2841 of 3013 strings) Translated using Weblate (Russian) Currently translated at 18.9% (26 of 137 strings) Translated using Weblate (Italian) Currently translated at 93.9% (789 of 840 strings) Translated using Weblate (Italian) Currently translated at 100.0% (161 of 161 strings) Translated using Weblate (Russian) Currently translated at 75.7% (194 of 256 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 94.2% (2841 of 3013 strings) Translated using Weblate (Spanish (Latin America)) Currently translated at 91.3% (147 of 161 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 94.2% (2841 of 3013 strings) Translated using Weblate (Spanish) Currently translated at 59.3% (54 of 91 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (47 of 47 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (840 of 840 strings) Translated using Weblate (Italian) Currently translated at 98.1% (158 of 161 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (161 of 161 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (140 of 140 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (56 of 56 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (113 of 113 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (131 of 131 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (8 of 8 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 94.2% (2841 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (239 of 239 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (182 of 182 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.9% (748 of 764 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (840 of 840 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (256 of 256 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (140 of 140 strings) Translated using Weblate (Spanish) Currently translated at 92.1% (210 of 228 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (56 of 56 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (13 of 13 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (61 of 61 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (61 of 61 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 98.5% (279 of 283 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (427 of 427 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 94.0% (2835 of 3013 strings) Translated using Weblate (Ukrainian) Currently translated at 62.8% (1894 of 3013 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (224 of 224 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (182 of 182 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 21.1% (29 of 137 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (764 of 764 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (47 of 47 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (376 of 376 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (109 of 109 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (840 of 840 strings) Translated using Weblate (Spanish) Currently translated at 78.1% (200 of 256 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.1% (816 of 840 strings) Co-authored-by: Alberto Pesquera <dashmilel@gmail.com> Co-authored-by: Alcatraz Huo <alrcatraz@gmail.com> Co-authored-by: Alessandro Losi <pipipe550@hotmail.com> Co-authored-by: Delta S <deseji93@gmail.com> Co-authored-by: Dimitar Kraev <dimkraeff@gmail.com> Co-authored-by: Finrod <963505255@qq.com> Co-authored-by: Gean Ribeiro <geanribeirok@gmail.com> Co-authored-by: Ike Osenberg <ike.osenberg@gmail.com> Co-authored-by: Jaime Martí <jaumemarti77@icloud.com> Co-authored-by: Jerry Chen <minecjraft@qq.com> Co-authored-by: Kedr <sergeysamori.ua@gmail.com> Co-authored-by: Nikita Maximov <ruvemaximus@gmail.com> Co-authored-by: Omar Bertolla <scaram@icloud.com> Co-authored-by: Phillip Thelen <phillip@habitica.com> Co-authored-by: Roberto Tramontano <roberto.tramontano1@gmail.com> Co-authored-by: TOMA Mitsuru <toma0001@gmail.com> Co-authored-by: Vinicius Rodrigues <suburbanizar@gmail.com> Co-authored-by: Weblate <noreply@weblate.org> Co-authored-by: nelly <nellychopyuk@gmail.com> Co-authored-by: tony <duzhe163908@gmail.com> Co-authored-by: 이수진 <govl09876@naver.com> Translate-URL: https://translate.habitica.com/projects/habitica/achievements/es/ Translate-URL: https://translate.habitica.com/projects/habitica/achievements/es_419/ Translate-URL: https://translate.habitica.com/projects/habitica/achievements/it/ Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/bg/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/it/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt_BR/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/uk/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/challenge/es/ Translate-URL: https://translate.habitica.com/projects/habitica/challenge/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/challenge/pt_BR/ Translate-URL: https://translate.habitica.com/projects/habitica/challenge/uk/ Translate-URL: https://translate.habitica.com/projects/habitica/challenge/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/character/bg/ Translate-URL: https://translate.habitica.com/projects/habitica/character/uk/ Translate-URL: https://translate.habitica.com/projects/habitica/character/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/es/ Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/content/es/ Translate-URL: https://translate.habitica.com/projects/habitica/content/uk/ Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/contrib/es/ Translate-URL: https://translate.habitica.com/projects/habitica/contrib/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/death/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/bg/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/ko/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/ru/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/front/es/ Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/uk/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/generic/es/ Translate-URL: https://translate.habitica.com/projects/habitica/generic/it/ Translate-URL: https://translate.habitica.com/projects/habitica/generic/pt_BR/ Translate-URL: https://translate.habitica.com/projects/habitica/generic/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/es/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/uk/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/inventory/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/limited/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/limited/pt_BR/ Translate-URL: https://translate.habitica.com/projects/habitica/limited/uk/ 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/merch/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/messages/es/ Translate-URL: https://translate.habitica.com/projects/habitica/messages/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/noscript/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/npc/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/overview/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/pets/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/quests/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/es/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/es/ Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/es/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/ru/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/spells/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/it/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/uk/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/tasks/es/ Translate-URL: https://translate.habitica.com/projects/habitica/tasks/zh_Hans/ Translation: Habitica/Achievements Translation: Habitica/Backgrounds Translation: Habitica/Challenge Translation: Habitica/Character Translation: Habitica/Communityguidelines Translation: Habitica/Content Translation: Habitica/Contrib Translation: Habitica/Death Translation: Habitica/Defaulttasks Translation: Habitica/Faq Translation: Habitica/Front Translation: Habitica/Gear Translation: Habitica/Generic Translation: Habitica/Groups Translation: Habitica/Inventory Translation: Habitica/Limited Translation: Habitica/Loginincentives Translation: Habitica/Merch Translation: Habitica/Messages Translation: Habitica/Noscript Translation: Habitica/Npc Translation: Habitica/Overview Translation: Habitica/Pets Translation: Habitica/Quests Translation: Habitica/Questscontent Translation: Habitica/Rebirth Translation: Habitica/Settings Translation: Habitica/Spells Translation: Habitica/Subscriber Translation: Habitica/Tasks * fix(content): fix February background release date * 5.17.1 * chore(repo): remove duplicate file * chore(migrations): move various files to archive and remove erroneous comment bars * Translated using Weblate (Portuguese) Currently translated at 96.4% (109 of 113 strings) Translated using Weblate (Portuguese) Currently translated at 97.1% (816 of 840 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 98.9% (2982 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 98.9% (2982 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 98.0% (749 of 764 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 98.8% (2978 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 98.2% (2961 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 98.0% (749 of 764 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.9% (2951 of 3013 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (56 of 56 strings) Translated using Weblate (Korean) Currently translated at 58.0% (1748 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (56 of 56 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.9% (2951 of 3013 strings) Translated using Weblate (Spanish) Currently translated at 92.1% (2777 of 3013 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.7% (101 of 137 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (47 of 47 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.9% (2951 of 3013 strings) Translated using Weblate (French) Currently translated at 98.8% (2978 of 3013 strings) Translated using Weblate (Spanish) Currently translated at 90.9% (2741 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (376 of 376 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.2% (2871 of 3013 strings) Translated using Weblate (French) Currently translated at 98.5% (2969 of 3013 strings) Translated using Weblate (French) Currently translated at 100.0% (183 of 183 strings) Translated using Weblate (Spanish) Currently translated at 32.8% (45 of 137 strings) Translated using Weblate (French) Currently translated at 100.0% (109 of 109 strings) Translated using Weblate (French) Currently translated at 100.0% (840 of 840 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.2% (2989 of 3013 strings) Translated using Weblate (Spanish) Currently translated at 90.5% (2729 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Spanish) Currently translated at 90.5% (2729 of 3013 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.0% (2983 of 3013 strings) Translated using Weblate (Spanish) Currently translated at 89.7% (2703 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Spanish) Currently translated at 89.4% (2695 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 98.2% (2959 of 3013 strings) Translated using Weblate (Spanish) Currently translated at 89.1% (2685 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (113 of 113 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Spanish) Currently translated at 93.9% (266 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (183 of 183 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 98.0% (749 of 764 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (258 of 258 strings) Translated using Weblate (Spanish (Latin America)) Currently translated at 91.9% (148 of 161 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Spanish) Currently translated at 99.4% (182 of 183 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (376 of 376 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (258 of 258 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (258 of 258 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (228 of 228 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (94 of 94 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (131 of 131 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (283 of 283 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (427 of 427 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 95.1% (2867 of 3013 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 97.3% (2932 of 3013 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (239 of 239 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (183 of 183 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 98.0% (749 of 764 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (91 of 91 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (376 of 376 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (189 of 189 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (109 of 109 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (840 of 840 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 98.0% (749 of 764 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (140 of 140 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (94 of 94 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (61 of 61 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (183 of 183 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (183 of 183 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 97.9% (748 of 764 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 88.0% (96 of 109 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (840 of 840 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (258 of 258 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.2% (189 of 258 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.6% (837 of 840 strings) Co-authored-by: Delta S <deseji93@gmail.com> Co-authored-by: Finrod <963505255@qq.com> Co-authored-by: Gean Ribeiro <geanribeirok@gmail.com> Co-authored-by: Icaro <icaro.mascarenhas@outlook.com> Co-authored-by: Jaime Martí <jaumemarti77@icloud.com> Co-authored-by: Natalie Luhrs <eilatan@gmail.com> Co-authored-by: Quim Martínez Lara <quimml60@gmail.com> Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com> Co-authored-by: Weblate <noreply@weblate.org> Co-authored-by: 김경은 <kekim.lang@gmail.com> Translate-URL: https://translate.habitica.com/projects/habitica/achievements/es_419/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt_BR/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/challenge/fr/ Translate-URL: https://translate.habitica.com/projects/habitica/challenge/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/character/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/es/ Translate-URL: https://translate.habitica.com/projects/habitica/content/es/ Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/contrib/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/es/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/ Translate-URL: https://translate.habitica.com/projects/habitica/front/es/ Translate-URL: https://translate.habitica.com/projects/habitica/front/fr/ Translate-URL: https://translate.habitica.com/projects/habitica/front/pt_BR/ Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/ko/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt_BR/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/generic/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/limited/es/ Translate-URL: https://translate.habitica.com/projects/habitica/limited/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/messages/pt_BR/ Translate-URL: https://translate.habitica.com/projects/habitica/npc/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/pets/pt/ Translate-URL: https://translate.habitica.com/projects/habitica/pets/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/quests/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/es/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/pt_BR/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/spells/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/spells/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/tasks/zh_Hans/ Translation: Habitica/Achievements Translation: Habitica/Backgrounds Translation: Habitica/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/Messages Translation: Habitica/Npc Translation: Habitica/Pets Translation: Habitica/Quests Translation: Habitica/Questscontent Translation: Habitica/Settings Translation: Habitica/Spells Translation: Habitica/Subscriber Translation: Habitica/Tasks --------- Co-authored-by: Leonardo Chappuis <40621126+leonardochappuis@users.noreply.github.com> Co-authored-by: Sabe Jones <sabe@habitica.com> Co-authored-by: Natalie <78037386+CuriousMagpie@users.noreply.github.com> Co-authored-by: Sabe Jones <sabrecat@gmail.com> Co-authored-by: Weblate <noreply@weblate.org> Co-authored-by: Alberto Pesquera <dashmilel@gmail.com> Co-authored-by: Alcatraz Huo <alrcatraz@gmail.com> Co-authored-by: Alessandro Losi <pipipe550@hotmail.com> Co-authored-by: Delta S <deseji93@gmail.com> Co-authored-by: Dimitar Kraev <dimkraeff@gmail.com> Co-authored-by: Finrod <963505255@qq.com> Co-authored-by: Gean Ribeiro <geanribeirok@gmail.com> Co-authored-by: Ike Osenberg <ike.osenberg@gmail.com> Co-authored-by: Jaime Martí <jaumemarti77@icloud.com> Co-authored-by: Jerry Chen <minecjraft@qq.com> Co-authored-by: Kedr <sergeysamori.ua@gmail.com> Co-authored-by: Nikita Maximov <ruvemaximus@gmail.com> Co-authored-by: Omar Bertolla <scaram@icloud.com> Co-authored-by: Roberto Tramontano <roberto.tramontano1@gmail.com> Co-authored-by: TOMA Mitsuru <toma0001@gmail.com> Co-authored-by: Vinicius Rodrigues <suburbanizar@gmail.com> Co-authored-by: nelly <nellychopyuk@gmail.com> Co-authored-by: tony <duzhe163908@gmail.com> Co-authored-by: 이수진 <govl09876@naver.com> Co-authored-by: CuriousMagpie <eilatan@gmail.com> Co-authored-by: Icaro <icaro.mascarenhas@outlook.com> Co-authored-by: Quim Martínez Lara <quimml60@gmail.com> Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com> Co-authored-by: 김경은 <kekim.lang@gmail.com>
This commit is contained in:
@@ -9,7 +9,7 @@ describe('GET /heroes/:heroId', () => {
|
||||
|
||||
const heroFields = [
|
||||
'_id', 'id', 'auth', 'balance', 'contributor', 'flags', 'items',
|
||||
'lastCron', 'party', 'preferences', 'profile', 'purchased', 'secret',
|
||||
'lastCron', 'party', 'preferences', 'profile', 'purchased', 'secret', 'achievements',
|
||||
];
|
||||
|
||||
before(async () => {
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('PUT /heroes/:heroId', () => {
|
||||
|
||||
const heroFields = [
|
||||
'_id', 'auth', 'balance', 'contributor', 'flags', 'items', 'lastCron',
|
||||
'party', 'preferences', 'profile', 'purchased', 'secret', 'permissions',
|
||||
'party', 'preferences', 'profile', 'purchased', 'secret', 'permissions', 'achievements',
|
||||
];
|
||||
|
||||
before(async () => {
|
||||
@@ -251,4 +251,159 @@ describe('PUT /heroes/:heroId', () => {
|
||||
expect(updatedHero.apiToken).to.not.equal(originalToken);
|
||||
expect(updatedHero.apiTokenObscured).to.not.exist;
|
||||
});
|
||||
|
||||
it('updates purchased hair customization', async () => {
|
||||
const hero = await generateUser();
|
||||
const heroRes = await user.put(`/hall/heroes/${hero._id}`, {
|
||||
purchasedPath: 'purchased.hair.bangs.1',
|
||||
purchasedVal: true,
|
||||
});
|
||||
|
||||
// test response
|
||||
expect(heroRes).to.have.all.keys(heroFields);
|
||||
expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']);
|
||||
expect(heroRes.profile).to.have.all.keys(['name']);
|
||||
|
||||
// test response values
|
||||
expect(heroRes.purchased.hair.bangs['1']).to.equal(true);
|
||||
// test hero values
|
||||
await hero.sync();
|
||||
expect(hero.purchased.hair.bangs['1']).to.equal(true);
|
||||
});
|
||||
|
||||
it('updates purchased customization', async () => {
|
||||
const hero = await generateUser();
|
||||
const heroRes = await user.put(`/hall/heroes/${hero._id}`, {
|
||||
purchasedPath: 'purchased.background.beach',
|
||||
purchasedVal: true,
|
||||
});
|
||||
|
||||
// test response
|
||||
expect(heroRes).to.have.all.keys(heroFields);
|
||||
expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']);
|
||||
expect(heroRes.profile).to.have.all.keys(['name']);
|
||||
|
||||
// test response values
|
||||
expect(heroRes.purchased.background.beach).to.equal(true);
|
||||
// test hero values
|
||||
await hero.sync();
|
||||
expect(hero.purchased.background.beach).to.equal(true);
|
||||
});
|
||||
|
||||
it('updates giving nested achievement', async () => {
|
||||
const hero = await generateUser();
|
||||
const heroRes = await user.put(`/hall/heroes/${hero._id}`, {
|
||||
achievementPath: 'achievements.quests.dilatory',
|
||||
achievementVal: 2,
|
||||
});
|
||||
|
||||
// test response
|
||||
expect(heroRes).to.have.all.keys(heroFields);
|
||||
expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']);
|
||||
expect(heroRes.profile).to.have.all.keys(['name']);
|
||||
|
||||
// test response values
|
||||
expect(heroRes.achievements.quests.dilatory).to.equal(2);
|
||||
// test hero values
|
||||
await hero.sync();
|
||||
expect(hero.achievements.quests.dilatory).to.equal(2);
|
||||
});
|
||||
|
||||
it('updates taking away nested achievement', async () => {
|
||||
const hero = await generateUser({ 'achievements.quests.dilatory': 3 });
|
||||
expect(hero.achievements.quests.dilatory).to.equal(3);
|
||||
const heroRes = await user.put(`/hall/heroes/${hero._id}`, {
|
||||
achievementPath: 'achievements.quests.dilatory',
|
||||
achievementVal: 0,
|
||||
});
|
||||
|
||||
// test response
|
||||
expect(heroRes).to.have.all.keys(heroFields);
|
||||
expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']);
|
||||
expect(heroRes.profile).to.have.all.keys(['name']);
|
||||
|
||||
// test response values
|
||||
expect(heroRes.achievements.quests.dilatory).to.equal(0);
|
||||
// test hero values
|
||||
await hero.sync();
|
||||
expect(hero.achievements.quests.dilatory).to.equal(0);
|
||||
});
|
||||
|
||||
it('updates giving achievement', async () => {
|
||||
const hero = await generateUser();
|
||||
const heroRes = await user.put(`/hall/heroes/${hero._id}`, {
|
||||
achievementPath: 'achievements.partyOn',
|
||||
achievementVal: true,
|
||||
});
|
||||
|
||||
// test response
|
||||
expect(heroRes).to.have.all.keys(heroFields);
|
||||
expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']);
|
||||
expect(heroRes.profile).to.have.all.keys(['name']);
|
||||
|
||||
// test response values
|
||||
expect(heroRes.achievements.partyOn).to.equal(true);
|
||||
// test hero values
|
||||
await hero.sync();
|
||||
expect(hero.achievements.partyOn).to.equal(true);
|
||||
});
|
||||
|
||||
it('updates taking away achievement', async () => {
|
||||
const hero = await generateUser({ 'achievements.partyUp': true });
|
||||
expect(hero.achievements.partyUp).to.equal(true);
|
||||
const heroRes = await user.put(`/hall/heroes/${hero._id}`, {
|
||||
achievementPath: 'achievements.partyUp',
|
||||
achievementVal: false,
|
||||
});
|
||||
|
||||
// test response
|
||||
expect(heroRes).to.have.all.keys(heroFields);
|
||||
expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']);
|
||||
expect(heroRes.profile).to.have.all.keys(['name']);
|
||||
|
||||
// test response values
|
||||
expect(heroRes.achievements.partyUp).to.equal(false);
|
||||
// test hero values
|
||||
await hero.sync();
|
||||
expect(hero.achievements.partyUp).to.equal(false);
|
||||
});
|
||||
|
||||
it('updates giving numbered achievement', async () => {
|
||||
const hero = await generateUser();
|
||||
const heroRes = await user.put(`/hall/heroes/${hero._id}`, {
|
||||
achievementPath: 'achievements.streak',
|
||||
achievementVal: 42,
|
||||
});
|
||||
|
||||
// test response
|
||||
expect(heroRes).to.have.all.keys(heroFields);
|
||||
expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']);
|
||||
expect(heroRes.profile).to.have.all.keys(['name']);
|
||||
|
||||
// test response values
|
||||
expect(heroRes.achievements.streak).to.equal(42);
|
||||
// test hero values
|
||||
await hero.sync();
|
||||
expect(hero.achievements.streak).to.equal(42);
|
||||
});
|
||||
|
||||
it('updates setting numbered achievement to 0', async () => {
|
||||
const hero = await generateUser({ 'achievements.streak': 42 });
|
||||
expect(hero.achievements.streak).to.equal(42);
|
||||
const heroRes = await user.put(`/hall/heroes/${hero._id}`, {
|
||||
achievementPath: 'achievements.streak',
|
||||
achievementVal: 0,
|
||||
});
|
||||
|
||||
// test response
|
||||
expect(heroRes).to.have.all.keys(heroFields);
|
||||
expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']);
|
||||
expect(heroRes.profile).to.have.all.keys(['name']);
|
||||
|
||||
// test response values
|
||||
expect(heroRes.achievements.streak).to.equal(0);
|
||||
// test hero values
|
||||
await hero.sync();
|
||||
expect(hero.achievements.streak).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
<template>
|
||||
<div class="accordion-group">
|
||||
<h3
|
||||
class="expand-toggle"
|
||||
:class="{'open': expand}"
|
||||
@click="expand = !expand"
|
||||
>
|
||||
Achievements
|
||||
</h3>
|
||||
<div v-if="expand">
|
||||
<ul>
|
||||
<li
|
||||
v-for="item in achievements"
|
||||
:key="item.path"
|
||||
>
|
||||
<form @submit.prevent="saveItem(item)">
|
||||
<span
|
||||
class="enableValueChange"
|
||||
@click="enableValueChange(item)"
|
||||
>
|
||||
<span :class="item.value ? 'achieved' : 'not-achieved'">
|
||||
{{ item.value }}
|
||||
</span>
|
||||
:
|
||||
{{ item.text || item.key }}
|
||||
</span>
|
||||
|
||||
<div
|
||||
v-if="item.modified"
|
||||
class="form-inline"
|
||||
>
|
||||
<input
|
||||
v-if="item.valueIsInteger"
|
||||
v-model="item.value"
|
||||
class="form-control valueField"
|
||||
type="number"
|
||||
>
|
||||
<input
|
||||
v-if="item.modified"
|
||||
type="submit"
|
||||
value="Save"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
v-for="achievementType in nestedAchievementKeys"
|
||||
:key="achievementType"
|
||||
>
|
||||
<div class="accordion-group">
|
||||
<h4
|
||||
class="expand-toggle"
|
||||
:class="{'open': expandItemType[achievementType]}"
|
||||
@click="expandItemType[achievementType] = !expandItemType[achievementType]"
|
||||
>
|
||||
{{ achievementType }}
|
||||
</h4>
|
||||
|
||||
<div v-if="expandItemType[achievementType]">
|
||||
<ul>
|
||||
<li
|
||||
v-for="item in nestedAchievements[achievementType]"
|
||||
:key="item.path"
|
||||
>
|
||||
<form @submit.prevent="saveItem(item)">
|
||||
<span
|
||||
class="enableValueChange"
|
||||
@click="enableValueChange(item)"
|
||||
>
|
||||
<span :class="item.value ? 'achieved' : 'not-achieved'">
|
||||
{{ item.value }}
|
||||
</span>
|
||||
:
|
||||
{{ item.text || item.key }}
|
||||
</span>
|
||||
|
||||
<div
|
||||
v-if="item.modified"
|
||||
class="form-inline"
|
||||
>
|
||||
<input
|
||||
v-if="item.valueIsInteger"
|
||||
v-model="item.value"
|
||||
class="form-control valueField"
|
||||
type="number"
|
||||
>
|
||||
<input
|
||||
v-if="item.modified"
|
||||
type="submit"
|
||||
value="Save"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
ul li {
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
.ownedItem {
|
||||
font-weight: bold;
|
||||
}
|
||||
.enableValueChange:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
.valueField {
|
||||
min-width: 10ch;
|
||||
}
|
||||
|
||||
.achieved {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.not-achieved {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import content from '@/../../common/script/content';
|
||||
import i18n from '@/../../common/script/i18n';
|
||||
import saveHero from '../mixins/saveHero';
|
||||
|
||||
function getText (achievementItem) {
|
||||
if (achievementItem === undefined) {
|
||||
return '';
|
||||
}
|
||||
const { titleKey } = achievementItem;
|
||||
if (titleKey !== undefined) {
|
||||
return i18n.t(titleKey, 'en');
|
||||
}
|
||||
const { singularTitleKey } = achievementItem;
|
||||
if (singularTitleKey !== undefined) {
|
||||
return i18n.t(singularTitleKey, 'en');
|
||||
}
|
||||
return achievementItem.key;
|
||||
}
|
||||
|
||||
function collateItemData (self) {
|
||||
const achievements = [];
|
||||
const nestedAchievements = {};
|
||||
const basePath = 'achievements';
|
||||
const ownedAchievements = self.hero.achievements;
|
||||
const allAchievements = content.achievements;
|
||||
|
||||
for (const key of Object.keys(ownedAchievements)) {
|
||||
const value = ownedAchievements[key];
|
||||
if (typeof value === 'object') {
|
||||
nestedAchievements[key] = [];
|
||||
for (const nestedKey of Object.keys(value)) {
|
||||
const valueIsInteger = self.integerTypes.includes(key);
|
||||
let text = nestedKey;
|
||||
if (allAchievements[key] && allAchievements[key][nestedKey]) {
|
||||
text = getText(allAchievements[key][nestedKey]);
|
||||
}
|
||||
nestedAchievements[key].push({
|
||||
key: nestedKey,
|
||||
text,
|
||||
achievementType: key,
|
||||
modified: false,
|
||||
path: `${basePath}.${key}.${nestedKey}`,
|
||||
value: value[nestedKey],
|
||||
valueIsInteger,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const valueIsInteger = self.integerTypes.includes(key);
|
||||
achievements.push({
|
||||
key,
|
||||
text: getText(allAchievements[key]),
|
||||
modified: false,
|
||||
path: `${basePath}.${key}`,
|
||||
value: ownedAchievements[key],
|
||||
valueIsInteger,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of Object.keys(allAchievements)) {
|
||||
if (key !== '' && !key.endsWith('UltimateGear') && !key.endsWith('Quest')) {
|
||||
if (ownedAchievements[key] === undefined) {
|
||||
const valueIsInteger = self.integerTypes.includes(key);
|
||||
achievements.push({
|
||||
key,
|
||||
text: getText(allAchievements[key]),
|
||||
modified: false,
|
||||
path: `${basePath}.${key}`,
|
||||
value: valueIsInteger ? 0 : false,
|
||||
valueIsInteger,
|
||||
neverOwned: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.achievements = achievements;
|
||||
self.nestedAchievements = nestedAchievements;
|
||||
}
|
||||
|
||||
function resetData (self) {
|
||||
collateItemData(self);
|
||||
self.nestedAchievementKeys.forEach(itemType => { self.expandItemType[itemType] = false; });
|
||||
}
|
||||
|
||||
export default {
|
||||
mixins: [
|
||||
saveHero,
|
||||
],
|
||||
props: {
|
||||
resetCounter: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
hero: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
expand: false,
|
||||
expandItemType: {
|
||||
quests: false,
|
||||
ultimateGearSets: false,
|
||||
},
|
||||
nestedAchievementKeys: ['quests', 'ultimateGearSets'],
|
||||
integerTypes: ['streak', 'perfect', 'birthday', 'habiticaDays', 'habitSurveys', 'habitBirthdays',
|
||||
'valentine', 'congrats', 'shinySeed', 'goodluck', 'thankyou', 'seafoam', 'snowball', 'quests'],
|
||||
achievements: [],
|
||||
nestedAchievements: {},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
resetCounter () {
|
||||
resetData(this);
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
resetData(this);
|
||||
},
|
||||
methods: {
|
||||
async saveItem (item) {
|
||||
// prepare the item's new value and path for being saved
|
||||
this.hero.achievementPath = item.path;
|
||||
this.hero.achievementVal = item.value;
|
||||
|
||||
await this.saveHero({ hero: this.hero, msg: item.path });
|
||||
item.modified = false;
|
||||
},
|
||||
enableValueChange (item) {
|
||||
// allow form field(s) to be shown:
|
||||
item.modified = true;
|
||||
if (!item.valueIsInteger) {
|
||||
item.value = !item.value;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,245 @@
|
||||
<template>
|
||||
<div class="accordion-group">
|
||||
<h3
|
||||
class="expand-toggle"
|
||||
:class="{'open': expand}"
|
||||
@click="expand = !expand"
|
||||
>
|
||||
Customizations
|
||||
</h3>
|
||||
<div v-if="expand">
|
||||
<div
|
||||
v-for="itemType in itemTypes"
|
||||
:key="itemType"
|
||||
>
|
||||
<div
|
||||
v-if="collatedItemData[itemType]"
|
||||
class="accordion-group"
|
||||
>
|
||||
<h4
|
||||
class="expand-toggle"
|
||||
:class="{'open': expandItemType[itemType]}"
|
||||
@click="expandItemType[itemType] = !expandItemType[itemType]"
|
||||
>
|
||||
{{ itemType }}
|
||||
</h4>
|
||||
|
||||
<div v-if="expandItemType[itemType]">
|
||||
<ul>
|
||||
<li
|
||||
v-for="item in collatedItemData[itemType]"
|
||||
:key="item.path"
|
||||
>
|
||||
<form @submit.prevent="saveItem(item)">
|
||||
<span
|
||||
class="enableValueChange"
|
||||
@click="enableValueChange(item)"
|
||||
>
|
||||
<span :class="item.value ? 'owned' : 'not-owned'">
|
||||
{{ item.value }}
|
||||
</span>
|
||||
:
|
||||
<span :class="{ ownedItem: !item.neverOwned }">{{ item.text }}</span>
|
||||
</span>
|
||||
{{ item.set }}
|
||||
|
||||
<div
|
||||
v-if="item.modified"
|
||||
class="form-inline"
|
||||
>
|
||||
<input
|
||||
v-if="item.valueIsInteger"
|
||||
v-model="item.value"
|
||||
class="form-control valueField"
|
||||
type="number"
|
||||
>
|
||||
<input
|
||||
v-if="item.modified"
|
||||
type="submit"
|
||||
value="Save"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
ul li {
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
.ownedItem {
|
||||
font-weight: bold;
|
||||
}
|
||||
.enableValueChange:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
.valueField {
|
||||
min-width: 10ch;
|
||||
}
|
||||
.owned {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.not-owned {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import content from '@/../../common/script/content';
|
||||
import getItemDescription from '../mixins/getItemDescription';
|
||||
import saveHero from '../mixins/saveHero';
|
||||
|
||||
const months = [
|
||||
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
|
||||
'September', 'October', 'November', 'December',
|
||||
];
|
||||
|
||||
function makeSetText (set) {
|
||||
if (set === undefined) {
|
||||
return '';
|
||||
}
|
||||
if (set.key.indexOf('backgrounds') === 0) {
|
||||
const { text } = set;
|
||||
return `${months[parseInt(text.slice(11, 13), 10) - 1]} ${text.slice(13)}`;
|
||||
}
|
||||
return set.key;
|
||||
}
|
||||
|
||||
function collateItemData (self) {
|
||||
const collatedItemData = {};
|
||||
self.itemTypes.forEach(itemType => {
|
||||
// itemTypes are pets, food, gear, etc
|
||||
|
||||
// Set up some basic data for this itemType:
|
||||
const basePath = `purchased.${itemType}`;
|
||||
let ownedItems;
|
||||
let allItems;
|
||||
if (itemType.indexOf('hair') === 0) {
|
||||
const hairType = itemType.split('.')[1];
|
||||
allItems = content.appearances.hair[hairType];
|
||||
if (self.hero.purchased && self.hero.purchased.hair) {
|
||||
ownedItems = self.hero.purchased.hair[hairType] || {};
|
||||
} else {
|
||||
ownedItems = {};
|
||||
}
|
||||
} else {
|
||||
allItems = content.appearances[itemType];
|
||||
ownedItems = self.hero.purchased[itemType] || {};
|
||||
}
|
||||
|
||||
const itemData = []; // all items for this itemType
|
||||
|
||||
// Collate data for items that the user owns or used to own:
|
||||
for (const key of Object.keys(ownedItems)) {
|
||||
// Do not sort keys. The order in the items object gives hints about order received.
|
||||
const item = allItems[key];
|
||||
itemData.push({
|
||||
itemType,
|
||||
key,
|
||||
text: item.text ? item.text() : key,
|
||||
modified: false,
|
||||
path: `${basePath}.${key}`,
|
||||
value: ownedItems[key],
|
||||
set: makeSetText(item.set),
|
||||
});
|
||||
}
|
||||
|
||||
// Collate data for items that the user never owned:
|
||||
for (const key of Object.keys(allItems).sort()) {
|
||||
if (
|
||||
// ignore items the user owns because we captured them above:
|
||||
!(key in ownedItems)
|
||||
&& allItems[key].price > 0
|
||||
) {
|
||||
const item = allItems[key];
|
||||
itemData.push({
|
||||
itemType,
|
||||
key,
|
||||
text: item.text ? item.text() : key,
|
||||
modified: false,
|
||||
path: `${basePath}.${key}`,
|
||||
value: false,
|
||||
set: makeSetText(item.set),
|
||||
});
|
||||
}
|
||||
}
|
||||
if (itemData.length > 0) {
|
||||
collatedItemData[itemType] = itemData;
|
||||
}
|
||||
});
|
||||
return collatedItemData;
|
||||
}
|
||||
|
||||
function resetData (self) {
|
||||
self.collatedItemData = collateItemData(self);
|
||||
self.itemTypes.forEach(itemType => { self.expandItemType[itemType] = false; });
|
||||
}
|
||||
|
||||
export default {
|
||||
mixins: [
|
||||
getItemDescription,
|
||||
saveHero,
|
||||
],
|
||||
props: {
|
||||
resetCounter: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
hero: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
expand: false,
|
||||
expandItemType: {
|
||||
skin: false,
|
||||
shirt: false,
|
||||
background: false,
|
||||
'hair.bangs': false,
|
||||
'hair.base': false,
|
||||
'hair.color': false,
|
||||
'hair.mustache': false,
|
||||
'hair.beard': false,
|
||||
'hair.flower': false,
|
||||
},
|
||||
itemTypes: ['skin', 'shirt', 'background', 'hair.bangs', 'hair.base', 'hair.color', 'hair.mustache', 'hair.beard', 'hair.flower'],
|
||||
nonIntegerTypes: ['skin', 'shirt', 'background'],
|
||||
collatedItemData: {},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
resetCounter () {
|
||||
resetData(this);
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
resetData(this);
|
||||
},
|
||||
methods: {
|
||||
async saveItem (item) {
|
||||
// prepare the item's new value and path for being saved
|
||||
this.hero.purchasedPath = item.path;
|
||||
this.hero.purchasedVal = item.value;
|
||||
|
||||
await this.saveHero({ hero: this.hero, msg: item.path });
|
||||
item.modified = false;
|
||||
},
|
||||
enableValueChange (item) {
|
||||
// allow form field(s) to be shown:
|
||||
item.modified = true;
|
||||
item.value = !item.value;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -47,6 +47,16 @@
|
||||
:reset-counter="resetCounter"
|
||||
/>
|
||||
|
||||
<customizations-owned
|
||||
:hero="hero"
|
||||
:reset-counter="resetCounter"
|
||||
/>
|
||||
|
||||
<achievements
|
||||
:hero="hero"
|
||||
:reset-counter="resetCounter"
|
||||
/>
|
||||
|
||||
<transactions
|
||||
:hero="hero"
|
||||
:reset-counter="resetCounter"
|
||||
@@ -103,6 +113,8 @@ import PrivilegesAndGems from './privilegesAndGems';
|
||||
import ContributorDetails from './contributorDetails';
|
||||
import Transactions from './transactions';
|
||||
import SubscriptionAndPerks from './subscriptionAndPerks';
|
||||
import CustomizationsOwned from './customizationsOwned.vue';
|
||||
import Achievements from './achievements.vue';
|
||||
|
||||
import { userStateMixin } from '../../../mixins/userState';
|
||||
|
||||
@@ -110,6 +122,7 @@ export default {
|
||||
components: {
|
||||
BasicDetails,
|
||||
ItemsOwned,
|
||||
CustomizationsOwned,
|
||||
CronAndAuth,
|
||||
PartyAndQuest,
|
||||
AvatarAndDrops,
|
||||
@@ -117,6 +130,7 @@ export default {
|
||||
ContributorDetails,
|
||||
Transactions,
|
||||
SubscriptionAndPerks,
|
||||
Achievements,
|
||||
},
|
||||
mixins: [userStateMixin],
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
|
||||
@@ -68,7 +68,9 @@
|
||||
class="enableValueChange"
|
||||
@click="enableValueChange(item)"
|
||||
>
|
||||
<span :class="item.value ? 'owned' : 'not-owned'">
|
||||
{{ item | displayValue }}
|
||||
</span>
|
||||
:
|
||||
<span :class="{ ownedItem: !item.neverOwned }">{{ item.key }} : </span>
|
||||
</span>
|
||||
@@ -102,15 +104,26 @@
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
ul li {
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
.ownedItem {
|
||||
font-weight: bold;
|
||||
}
|
||||
.enableValueChange:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
.valueField {
|
||||
min-width: 10ch;
|
||||
}
|
||||
.owned {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.not-owned {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -21,7 +21,10 @@
|
||||
Group plan ID:
|
||||
<strong>{{ hero.purchased.plan.owner }}</strong>
|
||||
</div>
|
||||
<div v-if="hero.purchased.plan.dateCreated" class="form-inline">
|
||||
<div
|
||||
v-if="hero.purchased.plan.dateCreated"
|
||||
class="form-inline"
|
||||
>
|
||||
<label>
|
||||
Creation date:
|
||||
<input
|
||||
@@ -31,7 +34,10 @@
|
||||
> <strong class="ml-2">{{ dateFormat(hero.purchased.plan.dateCreated) }}</strong>
|
||||
</label>
|
||||
</div>
|
||||
<div v-if="hero.purchased.plan.dateCurrentTypeCreated" class="form-inline">
|
||||
<div
|
||||
v-if="hero.purchased.plan.dateCurrentTypeCreated"
|
||||
class="form-inline"
|
||||
>
|
||||
<label>
|
||||
Start date for current subscription type:
|
||||
<input
|
||||
@@ -45,15 +51,13 @@
|
||||
<div class="form-inline">
|
||||
<label>
|
||||
Termination date:
|
||||
<div
|
||||
v-if="hero.purchased.plan.dateTerminated">
|
||||
<div>
|
||||
<input
|
||||
v-model="hero.purchased.plan.dateTerminated"
|
||||
class="form-control"
|
||||
type="text"
|
||||
> <strong class="ml-2">{{ dateFormat(hero.purchased.plan.dateTerminated) }}</strong>
|
||||
</div>
|
||||
<strong v-else> None</strong>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-inline">
|
||||
@@ -199,6 +203,9 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
dateFormat (date) {
|
||||
if (!date) {
|
||||
return '--';
|
||||
}
|
||||
return moment(date).format('YYYY/MM/DD');
|
||||
},
|
||||
},
|
||||
|
||||
@@ -59,8 +59,8 @@
|
||||
|
||||
<div
|
||||
v-once
|
||||
v-html="$t('resetText2')"
|
||||
class="mb-3"
|
||||
v-html="$t('resetText2')"
|
||||
>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ api.getHeroes = {
|
||||
// Note, while the following routes are called getHero / updateHero
|
||||
// they can be used by admins to get/update any user
|
||||
|
||||
const heroAdminFields = 'auth balance contributor flags items lastCron party preferences profile purchased secret permissions';
|
||||
const heroAdminFields = 'auth balance contributor flags items lastCron party preferences profile purchased secret permissions achievements';
|
||||
const heroAdminFieldsToFetch = heroAdminFields; // these variables will make more sense when...
|
||||
const heroAdminFieldsToShow = heroAdminFields; // ... apiTokenObscured is added
|
||||
|
||||
@@ -285,7 +285,7 @@ api.updateHero = {
|
||||
if (plan.dateCurrentTypeCreated) {
|
||||
hero.purchased.plan.dateCurrentTypeCreated = plan.dateCurrentTypeCreated;
|
||||
}
|
||||
if (plan.dateTerminated) {
|
||||
if (plan.dateTerminated !== hero.purchased.plan.dateTerminated) {
|
||||
hero.purchased.plan.dateTerminated = plan.dateTerminated;
|
||||
}
|
||||
if (plan.perkMonthCount) {
|
||||
@@ -342,6 +342,42 @@ api.updateHero = {
|
||||
hero.purchased.ads = updateData.purchased.ads;
|
||||
}
|
||||
|
||||
if (updateData.purchasedPath && updateData.purchasedVal !== undefined
|
||||
&& validateItemPath(updateData.purchasedPath)) {
|
||||
const parts = updateData.purchasedPath.split('.');
|
||||
const key = _.last(parts);
|
||||
const type = parts[parts.length - 2];
|
||||
// using _.set causes weird issues
|
||||
if (updateData.purchasedVal === true) {
|
||||
if (updateData.purchasedPath.indexOf('hair.') === 10) {
|
||||
if (hero.purchased.hair[type] === undefined) hero.purchased.hair[type] = {};
|
||||
hero.purchased.hair[type][key] = true;
|
||||
} else {
|
||||
if (hero.purchased[type] === undefined) hero.purchased[type] = {};
|
||||
hero.purchased[type][key] = true;
|
||||
}
|
||||
} else if (updateData.purchasedPath.indexOf('hair.') === 10) {
|
||||
delete hero.purchased.hair[type][key];
|
||||
} else {
|
||||
delete hero.purchased[type][key];
|
||||
}
|
||||
hero.markModified('purchased');
|
||||
}
|
||||
|
||||
if (updateData.achievementPath && updateData.achievementVal !== undefined) {
|
||||
const parts = updateData.achievementPath.split('.');
|
||||
const key = _.last(parts);
|
||||
const type = parts[parts.length - 2];
|
||||
// using _.set causes weird issues
|
||||
if (type !== 'achievements') {
|
||||
if (hero.achievements[type] === undefined) hero.achievements[type] = {};
|
||||
hero.achievements[type][key] = updateData.achievementVal;
|
||||
} else {
|
||||
hero.achievements[key] = updateData.achievementVal;
|
||||
}
|
||||
hero.markModified('achievements');
|
||||
}
|
||||
|
||||
// give them the Dragon Hydra pet if they're above level 6
|
||||
if (hero.contributor.level >= 6) {
|
||||
hero.items.pets['Dragon-Hydra'] = 5;
|
||||
|
||||
@@ -22,7 +22,7 @@ export function getDefaultOwnedGear () {
|
||||
// Example of an item path: `items.gear.owned.head_warrior_0`
|
||||
export function validateItemPath (itemPath) {
|
||||
// The item path must start with `items.`
|
||||
if (itemPath.indexOf('items.') !== 0) return false;
|
||||
if (itemPath.indexOf('items.') === 0) {
|
||||
if (User.schema.paths[itemPath]) return true;
|
||||
|
||||
const key = last(itemPath.split('.'));
|
||||
@@ -54,6 +54,18 @@ export function validateItemPath (itemPath) {
|
||||
if (itemPath.indexOf('items.quests') === 0) {
|
||||
return Boolean(shared.content.quests[key]);
|
||||
}
|
||||
}
|
||||
|
||||
if (itemPath.indexOf('purchased.') === 0) {
|
||||
const parts = itemPath.split('.');
|
||||
const key = last(parts);
|
||||
const type = parts[parts.length - 2];
|
||||
|
||||
if (itemPath.indexOf('hair.') === 10) {
|
||||
return Boolean(shared.content.appearances.hair[type][key]);
|
||||
}
|
||||
return Boolean(shared.content.appearances[type][key]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user