mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 07:07:35 +01:00
* WIP(privacy): start of banner * WIP(privacy): layout rough * WIP(privacy): mobile layout, add modal * fix(privacy): implement toggle disable and setting row fold * fix(privacy): clean up a couple of styles * fix(privacy): adjust banner width at mobile sizes * WIP(privacy): remove Loggly echo of Amplitude data * fix(banners): account for privacy in snackbar position * WIP(privacy): dismiss banner * chore(analytics): update to maintaned GA4 library * fix(tests): lint, misuse of apiError * fix(analytics): add debug mode * fix(analytics): load new library on client * WIP(privacy): gtag.js based implementation * fix(analytics): lint issues * fix(lint): one more unused * fix(lint): client errors * feat(privacy): draft workflows * fix(analytics): linting, send needed user values * fix(tests): use mock analytics service in test env * fix(tests): restore previous logic for node env * feat(intro): jump to page 2 onboarding * WIP(auth): revisions to registration flow * WIP(privacy): landing page and banner revisions * WIP(signup): added new username, tos, privacy state * fix(signup): revert debugging logic * WIP(signup): add defaulting and checkbox * wip(signup): move social auth behind username screen * Squashed commit of the following: commit ca0a238e5f008525ed154c5eaf12e44f2fc22b00 Author: Phillip Thelen <phillip@habitica.com> Date: Wed May 7 12:17:20 2025 +0200 make emails lowercase commit a2ce748558ce9134e6825208a7e66d78e720202e Author: Phillip Thelen <phillip@habitica.com> Date: Wed Apr 9 13:27:01 2025 +0200 remove unused import commit cc6ce6c388d9693cf192c4bea733931fc8c31c37 Author: Phillip Thelen <phillip@habitica.com> Date: Wed Apr 9 13:13:03 2025 +0200 add tests for new api route commit 0d40a6230b548625482aa9f6831c93ed9d62533a Author: Kalista Payne <sabrecat@gmail.com> Date: Wed Jun 18 15:50:22 2025 -0500 update social tests commit 79177d6754589b9e54682af8a531b63f60215dab Author: Phillip Thelen <phillip@habitica.com> Date: Wed Apr 9 10:21:51 2025 +0200 new api route to check if an email is available commit 11df73fe07eeb730c2a95593e18e14a931f52429 Author: Phillip Thelen <phillip@habitica.com> Date: Wed Apr 9 10:21:39 2025 +0200 Add field to not register social account when called * Squashed commit of the following: commitb8a2f0b8eeAuthor: CuriousMagpie <eilatan@gmail.com> Date: Thu Jun 20 17:18:30 2024 -0400 update privacy policy * fix(vite): import syntax * feat(auth): precheck on defaulted username * feat(auth): add store action for check-email * feat(auth): check email before proceeding * WIP(login): refactor username screen * WIP(auth): complete login/reg flow * fix(auth): filter out expected 404 * fix(login): use allowRegister with Apple and add z-index to component * fix(login): style corrections and email passthru * Fix edgecase Signed-off-by: Kalista Payne <sabrecat@gmail.com> * fix(auth): correct error behaviors * fix(auth): rewire Apple auth * make check-email check for restricted domains Signed-off-by: Kalista Payne <kalista@habitica.com> * fix(signup): all the style * fix(express): return when responding * fix(error): reduce specificity for restricted domain issue * fix apple auth Signed-off-by: Kalista Payne <kalista@habitica.com> * fix(signup): change from blur to 500ms debounce * fix(login): add missing 200 response in Apple flow * fix(signup): more reconciliation with @phillipthelen's work * fix(signup): now using token not code * fix(reg): don't bail on Apple if we're allowing reg * fix(auth): more reconciliation with @phillipthelen code * feat(copy): privacy policy updates * fix(copy): replace placeholder * fix(vue): use Vite syntax for scss import * fix(static): corrections to copy and css * chore(style): remove excess whitespace * use correct error Signed-off-by: Kalista Payne <kalista@habitica.com> * fix(layout): inputs, add privacy banner * fix(login): button hover, more validation states * fix(login): further layout and UX corrections * fix(static): add back containing div for show/hide * fix(apple): clean out Apple token * fix(settings): only change preference on save * fix(settings): correct save/cancel behavior * fix(layout): consistent use of header/footer * fix(layout): reposition mountains for reg/login/forgot * fix(signup): partial rollback of /username route * refactor(signup): move /username to page * fix(apple): don't overwrite reg method * fix(username): don't skip empty validation * fix(input): don't show valid if no username * fix(login): clean out Apple token if using another method * fix(apple): possible race with token * fix(tests): some housekeeping * fix(config): copypasta * fix(lint): various cleanup * fix(lint): line squeeze * fix(lint): one more v-for * fix(groups): funnel invite flow to new username page * Squashed commit of the following: commit 3c5ba4bf24e4bb7996786520101f27ad66405bce Author: Kalista Payne <kalista@habitica.com> Date: Mon Aug 18 14:38:31 2025 -0500 fix(privacy): update link ref commit9d216f623bAuthor: Kalista Payne <kalista@habitica.com> Date: Mon Aug 18 14:18:22 2025 -0500 fix(privacy-tos): copy edits cont'd commitd744f47140Author: Kalista Payne <kalista@habitica.com> Date: Mon Aug 18 13:43:22 2025 -0500 fix(privacy): copy edits and ToC reflow commit2c3c3fc9ceAuthor: Phillip Thelen <phillip@habitica.com> Date: Mon Aug 18 18:46:24 2025 +0200 lint commitcf363034d5Author: Phillip Thelen <phillip@habitica.com> Date: Mon Aug 18 18:34:54 2025 +0200 fix link commit3afacd2c05Author: Phillip Thelen <phillip@habitica.com> Date: Mon Aug 18 18:34:42 2025 +0200 add updated terms commit258b722499Author: Phillip Thelen <phillip@habitica.com> Date: Mon Aug 18 17:58:42 2025 +0200 put back button to show/hide third party info commit2992e0299bAuthor: Phillip Thelen <phillip@habitica.com> Date: Mon Aug 18 17:58:32 2025 +0200 minor edits commitbb5e252299Author: Kalista Payne <kalista@habitica.com> Date: Sun Aug 17 21:01:50 2025 -0500 fix(privacy): update Section 3 commitc79af7baa8Author: Kalista Payne <kalista@habitica.com> Date: Fri Aug 15 17:28:49 2025 -0500 fix(privacy): various copy edits commit100f2f4574Author: Phillip Thelen <phillip@habitica.com> Date: Fri Aug 15 11:37:37 2025 +0200 add newline commit11d1cfd0d9Author: Phillip Thelen <phillip@habitica.com> Date: Fri Aug 15 11:10:01 2025 +0200 update privacy policy commit59b99badf3Author: Kalista Payne <kalista@habitica.com> Date: Fri Aug 8 14:04:19 2025 -0500 5.38.2 commit78daeb4191Author: Kalista Payne <kalista@habitica.com> Date: Fri Aug 8 13:36:19 2025 -0500 fix(apple): don't run auth middleware during redirect commit93f8d60903Author: Weblate <noreply@weblate.org> Date: Fri Aug 8 10:12:25 2025 +0200 Translated using Weblate (German) Currently translated at 99.4% (185 of 186 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (186 of 186 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (186 of 186 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (54 of 54 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (243 of 243 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (15 of 15 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (47 of 47 strings) Translated using Weblate (Dutch) Currently translated at 78.0% (2643 of 3385 strings) Translated using Weblate (Dutch) Currently translated at 40.8% (100 of 245 strings) Translated using Weblate (Polish) Currently translated at 89.9% (233 of 259 strings) Translated using Weblate (Dutch) Currently translated at 67.5% (175 of 259 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (914 of 914 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (110 of 110 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (914 of 914 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (914 of 914 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 20.8% (51 of 245 strings) Translated using Weblate (Turkish) Currently translated at 65.9% (60 of 91 strings) Translated using Weblate (Turkish) Currently translated at 65.9% (60 of 91 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 17.9% (44 of 245 strings) Co-authored-by: FingerTiao <787170918@qq.com> Co-authored-by: Jaime Martí <jaumemarti77@icloud.com> Co-authored-by: Karmelkowy <kicimeow.karmelio@gmail.com> Co-authored-by: Linsey Dunya Pastoor <sekai.creations@gmail.com> Co-authored-by: Mete Olmez <metezori27@gmail.com> Co-authored-by: Sefa Uğurlu <ugurlusefa2@gmail.com> Co-authored-by: Summer_GUI <heyang94@163.com> Co-authored-by: Toro Mor <thomas.bizer@gmx.de> Co-authored-by: Weblate <noreply@weblate.org> Co-authored-by: innnko <ayakabooker@gmail.com> Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/challenge/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/tr/ Translate-URL: https://translate.habitica.com/projects/habitica/contrib/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/death/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/nl/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/ Translate-URL: https://translate.habitica.com/projects/habitica/front/de/ Translate-URL: https://translate.habitica.com/projects/habitica/front/es/ Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/nl/ Translate-URL: https://translate.habitica.com/projects/habitica/generic/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/nl/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/pl/ Translation: Habitica/Backgrounds Translation: Habitica/Challenge Translation: Habitica/Communityguidelines Translation: Habitica/Contrib Translation: Habitica/Death Translation: Habitica/Defaulttasks Translation: Habitica/Faq Translation: Habitica/Front Translation: Habitica/Gear Translation: Habitica/Generic Translation: Habitica/Settings commiteb16fec41eAuthor: Phillip Thelen <phillip@habitica.com> Date: Wed Aug 6 22:08:07 2025 +0200 Add interface to block ip-addresses or clients due to abuse (#15484) * Read IP blocks from database * begin building general blocking solution * add new frontend files * Add UI for managing blockers * correctly reset local data after creating blocker * Tweak wording * Add UI for managing blockers * restructure admin pages * improve test coverage * Improve blocker UI * add blocker to block emails from registration * lint fix * fix * lint fixes * fix import * add new permission for managing blockers * improve permission check * fix managing permissions from admin * improve navbar display for non fullAccess admin * update block error strings * lint fix * add option to errorHandler to skip logging * validate blocker value during input * improve blocker form display * chore(subproj): reconcile habitica-images * fix(scripts): use same Mongo version for dev/test * fix(whitespace): eof * documentation improvements * remove nconf import * remove old test --------- Co-authored-by: Kalista Payne <kalista@habitica.com> Co-authored-by: Kalista Payne <sabrecat@gmail.com> commit47d832bf12Author: Fiz <34069775+Hafizzle@users.noreply.github.com> Date: Tue Aug 5 15:12:44 2025 -0500 Add backend support for Hydra mount (#15482) * chore: update time travelers shop to display seasonal backgrounds * chore: update time travelers banner (note CSS borken rn) * chore: fix borken CSS and update logic in shop * chore: added isSubscribed function, not working * chore: isSubscribed working but no bg for subscribers * chore: logic and css updates * chore: update habitica-images * chore: add check for trinket * chore: more time traveler shop logicking * Add backend support for Hydra mount - Add Dragon-Hydra to special mounts in stable.js - Configure as contributor level 7 reward with canFind: true - Add GIF format support for mount sprites - Enable admin panel granting capability * Fix Vue template errors in timeTravelers component * Fix duplicate template block in timeTravelers component * add CSS for Hydra mount GIF sprites Added CSS rules for Mount_Head_Dragon-Hydra and Mount_Body_Dragon-Hydra GIF sprites * Remove the separate Hydra mount dimension declaration --------- Co-authored-by: CuriousMagpie <eilatan@gmail.com> commitc03ab9855fAuthor: Kalista Payne <kalista@habitica.com> Date: Tue Aug 5 14:31:05 2025 -0500 5.38.1 commit8f96b7b7fdAuthor: Weblate <noreply@weblate.org> Date: Tue Aug 5 13:02:45 2025 +0200 Translated using Weblate (Chinese (Traditional)) Currently translated at 17.1% (42 of 245 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 16.7% (41 of 245 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 16.3% (40 of 245 strings) Translated using Weblate (Polish) Currently translated at 100.0% (914 of 914 strings) Translated using Weblate (Japanese) Currently translated at 98.8% (425 of 430 strings) Translated using Weblate (French) Currently translated at 99.4% (184 of 185 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 15.9% (39 of 245 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (268 of 268 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (3385 of 3385 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (185 of 185 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (914 of 914 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 99.8% (3379 of 3385 strings) Translated using Weblate (Polish) Currently translated at 95.5% (128 of 134 strings) Translated using Weblate (Japanese) Currently translated at 94.7% (254 of 268 strings) Translated using Weblate (Polish) Currently translated at 94.0% (126 of 134 strings) Translated using Weblate (Japanese) Currently translated at 98.6% (424 of 430 strings) Translated using Weblate (Japanese) Currently translated at 98.3% (423 of 430 strings) Translated using Weblate (Japanese) Currently translated at 92.5% (798 of 862 strings) Translated using Weblate (Japanese) Currently translated at 92.4% (797 of 862 strings) Translated using Weblate (Japanese) Currently translated at 90.6% (781 of 862 strings) Translated using Weblate (Japanese) Currently translated at 91.9% (3112 of 3385 strings) Translated using Weblate (Japanese) Currently translated at 91.9% (3111 of 3385 strings) Translated using Weblate (Japanese) Currently translated at 94.0% (174 of 185 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (259 of 259 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (8 of 8 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 15.5% (38 of 245 strings) Translated using Weblate (Japanese) Currently translated at 91.6% (3104 of 3385 strings) Translated using Weblate (Japanese) Currently translated at 93.5% (173 of 185 strings) Translated using Weblate (Japanese) Currently translated at 99.6% (279 of 280 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (167 of 167 strings) Translated using Weblate (Japanese) Currently translated at 89.2% (769 of 862 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (914 of 914 strings) Translated using Weblate (Japanese) Currently translated at 94.4% (253 of 268 strings) Translated using Weblate (Japanese) Currently translated at 91.8% (170 of 185 strings) Translated using Weblate (Japanese) Currently translated at 97.9% (421 of 430 strings) Translated using Weblate (Japanese) Currently translated at 91.6% (3104 of 3385 strings) Translated using Weblate (Japanese) Currently translated at 93.6% (251 of 268 strings) Translated using Weblate (Japanese) Currently translated at 90.8% (168 of 185 strings) Translated using Weblate (Japanese) Currently translated at 82.4% (202 of 245 strings) Translated using Weblate (French) Currently translated at 100.0% (268 of 268 strings) Translated using Weblate (French) Currently translated at 100.0% (3385 of 3385 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 15.1% (37 of 245 strings) Translated using Weblate (French) Currently translated at 100.0% (914 of 914 strings) Translated using Weblate (Japanese) Currently translated at 91.3% (3092 of 3385 strings) Translated using Weblate (Japanese) Currently translated at 92.5% (248 of 268 strings) Translated using Weblate (Japanese) Currently translated at 92.5% (248 of 268 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (193 of 193 strings) Translated using Weblate (Croatian) Currently translated at 100.0% (15 of 15 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (167 of 167 strings) Translated using Weblate (Korean) Currently translated at 22.8% (56 of 245 strings) Translated using Weblate (Korean) Currently translated at 47.7% (128 of 268 strings) Translated using Weblate (Croatian) Currently translated at 45.1% (121 of 268 strings) Translated using Weblate (Korean) Currently translated at 71.9% (620 of 862 strings) Translated using Weblate (Croatian) Currently translated at 70.6% (609 of 862 strings) Translated using Weblate (Croatian) Currently translated at 75.0% (6 of 8 strings) Translated using Weblate (Korean) Currently translated at 67.6% (291 of 430 strings) Translated using Weblate (Korean) Currently translated at 52.8% (1788 of 3385 strings) Translated using Weblate (Croatian) Currently translated at 50.3% (1706 of 3385 strings) Translated using Weblate (Croatian) Currently translated at 51.7% (134 of 259 strings) Translated using Weblate (Czech) Currently translated at 92.8% (130 of 140 strings) Translated using Weblate (English (United Kingdom)) Currently translated at 86.9% (233 of 268 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (94 of 94 strings) Translated using Weblate (English (United Kingdom)) Currently translated at 100.0% (94 of 94 strings) Translated using Weblate (Danish) Currently translated at 92.1% (105 of 114 strings) Translated using Weblate (Czech) Currently translated at 89.4% (102 of 114 strings) Translated using Weblate (Czech) Currently translated at 83.5% (112 of 134 strings) Translated using Weblate (Spanish (Latin America)) Currently translated at 71.6% (308 of 430 strings) Translated using Weblate (Spanish (Latin America)) Currently translated at 100.0% (245 of 245 strings) Translated using Weblate (Serbian) Currently translated at 84.4% (49 of 58 strings) Translated using Weblate (Bulgarian) Currently translated at 51.4% (144 of 280 strings) Translated using Weblate (Swedish) Currently translated at 66.5% (286 of 430 strings) Translated using Weblate (Serbian) Currently translated at 65.5% (282 of 430 strings) Translated using Weblate (Slovak) Currently translated at 65.5% (282 of 430 strings) Translated using Weblate (Romanian) Currently translated at 66.7% (287 of 430 strings) Translated using Weblate (English (United Kingdom)) Currently translated at 100.0% (430 of 430 strings) Translated using Weblate (Danish) Currently translated at 66.0% (284 of 430 strings) Translated using Weblate (Czech) Currently translated at 69.7% (300 of 430 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 99.7% (3377 of 3385 strings) Translated using Weblate (Swedish) Currently translated at 54.1% (1834 of 3385 strings) Translated using Weblate (Serbian) Currently translated at 50.6% (1714 of 3385 strings) Translated using Weblate (Slovak) Currently translated at 50.0% (1695 of 3385 strings) Translated using Weblate (Romanian) Currently translated at 60.5% (2050 of 3385 strings) Translated using Weblate (Hebrew) Currently translated at 38.4% (1301 of 3385 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (3385 of 3385 strings) Translated using Weblate (Danish) Currently translated at 54.0% (1829 of 3385 strings) Translated using Weblate (Czech) Currently translated at 59.6% (2020 of 3385 strings) Translated using Weblate (Swedish) Currently translated at 75.6% (140 of 185 strings) Translated using Weblate (Serbian) Currently translated at 73.5% (136 of 185 strings) Translated using Weblate (Slovak) Currently translated at 84.8% (157 of 185 strings) Translated using Weblate (Romanian) Currently translated at 78.9% (146 of 185 strings) Translated using Weblate (Portuguese) Currently translated at 82.1% (152 of 185 strings) Translated using Weblate (Italian) Currently translated at 91.8% (170 of 185 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (185 of 185 strings) Translated using Weblate (English (United Kingdom)) Currently translated at 100.0% (185 of 185 strings) Translated using Weblate (English (United Kingdom)) Currently translated at 100.0% (185 of 185 strings) Translated using Weblate (German) Currently translated at 100.0% (184 of 184 strings) Translated using Weblate (Danish) Currently translated at 77.2% (143 of 185 strings) Translated using Weblate (English (United Kingdom)) Currently translated at 98.7% (242 of 245 strings) Translated using Weblate (Czech) Currently translated at 75.1% (139 of 185 strings) Translated using Weblate (Bulgarian) Currently translated at 74.5% (138 of 185 strings) Translated using Weblate (Czech) Currently translated at 8.1% (20 of 245 strings) Translated using Weblate (Swedish) Currently translated at 72.0% (621 of 862 strings) Translated using Weblate (Serbian) Currently translated at 65.1% (562 of 862 strings) Translated using Weblate (Slovak) Currently translated at 66.9% (577 of 862 strings) Translated using Weblate (Romanian) Currently translated at 77.7% (670 of 862 strings) Translated using Weblate (Portuguese) Currently translated at 70.0% (604 of 862 strings) Translated using Weblate (Polish) Currently translated at 67.1% (579 of 862 strings) Translated using Weblate (Italian) Currently translated at 86.8% (749 of 862 strings) Translated using Weblate (Indonesian) Currently translated at 86.0% (742 of 862 strings) Translated using Weblate (Hebrew) Currently translated at 66.1% (570 of 862 strings) Translated using Weblate (English (United Kingdom)) Currently translated at 98.0% (845 of 862 strings) Translated using Weblate (Danish) Currently translated at 69.9% (603 of 862 strings) Translated using Weblate (Czech) Currently translated at 69.7% (601 of 862 strings) Translated using Weblate (Bulgarian) Currently translated at 66.3% (572 of 862 strings) Translated using Weblate (Serbian) Currently translated at 74.0% (305 of 412 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (193 of 193 strings) Translated using Weblate (Danish) Currently translated at 90.0% (371 of 412 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (259 of 259 strings) Translated using Weblate (Swedish) Currently translated at 53.6% (139 of 259 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (259 of 259 strings) Translated using Weblate (English (United Kingdom)) Currently translated at 100.0% (259 of 259 strings) Translated using Weblate (Danish) Currently translated at 62.1% (161 of 259 strings) Translated using Weblate (Bulgarian) Currently translated at 54.0% (140 of 259 strings) Translated using Weblate (English (United Kingdom)) Currently translated at 82.8% (222 of 268 strings) Translated using Weblate (English (United Kingdom)) Currently translated at 99.4% (184 of 185 strings) Translated using Weblate (English (United Kingdom)) Currently translated at 98.3% (241 of 245 strings) Translated using Weblate (Japanese) Currently translated at 91.3% (3092 of 3385 strings) Translated using Weblate (Japanese) Currently translated at 88.4% (237 of 268 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (134 of 134 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (259 of 259 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (914 of 914 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (243 of 243 strings) Translated using Weblate (Japanese) Currently translated at 82.4% (202 of 245 strings) Translated using Weblate (English (United Kingdom)) Currently translated at 100.0% (259 of 259 strings) Translated using Weblate (Japanese) Currently translated at 87.3% (234 of 268 strings) Translated using Weblate (Japanese) Currently translated at 86.4% (160 of 185 strings) Translated using Weblate (Japanese) Currently translated at 99.8% (913 of 914 strings) Translated using Weblate (German) Currently translated at 100.0% (268 of 268 strings) Translated using Weblate (German) Currently translated at 100.0% (3377 of 3377 strings) Translated using Weblate (German) Currently translated at 100.0% (914 of 914 strings) Translated using Weblate (German) Currently translated at 100.0% (259 of 259 strings) Translated using Weblate (German) Currently translated at 100.0% (259 of 259 strings) Translated using Weblate (German) Currently translated at 100.0% (259 of 259 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (3385 of 3385 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (914 of 914 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (268 of 268 strings) Translated using Weblate (Russian) Currently translated at 88.5% (248 of 280 strings) Translated using Weblate (Spanish) Currently translated at 99.8% (3379 of 3385 strings) Translated using Weblate (German) Currently translated at 100.0% (862 of 862 strings) Co-authored-by: Ayaka Booker <ayakabooker@gmail.com> Co-authored-by: Chaotic Lawful <habitica@eusebius.fr> Co-authored-by: FingerTiao <787170918@qq.com> Co-authored-by: Jaime Martí <jaumemarti77@icloud.com> Co-authored-by: Jan Freihöfer <jan.stauch.is@gmail.com> Co-authored-by: Karmelkowy <kicimeow.karmelio@gmail.com> Co-authored-by: Lio Zam <zerofux@web.de> Co-authored-by: Mika <isekai.chr@gmail.com> Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com> Co-authored-by: Summer_GUI <heyang94@163.com> Co-authored-by: Vera <verasmolinap@gmail.com> Co-authored-by: Weblate <noreply@weblate.org> Co-authored-by: Zhi Hao Li <zhihaoli000@gmail.com> Co-authored-by: Zuz Q <zuzannakunik@gmail.com> Co-authored-by: innnko <ayakabooker@gmail.com> Co-authored-by: 吳昀錡 <J1120241@gm.fdhs.tyc.edu.tw> Co-authored-by: 潘致翰 <happyq0908@gmail.com> Translate-URL: https://translate.habitica.com/projects/habitica/achievements/es/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pl/ Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/character/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/character/tr/ Translate-URL: https://translate.habitica.com/projects/habitica/content/da/ Translate-URL: https://translate.habitica.com/projects/habitica/content/sr/ Translate-URL: https://translate.habitica.com/projects/habitica/death/hr/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/cs/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/es_419/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/ko/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/ Translate-URL: https://translate.habitica.com/projects/habitica/front/bg/ Translate-URL: https://translate.habitica.com/projects/habitica/front/cs/ Translate-URL: https://translate.habitica.com/projects/habitica/front/da/ Translate-URL: https://translate.habitica.com/projects/habitica/front/de/ Translate-URL: https://translate.habitica.com/projects/habitica/front/en_GB/ Translate-URL: https://translate.habitica.com/projects/habitica/front/es/ Translate-URL: https://translate.habitica.com/projects/habitica/front/fr/ Translate-URL: https://translate.habitica.com/projects/habitica/front/it/ Translate-URL: https://translate.habitica.com/projects/habitica/front/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/front/pt/ Translate-URL: https://translate.habitica.com/projects/habitica/front/ro/ Translate-URL: https://translate.habitica.com/projects/habitica/front/sk/ Translate-URL: https://translate.habitica.com/projects/habitica/front/sr/ Translate-URL: https://translate.habitica.com/projects/habitica/front/sv/ Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/cs/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/da/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/he/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/hr/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/ko/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/ro/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/sk/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/sr/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/sv/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/generic/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/cs/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/da/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/en_GB/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/es_419/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/ko/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/ro/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/sk/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/sr/ Translate-URL: https://translate.habitica.com/projects/habitica/groups/sv/ Translate-URL: https://translate.habitica.com/projects/habitica/limited/bg/ Translate-URL: https://translate.habitica.com/projects/habitica/limited/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/limited/ru/ Translate-URL: https://translate.habitica.com/projects/habitica/messages/sr/ Translate-URL: https://translate.habitica.com/projects/habitica/npc/cs/ Translate-URL: https://translate.habitica.com/projects/habitica/npc/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/npc/pl/ Translate-URL: https://translate.habitica.com/projects/habitica/overview/hr/ Translate-URL: https://translate.habitica.com/projects/habitica/overview/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/pets/cs/ Translate-URL: https://translate.habitica.com/projects/habitica/pets/da/ Translate-URL: https://translate.habitica.com/projects/habitica/quests/en_GB/ Translate-URL: https://translate.habitica.com/projects/habitica/quests/es/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/bg/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/cs/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/da/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/en_GB/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/he/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hr/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/id/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/it/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ko/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pl/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ro/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sk/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sr/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sv/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/bg/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/da/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/de/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/en_GB/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/es/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/hr/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/sv/ Translate-URL: https://translate.habitica.com/projects/habitica/settings/uk/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/en_GB/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hr/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ko/ Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/ Translate-URL: https://translate.habitica.com/projects/habitica/tasks/cs/ Translation: Habitica/Achievements Translation: Habitica/Backgrounds Translation: Habitica/Character Translation: Habitica/Content Translation: Habitica/Death Translation: Habitica/Faq Translation: Habitica/Front Translation: Habitica/Gear Translation: Habitica/Generic Translation: Habitica/Groups Translation: Habitica/Limited Translation: Habitica/Messages Translation: Habitica/Npc Translation: Habitica/Overview Translation: Habitica/Pets Translation: Habitica/Quests Translation: Habitica/Questscontent Translation: Habitica/Settings Translation: Habitica/Subscriber Translation: Habitica/Tasks commit1dde2674f6Author: Kalista Payne <sabrecat@gmail.com> Date: Mon Jun 16 16:43:56 2025 -0500 fix(content): don't filter out the thing we want commit76122a8889Author: Kalista Payne <sabrecat@gmail.com> Date: Wed Jun 4 14:28:27 2025 -0500 fix(mobile): provide Challenge categories via API commit9e309a875eAuthor: Kalista Payne <kalista@habitica.com> Date: Mon Jul 28 14:15:00 2025 -0500 5.38.0 commit09e3a394b8Author: Kalista Payne <kalista@habitica.com> Date: Mon Jul 28 14:06:45 2025 -0500 5.37.3 commiteba263360fAuthor: Weblate <noreply@weblate.org> Date: Mon Jul 28 21:03:17 2025 +0200 Translated using Weblate (German) Currently translated at 100.0% (134 of 134 strings) Translated using Weblate (German) Currently translated at 100.0% (134 of 134 strings) Translated using Weblate (German) Currently translated at 100.0% (3377 of 3377 strings) Translated using Weblate (German) Currently translated at 100.0% (3377 of 3377 strings) Translated using Weblate (German) Currently translated at 100.0% (3377 of 3377 strings) Translated using Weblate (German) Currently translated at 100.0% (243 of 243 strings) Translated using Weblate (German) Currently translated at 100.0% (184 of 184 strings) Translated using Weblate (German) Currently translated at 98.6% (850 of 862 strings) Translated using Weblate (German) Currently translated at 99.8% (3373 of 3377 strings) Translated using Weblate (German) Currently translated at 99.8% (3373 of 3377 strings) Translated using Weblate (German) Currently translated at 99.8% (3373 of 3377 strings) Translated using Weblate (German) Currently translated at 99.5% (3361 of 3377 strings) Translated using Weblate (German) Currently translated at 99.5% (3361 of 3377 strings) Translated using Weblate (German) Currently translated at 99.5% (3361 of 3377 strings) Translated using Weblate (German) Currently translated at 99.4% (3360 of 3377 strings) Translated using Weblate (German) Currently translated at 100.0% (184 of 184 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (185 of 185 strings) Translated using Weblate (Polish) Currently translated at 67.1% (579 of 862 strings) Translated using Weblate (Polish) Currently translated at 67.1% (579 of 862 strings) Translated using Weblate (Polish) Currently translated at 100.0% (91 of 91 strings) Translated using Weblate (Polish) Currently translated at 100.0% (91 of 91 strings) Translated using Weblate (German) Currently translated at 100.0% (184 of 184 strings) Translated using Weblate (German) Currently translated at 100.0% (184 of 184 strings) Translated using Weblate (German) Currently translated at 100.0% (184 of 184 strings) Translated using Weblate (German) Currently translated at 100.0% (184 of 184 strings) Translated using Weblate (German) Currently translated at 100.0% (184 of 184 strings) Translated using Weblate (German) Currently translated at 100.0% (245 of 245 strings) Translated using Weblate (German) Currently translated at 100.0% (47 of 47 strings) Translated using Weblate (German) Currently translated at 100.0% (193 of 193 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 14.2% (35 of 245 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 13.8% (34 of 245 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 13.0% (32 of 245 strings) Translated using Weblate (Hebrew) Currently translated at 2.0% (5 of 245 strings) Translated using Weblate (Hebrew) Currently translated at 66.1% (570 of 862 strings) Translated using Weblate (Portuguese) Currently translated at 54.1% (1830 of 3377 strings) Co-authored-by: FingerTiao <787170918@qq.com> Co-authored-by: Jaime Martí <jaumemarti77@icloud.com> Co-authored-by: Jan Freihöfer <jan.stauch.is@gmail.com> Co-authored-by: Jonathan Niessen <37.friedrich@gmail.com> Co-authored-by: Karmelkowy <kicimeow.karmelio@gmail.com> Co-authored-by: Katharina <katharinaanna.wilding@gmail.com> Co-authored-by: Laura Fleckenstein <fleckenstein_laura@web.de> Co-authored-by: Omer I.S <omeritzicschwartz@gmail.com> Co-authored-by: Remigiusz Haziak <haziakremigiusz@gmail.com> Co-authored-by: Uwe B <hbtca@tunixgut.de> Co-authored-by: Weblate <noreply@weblate.org> Co-authored-by: Wellinton Cardoso <wmcardoso1@hotmail.com> Co-authored-by: cloudzzy <truskawka412@gmail.com> Co-authored-by: 吳昀錡 <J1120241@gm.fdhs.tyc.edu.tw> Translate-URL: https://translate.habitica.com/projects/habitica/character/de/ Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pl/ Translate-URL: https://translate.habitica.com/projects/habitica/contrib/de/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/de/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/he/ Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/ Translate-URL: https://translate.habitica.com/projects/habitica/front/de/ Translate-URL: https://translate.habitica.com/projects/habitica/front/es/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/ Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt/ Translate-URL: https://translate.habitica.com/projects/habitica/generic/de/ Translate-URL: https://translate.habitica.com/projects/habitica/npc/de/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/he/ Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pl/ Translation: Habitica/Character Translation: Habitica/Communityguidelines Translation: Habitica/Contrib Translation: Habitica/Faq Translation: Habitica/Front Translation: Habitica/Gear Translation: Habitica/Generic Translation: Habitica/Npc Translation: Habitica/Questscontent commit9550eec718Author: Phillip Thelen <phillip@habitica.com> Date: Mon Jul 28 16:50:38 2025 +0200 Fix 500 when deleting a very old group plan account (#15481) commitf267eb67e9Author: Kalista Payne <kalista@habitica.com> Date: Tue Jul 29 14:12:35 2025 -0500 fix(static): add back missing div for show/hide commit28251f42abAuthor: Kalista Payne <kalista@habitica.com> Date: Thu Jul 24 22:59:01 2025 -0500 feat(privacy): preview page * feat(privacy): respect Global Privacy Control * fix(lint): remove unused component * fix(test): test user opts in to tracking * fix(test): add user pref to more contexts * fix(test): final spot in api-unit * fix(tests): update integrations * chore(privacy): add paragraph to s1, retire separate preview pages * fix(build): route copypasta * fix(router): lingering dead import --------- Signed-off-by: Kalista Payne <sabrecat@gmail.com> Signed-off-by: Kalista Payne <kalista@habitica.com> Co-authored-by: Phillip Thelen <phillip@habitica.com> Co-authored-by: CuriousMagpie <eilatan@gmail.com>
767 lines
26 KiB
JavaScript
767 lines
26 KiB
JavaScript
import { Schema } from 'mongoose';
|
|
import validator from 'validator';
|
|
import shared from '../../../common';
|
|
import { // eslint-disable-line import/no-cycle
|
|
getDefaultOwnedGear,
|
|
} from '../../libs/items/utils';
|
|
import {
|
|
RESTRICTED_EMAIL_DOMAINS,
|
|
isRestrictedEmailDomain,
|
|
} from '../../libs/auth/utils';
|
|
import { schema as PushDeviceSchema } from '../pushDevice';
|
|
import { schema as SubscriptionPlanSchema } from '../subscriptionPlan';
|
|
import { schema as TagSchema } from '../tag';
|
|
import { schema as UserNotificationSchema } from '../userNotification';
|
|
import { schema as WebhookSchema } from '../webhook';
|
|
import { model as Blocker } from '../blocker';
|
|
|
|
const BLOCKED_EMAILS = [];
|
|
|
|
Blocker.watchBlockers({
|
|
type: 'email',
|
|
area: 'full',
|
|
}, {
|
|
initial: true,
|
|
}).on('change', async change => {
|
|
const { operation, blocker: { value } } = change;
|
|
if (operation === 'add') {
|
|
if (value && !BLOCKED_EMAILS.includes(value)) {
|
|
BLOCKED_EMAILS.push(value);
|
|
}
|
|
} else if (operation === 'delete') {
|
|
const index = BLOCKED_EMAILS.indexOf(value);
|
|
if (index !== -1) {
|
|
BLOCKED_EMAILS.splice(index, 1);
|
|
}
|
|
}
|
|
});
|
|
|
|
// User schema definition
|
|
export const UserSchema = new Schema({
|
|
apiToken: {
|
|
$type: String,
|
|
default: shared.uuid,
|
|
},
|
|
|
|
auth: {
|
|
blocked: Boolean,
|
|
facebook: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
google: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
apple: { $type: Schema.Types.Mixed, default: () => ({}) },
|
|
local: {
|
|
email: {
|
|
$type: String,
|
|
validate: [{
|
|
validator: v => validator.isEmail(v),
|
|
message: shared.i18n.t('invalidEmail'),
|
|
}, {
|
|
validator (email) {
|
|
return !isRestrictedEmailDomain(email);
|
|
},
|
|
message: shared.i18n.t('invalidEmailDomain', { domains: RESTRICTED_EMAIL_DOMAINS.join(', ') }),
|
|
}, {
|
|
validator (email) {
|
|
const lowercaseEmail = email.toLowerCase();
|
|
return BLOCKED_EMAILS.every(block => lowercaseEmail.indexOf(block) === -1);
|
|
},
|
|
message: shared.i18n.t('emailBlockedRegistration'),
|
|
}],
|
|
},
|
|
username: {
|
|
$type: String,
|
|
},
|
|
// Store a lowercase version of username to check for duplicates
|
|
lowerCaseUsername: String,
|
|
hashed_password: String, // eslint-disable-line camelcase
|
|
// Legacy password are hashed with SHA1, new ones with bcrypt
|
|
passwordHashMethod: {
|
|
$type: String,
|
|
enum: ['bcrypt', 'sha1'],
|
|
},
|
|
salt: String, // Salt for SHA1 encrypted passwords, not stored for bcrypt,
|
|
// Used to validate password reset codes and make sure only the most recent one can be used
|
|
passwordResetCode: String,
|
|
},
|
|
timestamps: {
|
|
created: { $type: Date, default: Date.now },
|
|
loggedin: { $type: Date, default: Date.now },
|
|
updated: { $type: Date, default: Date.now },
|
|
},
|
|
},
|
|
// We want to know *every* time an object updates.
|
|
// Mongoose uses __v to designate when an object contains arrays which
|
|
// have been updated, but we want *every* update
|
|
_v: { $type: Number, default: 0 },
|
|
migration: String,
|
|
achievements: {
|
|
originalUser: Boolean,
|
|
habitSurveys: Number,
|
|
ultimateGearSets: {
|
|
healer: { $type: Boolean, default: false },
|
|
wizard: { $type: Boolean, default: false },
|
|
rogue: { $type: Boolean, default: false },
|
|
warrior: { $type: Boolean, default: false },
|
|
},
|
|
beastMaster: Boolean,
|
|
beastMasterCount: Number,
|
|
mountMaster: Boolean,
|
|
mountMasterCount: Number,
|
|
triadBingo: Boolean,
|
|
triadBingoCount: Number,
|
|
veteran: Boolean,
|
|
snowball: Number,
|
|
spookySparkles: Number,
|
|
shinySeed: Number,
|
|
seafoam: Number,
|
|
streak: { $type: Number, default: 0 },
|
|
challenges: Array,
|
|
quests: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
rebirths: Number,
|
|
rebirthLevel: Number,
|
|
perfect: { $type: Number, default: 0 },
|
|
habitBirthdays: Number,
|
|
valentine: Number,
|
|
nye: Number,
|
|
habiticaDays: Number,
|
|
greeting: Number,
|
|
thankyou: Number,
|
|
costumeContests: Number,
|
|
birthday: Number,
|
|
partyUp: Boolean,
|
|
partyOn: Boolean,
|
|
congrats: Number,
|
|
getwell: Number,
|
|
goodluck: Number,
|
|
royallyLoyal: Boolean,
|
|
joinedGuild: Boolean,
|
|
joinedChallenge: Boolean,
|
|
invitedFriend: Boolean,
|
|
lostMasterclasser: Boolean,
|
|
mindOverMatter: Boolean,
|
|
justAddWater: Boolean,
|
|
backToBasics: Boolean,
|
|
allYourBase: Boolean,
|
|
dustDevil: Boolean,
|
|
aridAuthority: Boolean,
|
|
kickstarter2019: Boolean,
|
|
monsterMagus: Boolean,
|
|
undeadUndertaker: Boolean,
|
|
primedForPainting: Boolean,
|
|
pearlyPro: Boolean,
|
|
tickledPink: Boolean,
|
|
rosyOutlook: Boolean,
|
|
bugBonanza: Boolean,
|
|
bareNecessities: Boolean,
|
|
freshwaterFriends: Boolean,
|
|
goodAsGold: Boolean,
|
|
allThatGlitters: Boolean,
|
|
boneCollector: Boolean,
|
|
skeletonCrew: Boolean,
|
|
seeingRed: Boolean,
|
|
redLetterDay: Boolean,
|
|
legendaryBestiary: Boolean,
|
|
seasonalSpecialist: Boolean,
|
|
violetsAreBlue: Boolean,
|
|
wildBlueYonder: Boolean,
|
|
domesticated: Boolean,
|
|
shadyCustomer: Boolean,
|
|
shadeOfItAll: Boolean,
|
|
zodiacZookeeper: Boolean,
|
|
birdsOfAFeather: Boolean,
|
|
reptacularRumble: Boolean,
|
|
woodlandWizard: Boolean,
|
|
boneToPick: Boolean,
|
|
polarPro: Boolean,
|
|
plantParent: Boolean,
|
|
dinosaurDynasty: Boolean,
|
|
bonelessBoss: Boolean,
|
|
duneBuddy: Boolean,
|
|
roughRider: Boolean,
|
|
rodentRuler: Boolean,
|
|
cats: Boolean,
|
|
// Onboarding Guide
|
|
createdTask: Boolean,
|
|
completedTask: Boolean,
|
|
hatchedPet: Boolean,
|
|
fedPet: Boolean,
|
|
purchasedEquipment: Boolean,
|
|
groupsBeta2022: Boolean,
|
|
},
|
|
|
|
backer: {
|
|
tier: Number,
|
|
npc: String,
|
|
tokensApplied: Boolean,
|
|
},
|
|
|
|
contributor: {
|
|
// 1-9, see https://habitica.fandom.com/wiki/Contributor_Rewards
|
|
level: {
|
|
$type: Number,
|
|
min: 0,
|
|
max: 9,
|
|
},
|
|
admin: Boolean,
|
|
// Artisan, Friend, Blacksmith, etc
|
|
text: String,
|
|
// a markdown textarea to list their contributions + links
|
|
contributions: String,
|
|
// user can own Critical Hammer of Bug-Crushing if this has a truthy value
|
|
critical: String,
|
|
},
|
|
permissions: {
|
|
fullAccess: Boolean, // esentially what was previously contributor.admin. Can do everything
|
|
news: Boolean,
|
|
userSupport: Boolean, // access User Support feature in Admin Panel
|
|
challengeAdmin: Boolean, // Can manage and administrate challenges
|
|
moderator: Boolean, // Can ban, flag users and manage social spaces
|
|
accessControl: Boolean, // Can manage IP and client blockers
|
|
coupons: Boolean, // Can generate and request coupons
|
|
},
|
|
balance: { $type: Number, default: 0 },
|
|
|
|
purchased: {
|
|
ads: { $type: Boolean, default: false },
|
|
// eg, {skeleton: true, pumpkin: true, eb052b: true}
|
|
skin: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
hair: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
shirt: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
background: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
txnCount: { $type: Number, default: 0 },
|
|
mobileChat: Boolean,
|
|
plan: {
|
|
$type: SubscriptionPlanSchema,
|
|
default: () => ({}),
|
|
},
|
|
},
|
|
|
|
flags: {
|
|
customizationsNotification: { $type: Boolean, default: false },
|
|
showTour: { $type: Boolean, default: true },
|
|
tour: {
|
|
// -1 indicates "uninitiated", -2 means "complete",
|
|
// any other number is the current tour step (0-index)
|
|
intro: { $type: Number, default: -1 },
|
|
classes: { $type: Number, default: -1 },
|
|
stats: { $type: Number, default: -1 },
|
|
tavern: { $type: Number, default: -1 },
|
|
party: { $type: Number, default: -1 },
|
|
guilds: { $type: Number, default: -1 },
|
|
challenges: { $type: Number, default: -1 },
|
|
market: { $type: Number, default: -1 },
|
|
pets: { $type: Number, default: -1 },
|
|
mounts: { $type: Number, default: -1 },
|
|
hall: { $type: Number, default: -1 },
|
|
equipment: { $type: Number, default: -1 },
|
|
groupPlans: { $type: Number, default: -1 },
|
|
},
|
|
tutorial: {
|
|
common: {
|
|
habits: { $type: Boolean, default: false },
|
|
dailies: { $type: Boolean, default: false },
|
|
todos: { $type: Boolean, default: false },
|
|
rewards: { $type: Boolean, default: false },
|
|
party: { $type: Boolean, default: false },
|
|
pets: { $type: Boolean, default: false },
|
|
gems: { $type: Boolean, default: false },
|
|
skills: { $type: Boolean, default: false },
|
|
classes: { $type: Boolean, default: false },
|
|
tavern: { $type: Boolean, default: false },
|
|
equipment: { $type: Boolean, default: false },
|
|
items: { $type: Boolean, default: false },
|
|
mounts: { $type: Boolean, default: false },
|
|
inbox: { $type: Boolean, default: false },
|
|
stats: { $type: Boolean, default: false },
|
|
},
|
|
ios: {
|
|
addTask: { $type: Boolean, default: false },
|
|
editTask: { $type: Boolean, default: false },
|
|
deleteTask: { $type: Boolean, default: false },
|
|
filterTask: { $type: Boolean, default: false },
|
|
groupPets: { $type: Boolean, default: false },
|
|
inviteParty: { $type: Boolean, default: false },
|
|
reorderTask: { $type: Boolean, default: false },
|
|
},
|
|
},
|
|
dropsEnabled: { $type: Boolean, default: false }, // unused
|
|
itemsEnabled: { $type: Boolean, default: false },
|
|
lastNewStuffRead: { $type: String, default: '' },
|
|
// The newStuff field was changed to be a computed property when returning the user in json,
|
|
// so that it doesn't have to be updated for each bailey post.
|
|
// See models/user/hooks#toJSONTransform
|
|
// newStuff: { $type: Boolean, default: false },
|
|
rewrite: { $type: Boolean, default: true },
|
|
classSelected: { $type: Boolean, default: false },
|
|
mathUpdates: Boolean,
|
|
rebirthEnabled: { $type: Boolean, default: false },
|
|
lastFreeRebirth: Date,
|
|
levelDrops: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
chatRevoked: Boolean,
|
|
chatShadowMuted: Boolean,
|
|
// Used to track the status of recapture emails sent to each user,
|
|
// can be 0 - no email sent - 1, 2, 3 or 4 - 4 means no more email will be sent to the user
|
|
recaptureEmailsPhase: { $type: Number, default: 0 },
|
|
// Needed to track the tip to send inside the email
|
|
weeklyRecapEmailsPhase: { $type: Number, default: 0 },
|
|
// Used to track when the next weekly recap should be sent
|
|
lastWeeklyRecap: { $type: Date, default: Date.now },
|
|
// Used to enable weekly recap emails as users login
|
|
lastWeeklyRecapDiscriminator: Boolean,
|
|
onboardingEmailsPhase: String, // Keep track of the latest onboarding email sent
|
|
communityGuidelinesAccepted: { $type: Boolean, default: false },
|
|
cronCount: { $type: Number, default: 0 },
|
|
welcomed: { $type: Boolean, default: false },
|
|
armoireEnabled: { $type: Boolean, default: true },
|
|
armoireOpened: { $type: Boolean, default: false },
|
|
armoireEmpty: { $type: Boolean, default: false },
|
|
cardReceived: { $type: Boolean, default: false },
|
|
warnedLowHealth: { $type: Boolean, default: false },
|
|
verifiedUsername: { $type: Boolean, default: false },
|
|
thirdPartyTools: { $type: Date },
|
|
initializedUserHistory: { $type: Boolean, default: false },
|
|
},
|
|
|
|
history: {
|
|
exp: Array, // [{date: Date, value: Number}], // big peformance issues if these are defined
|
|
todos: Array, // [{data: Date, value: Number}] // big peformance issues if these are defined
|
|
},
|
|
|
|
items: {
|
|
gear: {
|
|
owned: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => getDefaultOwnedGear(),
|
|
},
|
|
|
|
equipped: {
|
|
weapon: String,
|
|
armor: { $type: String, default: 'armor_base_0' },
|
|
head: { $type: String, default: 'head_base_0' },
|
|
shield: { $type: String, default: 'shield_base_0' },
|
|
back: String,
|
|
headAccessory: String,
|
|
eyewear: String,
|
|
body: String,
|
|
},
|
|
costume: {
|
|
weapon: String,
|
|
armor: { $type: String, default: 'armor_base_0' },
|
|
head: { $type: String, default: 'head_base_0' },
|
|
shield: { $type: String, default: 'shield_base_0' },
|
|
back: String,
|
|
headAccessory: String,
|
|
eyewear: String,
|
|
body: String,
|
|
},
|
|
},
|
|
|
|
special: {
|
|
snowball: { $type: Number, default: 0 },
|
|
spookySparkles: { $type: Number, default: 0 },
|
|
shinySeed: { $type: Number, default: 0 },
|
|
seafoam: { $type: Number, default: 0 },
|
|
valentine: { $type: Number, default: 0 },
|
|
valentineReceived: Array, // array of strings, by sender name
|
|
nye: { $type: Number, default: 0 },
|
|
nyeReceived: Array,
|
|
greeting: { $type: Number, default: 0 },
|
|
greetingReceived: Array,
|
|
thankyou: { $type: Number, default: 0 },
|
|
thankyouReceived: Array,
|
|
birthday: { $type: Number, default: 0 },
|
|
birthdayReceived: Array,
|
|
congrats: { $type: Number, default: 0 },
|
|
congratsReceived: Array,
|
|
getwell: { $type: Number, default: 0 },
|
|
getwellReceived: Array,
|
|
goodluck: { $type: Number, default: 0 },
|
|
goodluckReceived: Array,
|
|
},
|
|
|
|
// -------------- Animals -------------------
|
|
// Complex bit here. The result looks like:
|
|
// pets: {
|
|
// 'Wolf-Desert': 0, // 0 means does not own
|
|
// 'PandaCub-Red': 10, // Number represents "Growth Points"
|
|
// etc...
|
|
// }
|
|
pets: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
currentPet: { $type: String, default: '' }, // Cactus-Desert
|
|
|
|
// eggs: {
|
|
// 'PandaCub': 0, // 0 indicates "doesn't own"
|
|
// 'Wolf': 5 // Number indicates "stacking"
|
|
// }
|
|
eggs: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
|
|
// hatchingPotions: {
|
|
// 'Desert': 0, // 0 indicates "doesn't own"
|
|
// 'CottonCandyBlue': 5 // Number indicates "stacking"
|
|
// }
|
|
hatchingPotions: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
|
|
// Food: {
|
|
// 'Watermelon': 0, // 0 indicates "doesn't own"
|
|
// 'RottenMeat': 5 // Number indicates "stacking"
|
|
// }
|
|
food: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
|
|
// mounts: {
|
|
// 'Wolf-Desert': true,
|
|
// 'PandaCub-Red': false,
|
|
// etc...
|
|
// }
|
|
mounts: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
currentMount: { $type: String, default: '' }, // Cactus-Desert
|
|
|
|
// Quests: {
|
|
// 'boss_0': 0, // 0 indicates "doesn't own"
|
|
// 'collection_honey': 5 // Number indicates "stacking"
|
|
// }
|
|
quests: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
|
|
lastDrop: {
|
|
date: { $type: Date, default: Date.now },
|
|
count: { $type: Number, default: 0 },
|
|
},
|
|
},
|
|
|
|
lastCron: { $type: Date, default: Date.now },
|
|
_cronSignature: { $type: String, default: 'NOT_RUNNING' }, // Private property used to avoid double cron
|
|
// Lock property to avoid double subscription. Not strictly private because we query on it
|
|
_subSignature: { $type: String, default: 'NOT_RUNNING' },
|
|
|
|
// {GROUP_ID: Boolean}, represents whether they have unseen chat messages
|
|
newMessages: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
|
|
challenges: [{ $type: String, ref: 'Challenge', validate: [v => validator.isUUID(v), 'Invalid uuid for user challenges.'] }],
|
|
|
|
invitations: {
|
|
// Using an array without validation because otherwise mongoose
|
|
// treat this as a subdocument and applies _id by default
|
|
// Schema is (id, name, inviter, publicGuild)
|
|
// TODO one way to fix is https://mongoosejs.com/docs/guide.html#_id
|
|
guilds: { $type: Array, default: () => [] },
|
|
// Using a Mixed type because otherwise user.invitations.party = {}
|
|
// to reset invitation, causes validation to fail TODO
|
|
// schema is the same as for guild invitations (id, name, inviter)
|
|
party: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
parties: [{
|
|
id: {
|
|
$type: String,
|
|
ref: 'Group',
|
|
required: true,
|
|
validate: [v => validator.isUUID(v), 'Invalid uuid for user invitation party id.'],
|
|
},
|
|
name: {
|
|
$type: String,
|
|
required: true,
|
|
},
|
|
inviter: {
|
|
$type: String,
|
|
ref: 'User',
|
|
required: true,
|
|
validate: [v => validator.isUUID(v), 'Invalid uuid for user invitation inviter id.'],
|
|
},
|
|
cancelledPlan: {
|
|
$type: Boolean,
|
|
},
|
|
}],
|
|
},
|
|
|
|
guilds: [{ $type: String, ref: 'Group', validate: [v => validator.isUUID(v), 'Invalid uuid for user guild.'] }],
|
|
|
|
party: {
|
|
_id: { $type: String, validate: [v => validator.isUUID(v), 'Invalid uuid for user party.'], ref: 'Group' },
|
|
order: { $type: String, default: 'level' },
|
|
orderAscending: { $type: String, default: 'ascending' },
|
|
quest: {
|
|
key: String,
|
|
progress: {
|
|
up: { $type: Number, default: 0 },
|
|
down: { $type: Number, default: 0 },
|
|
collect: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
collectedItems: { $type: Number, default: 0 },
|
|
},
|
|
// When quest is done, we move it from key => completed,
|
|
// and it's a one-time flag (for modal) that they unset by clicking "ok" in browser
|
|
completed: String,
|
|
// Set to true when invite is pending, set to false when quest
|
|
// invite is accepted or rejected, quest starts, or quest is cancelled
|
|
RSVPNeeded: { $type: Boolean, default: false },
|
|
},
|
|
seeking: Date,
|
|
},
|
|
preferences: {
|
|
dayStart: {
|
|
$type: Number, default: 0, min: 0, max: 23,
|
|
},
|
|
size: { $type: String, enum: ['broad', 'slim'], default: 'slim' },
|
|
hair: {
|
|
color: { $type: String, default: 'red' },
|
|
base: { $type: Number, default: 3 },
|
|
bangs: { $type: Number, default: 1 },
|
|
beard: { $type: Number, default: 0 },
|
|
mustache: { $type: Number, default: 0 },
|
|
flower: { $type: Number, default: 1 },
|
|
},
|
|
hideHeader: { $type: Boolean, default: false },
|
|
skin: { $type: String, default: '915533' },
|
|
shirt: { $type: String, default: 'blue' },
|
|
timezoneOffset: { $type: Number, default: 0 },
|
|
sound: { $type: String, default: 'rosstavoTheme', enum: ['off', ...shared.content.audioThemes] },
|
|
chair: { $type: String, default: 'none' },
|
|
timezoneOffsetAtLastCron: Number,
|
|
language: String,
|
|
automaticAllocation: Boolean,
|
|
allocationMode: { $type: String, enum: ['flat', 'classbased', 'taskbased'], default: 'flat' },
|
|
autoEquip: { $type: Boolean, default: true },
|
|
costume: { $type: Boolean, default: false },
|
|
dateFormat: { $type: String, enum: ['MM/dd/yyyy', 'dd/MM/yyyy', 'yyyy/MM/dd'], default: 'MM/dd/yyyy' },
|
|
sleep: { $type: Boolean, default: false },
|
|
stickyHeader: { $type: Boolean, default: true },
|
|
disableClasses: { $type: Boolean, default: false },
|
|
newTaskEdit: { $type: Boolean, default: false },
|
|
// not used anymore, now the current filter is saved in preferences.activeFilter
|
|
dailyDueDefaultView: { $type: Boolean, default: false },
|
|
// deprecated, unused
|
|
advancedCollapsed: { $type: Boolean, default: false },
|
|
toolbarCollapsed: { $type: Boolean, default: false },
|
|
reverseChatOrder: { $type: Boolean, default: false },
|
|
developerMode: { $type: Boolean, default: false },
|
|
background: String,
|
|
// deprecated, unused
|
|
displayInviteToPartyWhenPartyIs1: { $type: Boolean, default: true },
|
|
webhooks: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
// For the following fields make sure to use strict
|
|
// comparison when searching for falsey values (=== false)
|
|
// As users who didn't login after these were introduced may have them undefined/null
|
|
emailNotifications: {
|
|
unsubscribeFromAll: { $type: Boolean, default: false },
|
|
newPM: { $type: Boolean, default: true },
|
|
kickedGroup: { $type: Boolean, default: true },
|
|
wonChallenge: { $type: Boolean, default: true },
|
|
giftedGems: { $type: Boolean, default: true },
|
|
giftedSubscription: { $type: Boolean, default: true },
|
|
invitedParty: { $type: Boolean, default: true },
|
|
invitedGuild: { $type: Boolean, default: true },
|
|
questStarted: { $type: Boolean, default: true },
|
|
invitedQuest: { $type: Boolean, default: true },
|
|
// remindersToLogin: {$type: Boolean, default: true},
|
|
// importantAnnouncements are in fact the recapture emails
|
|
importantAnnouncements: { $type: Boolean, default: true },
|
|
weeklyRecaps: { $type: Boolean, default: true },
|
|
onboarding: { $type: Boolean, default: true },
|
|
majorUpdates: { $type: Boolean, default: true },
|
|
subscriptionReminders: { $type: Boolean, default: true },
|
|
contentRelease: { $type: Boolean, default: true },
|
|
},
|
|
pushNotifications: {
|
|
unsubscribeFromAll: { $type: Boolean, default: false },
|
|
newPM: { $type: Boolean, default: true },
|
|
wonChallenge: { $type: Boolean, default: true },
|
|
giftedGems: { $type: Boolean, default: true },
|
|
giftedSubscription: { $type: Boolean, default: true },
|
|
invitedParty: { $type: Boolean, default: true },
|
|
invitedGuild: { $type: Boolean, default: true },
|
|
questStarted: { $type: Boolean, default: true },
|
|
invitedQuest: { $type: Boolean, default: true },
|
|
majorUpdates: { $type: Boolean, default: true },
|
|
mentionParty: { $type: Boolean, default: true },
|
|
mentionJoinedGuild: { $type: Boolean, default: true },
|
|
mentionUnjoinedGuild: { $type: Boolean, default: true },
|
|
partyActivity: { $type: Boolean, default: true },
|
|
contentRelease: { $type: Boolean, default: true },
|
|
},
|
|
suppressModals: {
|
|
levelUp: { $type: Boolean, default: false },
|
|
hatchPet: { $type: Boolean, default: false },
|
|
raisePet: { $type: Boolean, default: false },
|
|
streak: { $type: Boolean, default: false },
|
|
},
|
|
tasks: {
|
|
groupByChallenge: { $type: Boolean, default: false }, // @TODO remove? not used
|
|
confirmScoreNotes: { $type: Boolean, default: false }, // @TODO remove? not used
|
|
mirrorGroupTasks: [
|
|
{ $type: String, validate: [v => validator.isUUID(v), 'Invalid group UUID.'], ref: 'Group' },
|
|
],
|
|
activeFilter: {
|
|
habit: { $type: String, default: 'all' },
|
|
daily: { $type: String, default: 'all' },
|
|
todo: { $type: String, default: 'remaining' },
|
|
reward: { $type: String, default: 'all' },
|
|
},
|
|
},
|
|
improvementCategories: {
|
|
$type: Array,
|
|
validate: categories => {
|
|
const validCategories = ['work', 'exercise', 'healthWellness', 'school', 'teams', 'chores', 'creativity'];
|
|
const isValidCategory = categories
|
|
.every(category => validCategories.indexOf(category) !== -1);
|
|
return isValidCategory;
|
|
},
|
|
},
|
|
analyticsConsent: Boolean,
|
|
},
|
|
profile: {
|
|
blurb: String,
|
|
imageUrl: String,
|
|
name: {
|
|
$type: String,
|
|
required: true,
|
|
trim: true,
|
|
},
|
|
flags: { $type: Schema.Types.Mixed },
|
|
},
|
|
stats: {
|
|
hp: { $type: Number, default: shared.maxHealth },
|
|
mp: { $type: Number, default: 10, min: 0 },
|
|
exp: { $type: Number, default: 0 },
|
|
gp: { $type: Number, default: 0, min: 0 },
|
|
lvl: {
|
|
$type: Number,
|
|
default: 1,
|
|
min: 1,
|
|
max: shared.constants.MAX_LEVEL_HARD_CAP,
|
|
},
|
|
|
|
// Class System
|
|
class: {
|
|
$type: String, enum: shared.content.classes, default: 'warrior', required: true,
|
|
},
|
|
points: { $type: Number, default: 0, min: 0 },
|
|
str: { $type: Number, default: 0, min: 0 },
|
|
con: { $type: Number, default: 0, min: 0 },
|
|
int: { $type: Number, default: 0, min: 0 },
|
|
per: { $type: Number, default: 0, min: 0 },
|
|
buffs: {
|
|
str: { $type: Number, default: 0, min: 0 },
|
|
int: { $type: Number, default: 0, min: 0 },
|
|
per: { $type: Number, default: 0, min: 0 },
|
|
con: { $type: Number, default: 0, min: 0 },
|
|
stealth: { $type: Number, default: 0, min: 0 },
|
|
streaks: { $type: Boolean, default: false },
|
|
snowball: { $type: Boolean, default: false },
|
|
spookySparkles: { $type: Boolean, default: false },
|
|
shinySeed: { $type: Boolean, default: false },
|
|
seafoam: { $type: Boolean, default: false },
|
|
},
|
|
training: {
|
|
int: { $type: Number, default: 0 },
|
|
per: { $type: Number, default: 0 },
|
|
str: { $type: Number, default: 0 },
|
|
con: { $type: Number, default: 0 },
|
|
},
|
|
},
|
|
|
|
notifications: [UserNotificationSchema],
|
|
tags: [TagSchema],
|
|
|
|
inbox: {
|
|
// messages are stored in the Inbox collection
|
|
newMessages: { $type: Number, default: 0 },
|
|
blocks: { $type: Array, default: () => [] },
|
|
optOut: { $type: Boolean, default: false },
|
|
},
|
|
tasksOrder: {
|
|
habits: [{ $type: String, ref: 'Task' }],
|
|
dailys: [{ $type: String, ref: 'Task' }],
|
|
todos: [{ $type: String, ref: 'Task' }],
|
|
rewards: [{ $type: String, ref: 'Task' }],
|
|
},
|
|
extra: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
pushDevices: [PushDeviceSchema],
|
|
_ABtests: {
|
|
$type: Schema.Types.Mixed,
|
|
default: () => ({}),
|
|
},
|
|
webhooks: [WebhookSchema],
|
|
loginIncentives: { $type: Number, default: 0 },
|
|
invitesSent: { $type: Number, default: 0 },
|
|
|
|
// Items manually pinned by the user
|
|
pinnedItems: [{
|
|
_id: false,
|
|
path: { $type: String },
|
|
type: { $type: String },
|
|
}],
|
|
// Ordered array of shown pinned items,
|
|
// necessary for sorting because seasonal items are not stored in pinnedItems
|
|
pinnedItemsOrder: [{ $type: String }],
|
|
// Items the user manually unpinned from the ones suggested by Habitica
|
|
unpinnedItems: [{
|
|
_id: false,
|
|
path: { $type: String },
|
|
type: { $type: String },
|
|
}],
|
|
|
|
// only visible to staff and moderators
|
|
secret: {
|
|
text: String,
|
|
},
|
|
}, {
|
|
skipVersioning: { notifications: true },
|
|
strict: true,
|
|
minimize: false, // So empty objects are returned
|
|
typeKey: '$type', // So that we can use fields named `type`
|
|
});
|
|
|
|
export default UserSchema; // fallback export until all imports using the Named one
|