Compare commits

..

135 Commits

Author SHA1 Message Date
SabreCat
82f34ed437 v4.103.0 2019-06-25 15:19:00 +00:00
SabreCat
3b268287b1 chore(sprites): compile 2019-06-25 15:14:56 +00:00
SabreCat
0e253fbb06 feat(content): mystery items 2019-06-25 15:13:08 +00:00
Sabe Jones
043696c225 4.102.0 2019-06-20 15:13:55 -05:00
Sabe Jones
69c2b0378f chore(sprites): compile 2019-06-20 15:13:27 -05:00
Sabe Jones
fcfc44fd13 feat(content): Watery Potions
also fixes some sprite issues
2019-06-20 15:13:19 -05:00
Sabe Jones
76773d27c6 4.101.1 2019-06-18 23:29:07 -05:00
Sabe Jones
b3e81177ec fix(content): correct Mage weapon price, smarten Rebirth banner 2019-06-18 23:29:00 -05:00
Sabe Jones
020726f6f8 4.101.0 2019-06-18 20:05:15 -05:00
Sabe Jones
9f9b0c73c3 fix(event): 30 days hath July? uh, no 2019-06-18 19:49:55 -05:00
Sabe Jones
2c03c114da chore(sprites): compile 2019-06-18 19:20:23 -05:00
Sabe Jones
8e9500fed2 feat(event): Summer Splash 2019 2019-06-18 19:20:14 -05:00
Sabe Jones
377f849f9e Merge branch 'free-rebirth-limit' into release 2019-06-18 16:02:12 -05:00
Sabe Jones
8bbada540d feat(rebirth): add banner to buy modal 2019-06-18 15:24:50 -05:00
Matteo Pagliazzi
ee867a9d30 add tests 2019-06-17 21:04:22 +02:00
Matteo Pagliazzi
14a4f42b50 indentation 2019-06-17 20:47:55 +02:00
Matteo Pagliazzi
2cbb0a85a4 limit free rebirth to once every 45 days 2019-06-17 20:37:18 +02:00
Sabe Jones
975e068fe2 4.100.2 2019-06-14 12:35:37 +00:00
Sabe Jones
5a5f1d6895 Revert "Performance: Inbox Paging / loading (#11157)"
This reverts commit 5630e8cc8e.
2019-06-14 12:34:38 +00:00
Sabe Jones
1db0cda6b1 4.100.1 2019-06-13 13:14:32 -05:00
Sabe Jones
f987585cf1 fix(quests): content corrections 2019-06-13 13:13:55 -05:00
Sabe Jones
0325ae9636 fix(avatar): style correction 2019-06-13 13:10:06 -05:00
Sabe Jones
4c4fbfe790 fix(backgrounds): put errant BGs on 3x3 grid 2019-06-13 12:22:28 -05:00
Sabe Jones
6b59262e3e Challenge privacy fix (#11222)
* fix(challenges): filter out private content API-side

* fix(challenges): cleaner fix + test
2019-06-13 09:27:47 -05:00
negue
5630e8cc8e Performance: Inbox Paging / loading (#11157)
* load messages per conversation

* only sort ones in ui

*  add contributor to message

* fix correct message layout/message

* mugenScroll on chatMessages

* fix lint, no mugen-scroll, use own scroll handler

* fix height / margin of modal + use button to load more

* fix tests

* user data from inbox

* style "load earlier messages"

*  move mapMessage to the inbox api result / extract sentMessage of members-api-controller

* fix test back

* fix test

* keep last scroll position

* just set the Id of the returned message instead of all other properties

* fix add new messages (buttons were hidden) + load more

* item-mounted debounce to trigger the re-scrolling
2019-06-13 15:18:50 +02:00
Matteo Pagliazzi
5268bbb8a9 fix(subscription): reset lastReminderDate when creating a new subscription 2019-06-12 17:21:08 +02:00
Sabe Jones
34faf2fd50 4.100.0 2019-06-11 15:27:34 -05:00
Sabe Jones
896950dbf0 chore(sprites): compile 2019-06-11 15:26:50 -05:00
Sabe Jones
424c50f2a4 chore(news): Bailey
also direct mod contact form to HTTPS
2019-06-11 15:26:38 -05:00
Sabe Jones
f235a64d96 Merge branch 'sabrecat/pet-achievements' into release 2019-06-11 15:12:34 -05:00
Sabe Jones
286abe334e fix(notifications): add missing enum 2019-06-11 14:50:17 -05:00
Sabe Jones
81333a3074 feat(notifications): alert user to achievements 2019-06-11 13:06:00 -05:00
Matteo Pagliazzi
3c31945524 Email subs reminders release (#11216)
* add subscriptions reminders emails

* add field to record the last sub reminder
2019-06-10 22:37:30 +02:00
Sabe Jones
103945f7c5 feat(content): Dolphin Pet Quest
and revise Mind Over Matter achievement
2019-06-10 14:42:38 -05:00
Sabe Jones
b9d9a17aca 4.99.1 2019-06-06 15:54:18 -05:00
Sabe Jones
a0b6f576d2 Merge branch 'release' into develop 2019-06-04 16:49:17 -05:00
Sabe Jones
30036963b1 4.99.0 2019-06-04 16:48:53 -05:00
Sabe Jones
e1fe48bee4 chore(sprites): compile 2019-06-04 16:48:16 -05:00
Sabe Jones
77b19ffe97 feat(content): Armoire items and Backgrounds June 2019 2019-06-04 16:48:05 -05:00
Sabe Jones
e9a5e084fc fix(tests): use baseline quest with fewer update calls 2019-06-04 16:09:01 -05:00
Sabe Jones
12250a93f1 feat(basic-auth): allow multiple auth pairs (#11204) 2019-06-04 15:52:25 -05:00
Sabe Jones
7094e75dd8 feat(achievements): new pet-related cheevos 2019-06-04 10:31:25 -05:00
Sabe Jones
74c93955f8 Merge branch 'release' into develop 2019-06-03 15:22:54 -05:00
Sabe Jones
e4b2ef6599 4.98.1 2019-06-03 15:22:30 -05:00
Sabe Jones
a26c2bce23 chore(date): Bailey and disable potions 2019-06-03 15:22:21 -05:00
Matteo Pagliazzi
6cd00897ed fix(deps): do not update minor changes 2019-06-02 12:19:11 +02:00
greenkeeper[bot]
3c442318d8 Update js2xmlparser to the latest version 🚀 (#11019)
* fix(package): update js2xmlparser to version 4.0.0

* chore(package): update lockfile package-lock.json

* fix package-lock

* fix js2xmlparser usage
2019-06-01 14:45:05 +02:00
greenkeeper[bot]
fd3f7e2b0e Update pageres to the latest version 🚀 (#10981)
* fix(package): update pageres to version 5.0.0

* chore(package): update lockfile package-lock.json
2019-06-01 14:40:53 +02:00
Matteo Pagliazzi
6f0bd0b913 fix(package-lock.json): fix missing entry 2019-06-01 13:22:00 +02:00
greenkeeper[bot]
5b7621ceb3 fix(package): update merge-stream to version 2.0.0 (#11190) 2019-06-01 13:19:01 +02:00
greenkeeper[bot]
b8b414cae5 Update chromedriver to the latest version 🚀 (#11116)
* chore(package): update chromedriver to version 73.0.0

* chore(package): update lockfile package-lock.json
2019-06-01 09:48:52 +02:00
Sabe Jones
783338f1f0 fix(strings): misformatted variables in pt_BR 2019-05-31 14:56:26 -05:00
HydeHunter2
fb6ca0c73b Add chevron buttons to sub-items in the menu (#11158)
* Add arrow buttons to sub-items

* Fix mistyped in width

* Add 'v-once' to never changing elements(icons)

* Add animation and ability to close tabs

* Fix typo in rotate

* Only 1 opened tab

* Add animation to dropdown elements

* Close menu after clicking tab

* Improved closing the menu after clicking on links

* Remove browser prefixes

* Add 'active' class to some tabs after clicking

* Return ability to open tabs from Desktop

* pin 'currency-tray' at top of menu

* Revert "pin 'currency-tray' at top of menu"

This reverts commit 12d4144a11.

* Fix display of 'Groups' sub-items
2019-05-31 12:51:29 +02:00
greenkeeper[bot]
5792f54aa0 Update validator to the latest version 🚀 (#11188)
* fix(package): update validator to version 11.0.0

* chore(package): update lockfile package-lock.json
2019-05-31 12:41:58 +02:00
greenkeeper[bot]
3c943c8f23 Update gulp-imagemin to the latest version 🚀 (#11195)
* fix(package): update gulp-imagemin to version 6.0.0

* chore(package): update lockfile package-lock.json
2019-05-31 12:28:38 +02:00
garayj
4b2fd60e47 FIX Guild suggestion incorrectly identifies account as new #11159 (#11197)
* Changed the suggested guild string to a translatable string and made changes so that the string only shows within 60 days of user profile creation.

* Removed whitespace.
2019-05-31 12:26:15 +02:00
Alexey Pyltsyn
75281ab638 Remove default color in Instagram icon (#11196) 2019-05-31 12:23:00 +02:00
greenkeeper[bot]
d46c9967fb Update axios to the latest version 🚀 (#11198)
* fix(package): update axios to version 0.19.0

* chore(package): update lockfile package-lock.json
2019-05-31 12:19:53 +02:00
Sabe Jones
4132a39b90 4.98.0 2019-05-30 15:14:04 -05:00
Sabe Jones
36cc229dd2 Merge branch 'develop' into release 2019-05-30 15:13:57 -05:00
Sabe Jones
ef0c11c2bd chore(sprites): compile 2019-05-30 15:13:35 -05:00
Sabe Jones
3a8312832c feat(content): new freebie glasses
Fixes #11171
2019-05-30 15:13:23 -05:00
Sabe Jones
df17753bb6 Merge branch 'release' into develop 2019-05-28 15:48:51 -05:00
Matteo Pagliazzi
1a97d69edd Merge pull request #10865 from ianoxley/navbar-a11y-alt-text
Add text alternatives for navbar items
2019-05-26 11:59:04 +02:00
Matteo Pagliazzi
7baa7427a0 Merge pull request #11178 from Alys/mute-ban-block-notification-messages
adjust error messages for muted and banned users, and system flagging error
2019-05-26 11:49:24 +02:00
Matteo Pagliazzi
270078a030 Merge pull request #11170 from HabitRPG/Yutsuten-party-chat-translations
Party Chat Translations (Continuation of #10019)
2019-05-26 11:45:12 +02:00
Alys
bb2768071d change "all caps" to "all capital letters" for clarity
I found this question in the Help guild:
'I'm deleting my account and aren't able to find the "cap" to type in
"delete" in order to finish deleting process. Any ideas were that
"cap" is?'
2019-05-26 16:43:42 +10:00
Alys
ec75de5a90 add swear words - TRIGGER / CONTENT WARNING: assault, slurs, swearwords, etc 2019-05-26 16:25:33 +10:00
Alys
b9b944ba29 replace similar messages about chat privileges removed with one generic one 2019-05-26 07:59:23 +10:00
Alys
fa9553b371 removed mention of Transifex and added link to new Weblate site 2019-05-25 19:57:29 +10:00
Mateus Etto
8d5cbbe9be Docker setup for development (#11165)
* Basic docker setup for development

* Add missing environment variable to client

* Install gulp-cli into docker image for tests
2019-05-24 14:07:21 -05:00
Sabe Jones
3a339a4a09 Merge branch 'release' into develop 2019-05-23 13:41:14 -05:00
Sabe Jones
664cf5a47b Merge branch 'release' into develop 2019-05-21 15:29:40 -05:00
Matteo Pagliazzi
bcf304984d Task notes now disappear when they are deleted from the main task. Fix #11152 (#11167)
* Task notes now disappear when they are deleted from the main task.

* Html component changed back to Markdown. Markdown logic now accounts for if the value is an empty string.

* if-else statement to be sure that the markdown library doesn't create issues with empty strings.
2019-05-20 11:46:44 +02:00
Chris Pomerville
08d84ba691 hides item count in quest sidebar section for non-participants (#11183) 2019-05-20 11:33:47 +02:00
Matteo Pagliazzi
4007c26801 fix(test): fix stripe error message output 2019-05-20 11:31:20 +02:00
Alys
344c20fd99 improve error notification shown when a player is blocked
This improves the wording in the error notification that a player
sees when they've been blocked.

It also also changes markdown links to plain text because raw
markdown was being shown in some locations (for example when you saw
the error messages on the command line while using an API command).

These changes have been discussed with and approved by beffymaroo
and the other mods.
2019-05-18 21:38:38 +10:00
Alys
f5c2c39f6a adjust error messages for muted users and system flagging error 2019-05-18 17:12:24 +10:00
Jose Garay
bda3fb5f4d if-else statement to be sure that the markdown library doesn't create issues with empty strings. 2019-05-15 08:10:44 -07:00
Matteo Pagliazzi
eff8db0afd fix issue with language being undefined, refactoring auth middleware, new tests 2019-05-15 16:54:55 +02:00
Matteo Pagliazzi
8cce38ede1 Merge branch 'party-chat-translations' of https://github.com/Yutsuten/habitica into Yutsuten-party-chat-translations 2019-05-15 15:20:40 +02:00
Mateus Etto
ab0dae8df3 Translate messages only after serialization 2019-05-15 21:26:23 +09:00
Mateus Etto
0824af05b7 Remove unneeded parameter on method call 2019-05-15 19:45:42 +09:00
Mateus Etto
28bf024990 Move translateMessage to libs 2019-05-15 19:07:17 +09:00
Mateus Etto
8c59420d4e Fix quest cancel test 2019-05-15 17:28:05 +09:00
Mateus Etto
8a30ac0607 Add translation support for quest cancel 2019-05-15 16:55:37 +09:00
Mateus Etto
e1984762b5 Fix some tests 2019-05-15 16:55:27 +09:00
Mateus Etto
0360326f41 Change translateSystemMessages to be a class method 2019-05-15 15:46:32 +09:00
Mateus Etto
f3f215abea Fix typo on schema 2019-05-15 13:50:57 +09:00
Jose Garay
feb98a5ac7 Html component changed back to Markdown. Markdown logic now accounts for if the value is an empty string. 2019-05-14 19:06:22 -07:00
Jose Garay
2094a4d4b8 Task notes now disappear when they are deleted from the main task. 2019-05-11 23:47:24 -07:00
Sabe Jones
90532b0763 Merge branch 'develop' into Yutsuten/party-chat-translations 2019-05-08 15:13:53 -05:00
Ian Oxley
6fba71ea2c Add :focus styles to match :hover styles
Add :focus styles to the .habitica-menu-dropdown.
These match the existing :hover styles.
2019-03-28 20:38:17 +00:00
Ian Oxley
5755bfc952 Merge branch 'develop' into navbar-a11y-alt-text 2019-03-28 20:19:04 +00:00
Ian Oxley
08c6e8298c Fix linting errors
Remove parentheses and add trailing comma.
2018-11-24 17:12:12 +00:00
Ian Oxley
5a30b0cf1f Add aria-label to Habitica logo 2018-11-23 22:44:31 +00:00
Ian Oxley
cf847cd1d8 Add aria-label to notifaction and user menus
Add aria-label to give a text equivalent for non-visual users.
2018-11-23 22:44:31 +00:00
Ian Oxley
5753d3e648 Improve a11y for the dropdown menu
Add role="button" to make the component report itself as a button.

Add tabindex so the menu toggle can receive keyboard focus.

Add keydown handlers for `<Enter>` and `<Space>` so the dropdown menu
toggle responds to keyboard input.

Set the aria-pressed attribute to true if the menu is open, or false if
it is closed.
2018-11-23 22:44:30 +00:00
Ian Oxley
4718e5e5ea Improve a11y for the sync links
Add `role="link"` so it shows up as a link in VoiceOver.

Add `tabindex="0" so it can receive keyboard focus, and a keyup handler
for the enter key so it will respond to `<Enter>` keypresses.

Add `aria-label="$t('sync')"` to add text for non-visual users.

Add aria-label to mobile sync icon link.
2018-11-23 22:44:06 +00:00
Ian Oxley
9a2dbace30 Add aria-label to Gems and Gold icons
Add `aria-label` attributes to the gems and gold icons in the menu.

Make the gems icon a link, with a href set to `#buy-gems`, which is the element
that contains the gems dialog.
2018-11-23 22:30:43 +00:00
Mateus Etto
ef07abfd28 Merge branch 'develop' into party-chat-translations
# Conflicts:
#	test/api/unit/models/group.test.js
#	website/server/controllers/api-v3/user/spells.js
#	website/server/models/chat.js
#	website/server/models/group.js
2018-10-08 18:44:02 +09:00
Mateus Etto
b28adc6b42 Merge branch 'develop' into party-chat-translations 2018-08-29 20:07:19 +09:00
Mateus Etto
c814eabb29 Merge branch 'develop' into party-chat-translations 2018-07-21 16:06:38 +09:00
Mateus Etto
4dc19a8c60 Merge branch 'develop' into party-chat-translations 2018-07-12 06:28:44 +09:00
Mateus Etto
89f6a9b07e Merge branch 'develop' into party-chat-translations
# Conflicts:
#	test/api/unit/models/group.test.js
2018-06-20 20:24:38 +09:00
Mateus Etto
a23926f34f Merge branch 'develop' into party-chat-translations 2018-05-19 10:21:54 +09:00
Mateus Etto
118198b594 Fixed tests that contains random data 2018-05-19 00:26:28 +09:00
Mateus Etto
97e80e2093 Implemented requested changes 2018-05-18 23:08:37 +09:00
Mateus Etto
2588befceb Merge branch 'develop' into party-chat-translations
# Conflicts:
#	website/server/models/group.js
2018-05-06 00:46:47 +09:00
Mateus Etto
f09274225a Update chat schema: add 'info' field 2018-04-25 21:28:02 +09:00
Mateus Etto
03b66abe70 Merge branch 'develop' into party-chat-translations
# Conflicts:
#	website/server/controllers/api-v3/quests.js
#	website/server/controllers/api-v3/tasks/groups.js
#	website/server/controllers/api-v3/user/spells.js
#	website/server/models/group.js
2018-04-25 21:15:49 +09:00
Mateus Etto
9d41fb0252 Add tests + some small adjusts 2018-03-25 00:01:20 +09:00
Mateus Etto
012fa2f8ef Improve readability of translateSystemMessages function 2018-03-18 12:09:03 +09:00
Mateus Etto
00d8d9d0cc Merge branch 'develop' into party-chat-translations 2018-03-17 00:08:20 +09:00
Mateus Etto
cc4772c75a Update test 2018-03-12 23:48:09 +09:00
Mateus Etto
fabaaa6d92 Merge branch 'develop' into party-chat-translations 2018-03-12 22:35:13 +09:00
Mateus Etto
854696728a Fix message when boss don't attack 2018-03-12 21:50:23 +09:00
Mateus Etto
314926cc06 Hard coded messages now using i18n strings 2018-03-12 21:45:41 +09:00
Mateus Etto
68fa834946 spellName -> spell; grammar fix 2018-03-12 21:08:20 +09:00
Mateus Etto
09440d5adf Merge branch 'develop' into party-chat-translations 2018-03-02 19:28:41 +09:00
Mateus Etto
1724bfc553 Merge branch 'develop' into party-chat-translations
# Conflicts:
#	website/server/models/group.js
2018-02-28 19:42:19 +09:00
Mateus Etto
8304f99ecb Added comment explaining the new info object 2018-02-26 20:00:47 +09:00
Mateus Etto
d2fcdf4493 Merge branch 'develop' into party-chat-translations
# Conflicts:
#	website/server/controllers/api-v3/user.js
2018-02-26 19:55:32 +09:00
Mateus Etto
649404ac6a Fix travis-ci error 2018-02-19 20:57:31 +09:00
Mateus Etto
774db2564f Moving some strings around 2018-02-19 20:17:03 +09:00
Mateus Etto
521a1e646d Missing message (user claim task) 2018-02-19 20:07:05 +09:00
Mateus Etto
2619ac37d9 Fix mistake on tavern_boss_rage_tired 2018-02-19 00:36:49 +09:00
Mateus Etto
2ce9b319a0 Target username instead of uuid 2018-02-19 00:24:30 +09:00
Mateus Etto
477c23dd67 Save username instead of uuid (no queries necessary anymore) 2018-02-19 00:18:29 +09:00
Mateus Etto
7af71d1457 Fix errors 2018-02-18 19:44:17 +09:00
Mateus Etto
3171550de2 Fix syntax problems found when running tests 2018-02-18 17:14:18 +09:00
Mateus Etto
6477801d3e Translation support for missing sendChat calls 2018-02-18 15:26:27 +09:00
Mateus Etto
14798ced82 Translation support for a lot of messages in party 2018-02-17 22:17:08 +09:00
Mateus Etto
34d37cefcc Boss damage messages translation support 2018-02-17 19:41:15 +09:00
Mateus Etto
bd21933cea Quest started translation support 2018-02-17 17:15:14 +09:00
455 changed files with 29762 additions and 27104 deletions

View File

@@ -1,18 +1,5 @@
FROM node:10
# Install global packages
RUN npm install -g gulp-cli mocha
# Clone Habitica repo and install dependencies
RUN mkdir -p /usr/src/habitrpg
WORKDIR /usr/src/habitrpg
RUN git clone https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
RUN cp config.json.example config.json
RUN npm install
# Create Build dir
RUN mkdir -p ./website/build
# Start Habitica
EXPOSE 3000
CMD ["npm", "start"]
FROM node:10
WORKDIR /code
COPY package*.json /code/
RUN npm install
RUN npm install -g gulp-cli mocha

View File

@@ -61,8 +61,8 @@
"SESSION_SECRET_IV": "12345678912345678912345678912345",
"SESSION_SECRET_KEY": "1234567891234567891234567891234567891234567891234567891234567891",
"SITE_HTTP_AUTH_ENABLED": "false",
"SITE_HTTP_AUTH_PASSWORD": "password",
"SITE_HTTP_AUTH_USERNAME": "admin",
"SITE_HTTP_AUTH_PASSWORDS": "password,wordpass,passkey",
"SITE_HTTP_AUTH_USERNAMES": "admin,tester,contributor",
"SLACK_FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/",
"SLACK_FLAGGING_URL": "https://hooks.slack.com/services/id/id/id",
"SLACK_SUBSCRIPTIONS_URL": "https://hooks.slack.com/services/id/id/id",

View File

@@ -1,14 +1,45 @@
version: "3"
services:
client:
build:
context: .
dockerfile: ./Dockerfile-Dev
command: ["npm", "run", "client:dev"]
depends_on:
- server
environment:
- NODE_ENV=development
- BASE_URL=http://server:3000
image: habitica
networks:
- habitica
ports:
- "8080:8080"
volumes:
- '.:/usr/src/habitrpg'
- .:/code
- /code/node_modules
server:
build:
context: .
dockerfile: ./Dockerfile-Dev
command: ["npm", "start"]
depends_on:
- mongo
environment:
- NODE_ENV=development
- NODE_DB_URI=mongodb://mongo/habitrpg
image: habitica
networks:
- habitica
ports:
- "3000:3000"
volumes:
- '.:/usr/src/habitrpg'
- .:/code
- /code/node_modules
mongo:
image: mongo:3.4
networks:
- habitica
ports:
- "27017:27017"
networks:
habitica:
driver: bridge

View File

@@ -16,7 +16,7 @@ const IMG_DIST_PATH = 'website/client/assets/images/sprites/';
const CSS_DIST_PATH = 'website/client/assets/css/sprites/';
function checkForSpecialTreatment (name) {
let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame/;
let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame|^eyewear_special_\w+HalfMoon/;
return name.match(regex) || name === 'head_0';
}

View File

@@ -0,0 +1,62 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20190530_halfmoon_glasses';
import { v4 as uuid } from 'uuid';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
'items.gear.owned.eyewear_special_blackHalfMoon': true,
'items.gear.owned.eyewear_special_blueHalfMoon': true,
'items.gear.owned.eyewear_special_greenHalfMoon': true,
'items.gear.owned.eyewear_special_pinkHalfMoon': true,
'items.gear.owned.eyewear_special_redHalfMoon': true,
'items.gear.owned.eyewear_special_whiteHalfMoon': true,
'items.gear.owned.eyewear_special_yellowHalfMoon': true,
};
set.migration = MIGRATION_NAME;
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$set: set}).exec();
}
module.exports = async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2019-05-01')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -0,0 +1,59 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20190618_summer_splash_orcas';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
let set;
if (user && user.items && user.items.pets && typeof user.items.pets['Orca-Base'] !== 'undefined') {
set = { migration: MIGRATION_NAME };
} else if (user && user.items && user.items.mounts && typeof user.items.mounts['Orca-Base'] !== 'undefined') {
set = { migration: MIGRATION_NAME, 'items.pets.Orca-Base': 5 };
} else {
set = { migration: MIGRATION_NAME, 'items.mounts.Orca-Base': true };
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
module.exports = async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2019-05-18')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,6 +1,6 @@
/* eslint-disable no-console */
const MIGRATION_NAME = 'mystery_items_201905';
const MYSTERY_ITEMS = ['headAccessory_mystery_201905', 'back_mystery_201905'];
const MIGRATION_NAME = 'mystery_items_201906';
const MYSTERY_ITEMS = ['headAccessory_mystery_201906', 'armor_mystery_201906'];
import { model as User } from '../../website/server/models/user';
import { model as UserNotification } from '../../website/server/models/userNotification';

771
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "4.97.0",
"version": "4.103.0",
"main": "./website/server/index.js",
"dependencies": {
"@google-cloud/trace-agent": "^3.6.0",
@@ -14,7 +14,7 @@
"apn": "^2.2.0",
"autoprefixer": "^9.4.0",
"aws-sdk": "^2.432.0",
"axios": "^0.18.0",
"axios": "^0.19.0",
"axios-progress-bar": "^1.2.0",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
@@ -48,7 +48,7 @@
"got": "^9.0.0",
"gulp": "^4.0.0",
"gulp-babel": "^7.0.1",
"gulp-imagemin": "^5.0.3",
"gulp-imagemin": "^6.0.0",
"gulp-nodemon": "^2.4.1",
"gulp.spritesmith": "^6.9.0",
"habitica-markdown": "^1.3.0",
@@ -58,9 +58,9 @@
"in-app-purchase": "^1.11.3",
"intro.js": "^2.9.3",
"jquery": ">=3.0.0",
"js2xmlparser": "^3.0.0",
"js2xmlparser": "^4.0.0",
"lodash": "^4.17.10",
"merge-stream": "^1.0.0",
"merge-stream": "^2.0.0",
"method-override": "^3.0.0",
"moment": "^2.22.1",
"moment-recur": "^1.0.7",
@@ -71,7 +71,7 @@
"node-sass": "^4.9.0",
"nodemailer": "^6.0.0",
"ora": "^3.2.0",
"pageres": "^4.1.1",
"pageres": "^5.1.0",
"passport": "^0.4.0",
"passport-facebook": "^2.0.0",
"passport-google-oauth20": "1.0.0",
@@ -98,7 +98,7 @@
"url-loader": "^1.0.0",
"useragent": "^2.1.9",
"uuid": "^3.0.1",
"validator": "^10.5.0",
"validator": "^11.0.0",
"vinyl-buffer": "^1.0.1",
"vue": "^2.6.10",
"vue-loader": "^14.2.2",
@@ -152,7 +152,7 @@
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"chalk": "^2.4.1",
"chromedriver": "^2.40.0",
"chromedriver": "^73.0.0",
"connect-history-api-fallback": "^1.1.0",
"coveralls": "^3.0.3",
"cross-spawn": "^6.0.5",

View File

@@ -16,7 +16,7 @@ describe('auth middleware', () => {
describe('auth with headers', () => {
it('allows to specify a list of user field that we do not want to load', (done) => {
const authWithHeaders = authWithHeadersFactory({
userFieldsToExclude: ['items', 'flags', 'auth.timestamps'],
userFieldsToExclude: ['items'],
});
req.headers['x-api-user'] = user._id;
@@ -27,11 +27,34 @@ describe('auth middleware', () => {
const userToJSON = res.locals.user.toJSON();
expect(userToJSON.items).to.not.exist;
expect(userToJSON.flags).to.not.exist;
expect(userToJSON.auth.timestamps).to.not.exist;
expect(userToJSON.auth).to.exist;
done();
});
});
it('makes sure some fields are always included', (done) => {
const authWithHeaders = authWithHeadersFactory({
userFieldsToExclude: [
'items', 'auth.timestamps',
'preferences', 'notifications', '_id', 'flags', 'auth', // these are always loaded
],
});
req.headers['x-api-user'] = user._id;
req.headers['x-api-key'] = user.apiToken;
authWithHeaders(req, res, (err) => {
if (err) return done(err);
const userToJSON = res.locals.user.toJSON();
expect(userToJSON.items).to.not.exist;
expect(userToJSON.auth.timestamps).to.exist;
expect(userToJSON.auth).to.exist;
expect(userToJSON.notifications).to.exist;
expect(userToJSON.preferences).to.exist;
expect(userToJSON._id).to.exist;
expect(userToJSON.flags).to.exist;
done();
});

View File

@@ -1,7 +1,7 @@
import moment from 'moment';
import { v4 as generateUUID } from 'uuid';
import validator from 'validator';
import { sleep } from '../../../helpers/api-unit.helper';
import { sleep, translationCheck } from '../../../helpers/api-unit.helper';
import {
SPAM_MESSAGE_LIMIT,
SPAM_MIN_EXEMPT_CONTRIB_LEVEL,
@@ -271,7 +271,16 @@ describe('Group Model', () => {
party = await Group.findOne({_id: party._id});
expect(Group.prototype.sendChat).to.be.calledOnce;
expect(Group.prototype.sendChat).to.be.calledWith('`Participating Member attacks Wailing Whale for 5.0 damage.` `Wailing Whale attacks party for 7.5 damage.`');
expect(Group.prototype.sendChat).to.be.calledWith({
message: '`Participating Member attacks Wailing Whale for 5.0 damage. Wailing Whale attacks party for 7.5 damage.`',
info: {
bossDamage: '7.5',
quest: 'whale',
type: 'boss_damage',
user: 'Participating Member',
userDamage: '5.0',
},
});
});
it('applies damage only to participating members of party', async () => {
@@ -344,7 +353,10 @@ describe('Group Model', () => {
party = await Group.findOne({_id: party._id});
expect(Group.prototype.sendChat).to.be.calledTwice;
expect(Group.prototype.sendChat).to.be.calledWith('`You defeated Wailing Whale! Questing party members receive the rewards of victory.`');
expect(Group.prototype.sendChat).to.be.calledWith({
message: '`You defeated Wailing Whale! Questing party members receive the rewards of victory.`',
info: { quest: 'whale', type: 'boss_defeated' },
});
});
it('calls finishQuest when boss has <= 0 hp', async () => {
@@ -387,7 +399,10 @@ describe('Group Model', () => {
party = await Group.findOne({_id: party._id});
expect(Group.prototype.sendChat).to.be.calledWith(quest.boss.rage.effect('en'));
expect(Group.prototype.sendChat).to.be.calledWith({
message: quest.boss.rage.effect('en'),
info: { quest: 'trex_undead', type: 'boss_rage' },
});
expect(party.quest.progress.hp).to.eql(383.5);
expect(party.quest.progress.rage).to.eql(0);
});
@@ -437,7 +452,10 @@ describe('Group Model', () => {
party = await Group.findOne({_id: party._id});
expect(Group.prototype.sendChat).to.be.calledWith(quest.boss.rage.effect('en'));
expect(Group.prototype.sendChat).to.be.calledWith({
message: quest.boss.rage.effect('en'),
info: { quest: 'lostMasterclasser4', type: 'boss_rage' },
});
expect(party.quest.progress.rage).to.eql(0);
let drainedUser = await User.findById(participatingMember._id);
@@ -488,7 +506,15 @@ describe('Group Model', () => {
party = await Group.findOne({_id: party._id});
expect(Group.prototype.sendChat).to.be.calledOnce;
expect(Group.prototype.sendChat).to.be.calledWith('`Participating Member found 5 Bars of Soap.`');
expect(Group.prototype.sendChat).to.be.calledWith({
message: '`Participating Member found 5 Bars of Soap.`',
info: {
items: { soapBars: 5 },
quest: 'atom1',
type: 'user_found_items',
user: 'Participating Member',
},
});
});
it('sends a chat message if no progress is made', async () => {
@@ -499,7 +525,15 @@ describe('Group Model', () => {
party = await Group.findOne({_id: party._id});
expect(Group.prototype.sendChat).to.be.calledOnce;
expect(Group.prototype.sendChat).to.be.calledWith('`Participating Member found 0 Bars of Soap.`');
expect(Group.prototype.sendChat).to.be.calledWith({
message: '`Participating Member found 0 Bars of Soap.`',
info: {
items: { soapBars: 0 },
quest: 'atom1',
type: 'user_found_items',
user: 'Participating Member',
},
});
});
it('sends a chat message if no progress is made on quest with multiple items', async () => {
@@ -516,9 +550,15 @@ describe('Group Model', () => {
party = await Group.findOne({_id: party._id});
expect(Group.prototype.sendChat).to.be.calledOnce;
expect(Group.prototype.sendChat).to.be.calledWithMatch(/`Participating Member found/);
expect(Group.prototype.sendChat).to.be.calledWithMatch(/0 Blue Fins/);
expect(Group.prototype.sendChat).to.be.calledWithMatch(/0 Fire Coral/);
expect(Group.prototype.sendChat).to.be.calledWith({
message: '`Participating Member found 0 Fire Coral, 0 Blue Fins.`',
info: {
items: { blueFins: 0, fireCoral: 0 },
quest: 'dilatoryDistress1',
type: 'user_found_items',
user: 'Participating Member',
},
});
});
it('handles collection quests with multiple items', async () => {
@@ -535,8 +575,14 @@ describe('Group Model', () => {
party = await Group.findOne({_id: party._id});
expect(Group.prototype.sendChat).to.be.calledOnce;
expect(Group.prototype.sendChat).to.be.calledWithMatch(/`Participating Member found/);
expect(Group.prototype.sendChat).to.be.calledWithMatch(/\d* (Tracks|Broken Twigs)/);
expect(Group.prototype.sendChat).to.be.calledWithMatch({
message: sinon.match(/`Participating Member found/).and(sinon.match(/\d* (Tracks|Broken Twigs)/)),
info: {
quest: 'evilsanta2',
type: 'user_found_items',
user: 'Participating Member',
},
});
});
it('sends message about victory', async () => {
@@ -547,7 +593,10 @@ describe('Group Model', () => {
party = await Group.findOne({_id: party._id});
expect(Group.prototype.sendChat).to.be.calledTwice;
expect(Group.prototype.sendChat).to.be.calledWith('`All items found! Party has received their rewards.`');
expect(Group.prototype.sendChat).to.be.calledWith({
message: '`All items found! Party has received their rewards.`',
info: { type: 'all_items_found' },
});
});
it('calls finishQuest when all items are found', async () => {
@@ -718,6 +767,258 @@ describe('Group Model', () => {
expect(res.t).to.not.be.called;
});
});
describe('translateSystemMessages', () => {
it('translate quest_start', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'quest_start',
quest: 'basilist',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate boss_damage', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'boss_damage',
user: questLeader.profile.name,
quest: 'basilist',
userDamage: 15.3,
bossDamage: 3.7,
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate boss_dont_attack', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'boss_dont_attack',
user: questLeader.profile.name,
quest: 'basilist',
userDamage: 15.3,
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate boss_rage', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'boss_rage',
quest: 'lostMasterclasser3',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate boss_defeated', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'boss_defeated',
quest: 'lostMasterclasser3',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate user_found_items', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'user_found_items',
user: questLeader.profile.name,
quest: 'lostMasterclasser1',
items: {
ancientTome: 3,
forbiddenTome: 2,
hiddenTome: 1,
},
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate all_items_found', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'all_items_found',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate spell_cast_party', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'spell_cast_party',
user: questLeader.profile.name,
class: 'wizard',
spell: 'earth',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate spell_cast_user', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'spell_cast_user',
user: questLeader.profile.name,
class: 'special',
spell: 'snowball',
target: participatingMember.profile.name,
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate quest_cancel', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'quest_cancel',
user: questLeader.profile.name,
quest: 'basilist',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate quest_abort', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'quest_abort',
user: questLeader.profile.name,
quest: 'basilist',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate tavern_quest_completed', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'tavern_quest_completed',
quest: 'stressbeast',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate tavern_boss_rage_tired', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'tavern_boss_rage_tired',
quest: 'stressbeast',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate tavern_boss_rage', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'tavern_boss_rage',
quest: 'dysheartener',
scene: 'market',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate tavern_boss_desperation', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'tavern_boss_desperation',
quest: 'stressbeast',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
it('translate claim_task', async () => {
questLeader.preferences.language = 'en';
party.chat = [{
info: {
type: 'claim_task',
user: questLeader.profile.name,
task: 'Feed the pet',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
translationCheck(toJSON.chat[0].text);
});
});
describe('toJSONCleanChat', () => {
it('shows messages with 1 flag to non-admins', async () => {
party.chat = [{
flagCount: 1,
info: {
type: 'quest_start',
quest: 'basilist',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
expect(toJSON.chat.length).to.equal(1);
});
it('shows messages with >= 2 flag to admins', async () => {
party.chat = [{
flagCount: 3,
info: {
type: 'quest_start',
quest: 'basilist',
},
}];
const admin = new User({'contributor.admin': true});
let toJSON = await Group.toJSONCleanChat(party, admin);
expect(toJSON.chat.length).to.equal(1);
});
it('doesn\'t show flagged messages to non-admins', async () => {
party.chat = [{
flagCount: 3,
info: {
type: 'quest_start',
quest: 'basilist',
},
}];
let toJSON = await Group.toJSONCleanChat(party, questLeader);
expect(toJSON.chat.length).to.equal(0);
});
});
});
context('Instance Methods', () => {
@@ -1007,20 +1308,22 @@ describe('Group Model', () => {
});
it('formats message', () => {
const chatMessage = party.sendChat('a new message', {
_id: 'user-id',
profile: { name: 'user name' },
contributor: {
toObject () {
return 'contributor object';
const chatMessage = party.sendChat({
message: 'a new message', user: {
_id: 'user-id',
profile: { name: 'user name' },
contributor: {
toObject () {
return 'contributor object';
},
},
},
backer: {
toObject () {
return 'backer object';
backer: {
toObject () {
return 'backer object';
},
},
},
});
}}
);
const chat = chatMessage;
@@ -1037,7 +1340,7 @@ describe('Group Model', () => {
});
it('formats message as system if no user is passed in', () => {
const chat = party.sendChat('a system message');
const chat = party.sendChat({message: 'a system message'});
expect(chat.text).to.eql('a system message');
expect(validator.isUUID(chat.id)).to.eql(true);
@@ -1052,7 +1355,7 @@ describe('Group Model', () => {
});
it('updates users about new messages in party', () => {
party.sendChat('message');
party.sendChat({message: 'message'});
expect(User.update).to.be.calledOnce;
expect(User.update).to.be.calledWithMatch({
@@ -1066,7 +1369,7 @@ describe('Group Model', () => {
type: 'guild',
});
group.sendChat('message');
group.sendChat({message: 'message'});
expect(User.update).to.be.calledOnce;
expect(User.update).to.be.calledWithMatch({
@@ -1076,7 +1379,7 @@ describe('Group Model', () => {
});
it('does not send update to user that sent the message', () => {
party.sendChat('message', {_id: 'user-id', profile: { name: 'user' }});
party.sendChat({message: 'message', user: {_id: 'user-id', profile: { name: 'user' }}});
expect(User.update).to.be.calledOnce;
expect(User.update).to.be.calledWithMatch({
@@ -1088,7 +1391,7 @@ describe('Group Model', () => {
it('skips sending new message notification for guilds with > 5000 members', () => {
party.memberCount = 5001;
party.sendChat('message');
party.sendChat({message: 'message'});
expect(User.update).to.not.be.called;
});
@@ -1096,7 +1399,7 @@ describe('Group Model', () => {
it('skips sending messages to the tavern', () => {
party._id = TAVERN_ID;
party.sendChat('message');
party.sendChat({message: 'message'});
expect(User.update).to.not.be.called;
});
@@ -1431,7 +1734,7 @@ describe('Group Model', () => {
let quest;
beforeEach(() => {
quest = questScrolls.whale;
quest = questScrolls.armadillo;
party.quest.key = quest.key;
party.quest.active = false;
party.quest.leader = questLeader._id;
@@ -1579,6 +1882,36 @@ describe('Group Model', () => {
expect(updatedSleepingParticipatingMember.achievements.lostMasterclasser).to.not.eql(true);
});
it('gives out other pet-related quest achievements', async () => {
quest = questScrolls.rock;
party.quest.key = quest.key;
questLeader.achievements.quests = {
mayhemMistiflying1: 1,
yarn: 1,
mayhemMistiflying2: 1,
egg: 1,
mayhemMistiflying3: 1,
slime: 2,
};
await questLeader.save();
await party.finishQuest(quest);
let [
updatedLeader,
updatedParticipatingMember,
updatedSleepingParticipatingMember,
] = await Promise.all([
User.findById(questLeader._id).exec(),
User.findById(participatingMember._id).exec(),
User.findById(sleepingParticipatingMember._id).exec(),
]);
expect(updatedLeader.achievements.mindOverMatter).to.eql(true);
expect(updatedParticipatingMember.achievements.mindOverMatter).to.not.eql(true);
expect(updatedSleepingParticipatingMember.achievements.mindOverMatter).to.not.eql(true);
});
it('gives xp and gold', async () => {
await party.finishQuest(quest);
@@ -1715,13 +2048,13 @@ describe('Group Model', () => {
questLeader = await User.findById(questLeader._id);
participatingMember = await User.findById(participatingMember._id);
expect(questLeader.party.quest.completed).to.eql('whale');
expect(questLeader.party.quest.completed).to.eql('armadillo');
expect(questLeader.party.quest.progress.up).to.eql(10);
expect(questLeader.party.quest.progress.down).to.eql(8);
expect(questLeader.party.quest.progress.collectedItems).to.eql(5);
expect(questLeader.party.quest.RSVPNeeded).to.eql(false);
expect(participatingMember.party.quest.completed).to.eql('whale');
expect(participatingMember.party.quest.completed).to.eql('armadillo');
expect(participatingMember.party.quest.progress.up).to.eql(10);
expect(participatingMember.party.quest.progress.down).to.eql(8);
expect(participatingMember.party.quest.progress.collectedItems).to.eql(5);
@@ -1928,7 +2261,7 @@ describe('Group Model', () => {
await guild.save();
const groupMessage = guild.sendChat('Test message.');
const groupMessage = guild.sendChat({message: 'Test message.'});
await groupMessage.save();
await sleep();

View File

@@ -171,7 +171,7 @@ describe('GET challenges/user', () => {
});
});
it('should return not return challenges in user groups if we send member true param', async () => {
it('should not return challenges in user groups if we send member true param', async () => {
let challenges = await member.get(`/challenges/user?member=${true}`);
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
@@ -214,6 +214,28 @@ describe('GET challenges/user', () => {
let foundChallenge = _.find(challenges, { _id: privateChallenge._id });
expect(foundChallenge).to.not.exist;
});
it('should not return challenges user doesn\'t have access to, even with query parameters', async () => {
let { group, groupLeader } = await createAndPopulateGroup({
groupDetails: {
name: 'TestPrivateGuild',
summary: 'summary for TestPrivateGuild',
type: 'guild',
privacy: 'private',
},
});
let privateChallenge = await generateChallenge(groupLeader, group, {categories: [{
name: 'academics',
slug: 'academics',
}]});
await groupLeader.post(`/challenges/${privateChallenge._id}/join`);
let challenges = await nonMember.get('/challenges/user?categories=academics&owned=not_owned');
let foundChallenge = _.find(challenges, { _id: privateChallenge._id });
expect(foundChallenge).to.not.exist;
});
});
context('official challenge is present', () => {

View File

@@ -149,7 +149,7 @@ describe('POST /group', () => {
).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('cannotCreatePublicGuildWhenMuted'),
message: t('chatPrivilegesRevoked'),
});
});
});

View File

@@ -100,7 +100,7 @@ describe('Post /groups/:groupId/invite', () => {
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('cannotInviteWhenMuted'),
message: t('chatPrivilegesRevoked'),
});
});
@@ -262,7 +262,7 @@ describe('Post /groups/:groupId/invite', () => {
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('cannotInviteWhenMuted'),
message: t('chatPrivilegesRevoked'),
});
});
@@ -436,7 +436,7 @@ describe('Post /groups/:groupId/invite', () => {
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('cannotInviteWhenMuted'),
message: t('chatPrivilegesRevoked'),
});
});
@@ -526,7 +526,7 @@ describe('Post /groups/:groupId/invite', () => {
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('cannotInviteWhenMuted'),
message: t('chatPrivilegesRevoked'),
});
});

View File

@@ -127,7 +127,13 @@ describe('POST /groups/:groupId/quests/abort', () => {
members: {},
});
expect(Group.prototype.sendChat).to.be.calledOnce;
expect(Group.prototype.sendChat).to.be.calledWithMatch(/aborted the party quest Wail of the Whale.`/);
expect(Group.prototype.sendChat).to.be.calledWithMatch({
message: sinon.match(/aborted the party quest Wail of the Whale.`/),
info: {
quest: 'whale',
type: 'quest_abort',
},
});
stub.restore();
});

View File

@@ -141,7 +141,14 @@ describe('POST /groups/:groupId/quests/cancel', () => {
members: {},
});
expect(Group.prototype.sendChat).to.be.calledOnce;
expect(Group.prototype.sendChat).to.be.calledWithMatch(/cancelled the party quest Wail of the Whale.`/);
expect(Group.prototype.sendChat).to.be.calledWithMatch({
message: sinon.match(/cancelled the party quest Wail of the Whale.`/),
info: {
quest: 'whale',
type: 'quest_cancel',
user: sinon.match.any,
},
});
stub.restore();
});

View File

@@ -83,12 +83,12 @@ context('avatar.vue', () => {
expect(vm.paddingTop).to.equal('28px');
});
it('is 24.5px if user has a pet', () => {
it('is 24px if user has a pet', () => {
vm.member.items = {
currentPet: { name: 'Foo' },
};
expect(vm.paddingTop).to.equal('24.5px');
expect(vm.paddingTop).to.equal('24px');
});
it('is 0px if user has a mount', () => {
@@ -297,4 +297,4 @@ context('avatar.vue', () => {
});
});
});
});
});

View File

@@ -80,6 +80,13 @@ describe('shared.ops.buy', () => {
headAccessory_special_redHeadband: true,
headAccessory_special_whiteHeadband: true,
headAccessory_special_yellowHeadband: true,
eyewear_special_blackHalfMoon: true,
eyewear_special_blueHalfMoon: true,
eyewear_special_greenHalfMoon: true,
eyewear_special_pinkHalfMoon: true,
eyewear_special_redHalfMoon: true,
eyewear_special_whiteHalfMoon: true,
eyewear_special_yellowHalfMoon: true,
});
});

View File

@@ -75,6 +75,13 @@ describe('shared.ops.buyMarketGear', () => {
headAccessory_special_redHeadband: true,
headAccessory_special_whiteHeadband: true,
headAccessory_special_yellowHeadband: true,
eyewear_special_blackHalfMoon: true,
eyewear_special_blueHalfMoon: true,
eyewear_special_greenHalfMoon: true,
eyewear_special_pinkHalfMoon: true,
eyewear_special_redHalfMoon: true,
eyewear_special_whiteHalfMoon: true,
eyewear_special_yellowHalfMoon: true,
});
expect(analytics.track).to.be.calledOnce;
});

View File

@@ -169,6 +169,24 @@ describe('shared.ops.feed', () => {
expect(user.items.pets['Wolf-Base']).to.equal(7);
});
it('awards All Your Base achievement', () => {
user.items.pets['Wolf-Spooky'] = 5;
user.items.food.Milk = 2;
user.items.mounts = {
'Wolf-Base': true,
'TigerCub-Base': true,
'PandaCub-Base': true,
'LionCub-Base': true,
'Fox-Base': true,
'FlyingPig-Base': true,
'Dragon-Base': true,
'Cactus-Base': true,
'BearCub-Base': true,
};
feed(user, {params: {pet: 'Wolf-Spooky', food: 'Milk'}});
expect(user.achievements.allYourBase).to.eql(true);
});
it('evolves the pet into a mount when feeding user.items.pets[pet] >= 50', () => {
user.items.pets['Wolf-Base'] = 49;
user.items.food.Milk = 2;

View File

@@ -159,6 +159,24 @@ describe('shared.ops.hatch', () => {
expect(user.items.eggs).to.eql({Wolf: 0});
expect(user.items.hatchingPotions).to.eql({Base: 0});
});
it('awards Back to Basics achievement', () => {
user.items.pets = {
'Wolf-Base': 5,
'TigerCub-Base': 5,
'PandaCub-Base': 10,
'LionCub-Base': 5,
'Fox-Base': 5,
'FlyingPig-Base': 5,
'Dragon-Base': 5,
'Cactus-Base': 15,
'BearCub-Base': 5,
};
user.items.eggs = {Wolf: 1};
user.items.hatchingPotions = {Spooky: 1};
hatch(user, {params: {egg: 'Wolf', hatchingPotion: 'Spooky'}});
expect(user.achievements.backToBasics).to.eql(true);
});
});
});
});

View File

@@ -49,6 +49,7 @@ describe('shared.ops.rebirth', () => {
let [, message] = rebirth(user);
expect(message).to.equal(i18n.t('rebirthComplete'));
expect(user.flags.lastFreeRebirth).to.exist;
});
it('rebirths a user with not enough gems but more than max level', () => {
@@ -60,6 +61,16 @@ describe('shared.ops.rebirth', () => {
expect(message).to.equal(i18n.t('rebirthComplete'));
});
it('rebirths a user using gems if over max level but rebirthed recently', () => {
user.stats.lvl = MAX_LEVEL + 1;
user.flags.lastFreeRebirth = new Date();
let [, message] = rebirth(user);
expect(message).to.equal(i18n.t('rebirthComplete'));
expect(user.balance).to.equal(0);
});
it('resets user\'s tasks values except for rewards to 0', () => {
tasks[0].value = 1;
tasks[1].value = 1;

View File

@@ -8,6 +8,7 @@ import mongo from './mongo'; // eslint-disable-line
import moment from 'moment';
import i18n from '../../website/common/script/i18n';
import * as Tasks from '../../website/server/models/task';
export { translationCheck } from './translate';
afterEach((done) => {
sandbox.restore();

View File

@@ -16,3 +16,9 @@ export function translate (key, variables, language) {
return translatedString;
}
export function translationCheck (translatedString) {
expect(translatedString).to.not.be.empty;
expect(translatedString).to.not.eql(STRING_ERROR_MSG);
expect(translatedString).to.not.match(STRING_DOES_NOT_EXIST_MSG);
}

View File

@@ -656,5 +656,6 @@ export default {
<style src="assets/css/sprites/spritesmith-main-22.css"></style>
<style src="assets/css/sprites/spritesmith-main-23.css"></style>
<style src="assets/css/sprites/spritesmith-main-24.css"></style>
<style src="assets/css/sprites/spritesmith-main-25.css"></style>
<style src="assets/css/sprites.css"></style>
<style src="smartbanner.js/dist/smartbanner.min.css"></style>

View File

@@ -1,66 +1,84 @@
.promo_armoire_backgrounds_201905 {
.promo_armoire_backgrounds_201906 {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: 0px -786px;
background-position: 0px -869px;
width: 423px;
height: 147px;
}
.promo_bronze_quest {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -499px 0px;
background-position: 0px -359px;
width: 360px;
height: 360px;
}
.promo_feathered_friends_bundle {
.promo_dolphin_quest {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: 0px -638px;
background-position: 0px 0px;
width: 553px;
height: 358px;
}
.promo_glass_watery_potions {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -424px -869px;
width: 423px;
height: 147px;
}
.promo_floral_sunshine_potions {
.promo_halfmoon_glasses {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -424px -638px;
width: 423px;
background-position: -1086px -148px;
width: 279px;
height: 147px;
}
.promo_mystery_201905 {
.promo_mystery_201906 {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -860px -223px;
background-position: -1086px 0px;
width: 282px;
height: 147px;
}
.promo_oddballs_bundle {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -426px -720px;
width: 423px;
height: 147px;
}
.promo_orcas {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -1086px -296px;
width: 219px;
height: 147px;
}
.promo_seafoam {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: 0px -720px;
width: 425px;
height: 148px;
}
.promo_seasonal_shop {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -1086px -444px;
width: 162px;
height: 132px;
}
.promo_summer_splash_2019 {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -554px -244px;
width: 408px;
height: 186px;
}
.promo_take_this {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -1068px -371px;
background-position: -1249px -444px;
width: 96px;
height: 69px;
}
.scene_gold {
.scene_casting_spells {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: 0px 0px;
width: 498px;
height: 360px;
}
.scene_languages {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -328px -361px;
width: 297px;
height: 261px;
}
.scene_rewards {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -860px -371px;
width: 207px;
height: 180px;
}
.scene_spells {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -860px 0px;
background-position: -554px -431px;
width: 312px;
height: 222px;
}
.scene_yesterdailies_repeatables {
.scene_positivity {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: 0px -361px;
width: 327px;
height: 276px;
background-position: -554px 0px;
width: 531px;
height: 243px;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,984 @@
.Pet-TRex-Base {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -410px -300px;
width: 81px;
height: 99px;
}
.Pet-TRex-CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -492px 0px;
width: 81px;
height: 99px;
}
.Pet-TRex-CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -492px -100px;
width: 81px;
height: 99px;
}
.Pet-TRex-Desert {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -492px -200px;
width: 81px;
height: 99px;
}
.Pet-TRex-Golden {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -492px -300px;
width: 81px;
height: 99px;
}
.Pet-TRex-Red {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: 0px -400px;
width: 81px;
height: 99px;
}
.Pet-TRex-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -82px -400px;
width: 81px;
height: 99px;
}
.Pet-TRex-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -164px -400px;
width: 81px;
height: 99px;
}
.Pet-TRex-White {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -246px -400px;
width: 81px;
height: 99px;
}
.Pet-TRex-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -328px -400px;
width: 81px;
height: 99px;
}
.Pet-TigerCub-RoseQuartz {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -82px 0px;
width: 81px;
height: 99px;
}
.Pet-TigerCub-RoyalPurple {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -820px -200px;
width: 81px;
height: 99px;
}
.Pet-TigerCub-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -164px 0px;
width: 81px;
height: 99px;
}
.Pet-TigerCub-Shimmer {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: 0px -100px;
width: 81px;
height: 99px;
}
.Pet-TigerCub-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -82px -100px;
width: 81px;
height: 99px;
}
.Pet-TigerCub-Spooky {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -164px -100px;
width: 81px;
height: 99px;
}
.Pet-TigerCub-StarryNight {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -246px 0px;
width: 81px;
height: 99px;
}
.Pet-TigerCub-Sunshine {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -246px -100px;
width: 81px;
height: 99px;
}
.Pet-TigerCub-Thunderstorm {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: 0px -200px;
width: 81px;
height: 99px;
}
.Pet-TigerCub-Veggie {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -82px -200px;
width: 81px;
height: 99px;
}
.Pet-TigerCub-Watery {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -164px -200px;
width: 81px;
height: 99px;
}
.Pet-TigerCub-White {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -246px -200px;
width: 81px;
height: 99px;
}
.Pet-TigerCub-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -328px 0px;
width: 81px;
height: 99px;
}
.Pet-Treeling-Base {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -328px -100px;
width: 81px;
height: 99px;
}
.Pet-Treeling-CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -328px -200px;
width: 81px;
height: 99px;
}
.Pet-Treeling-CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: 0px -300px;
width: 81px;
height: 99px;
}
.Pet-Treeling-Desert {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -82px -300px;
width: 81px;
height: 99px;
}
.Pet-Treeling-Golden {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -164px -300px;
width: 81px;
height: 99px;
}
.Pet-Treeling-Red {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -246px -300px;
width: 81px;
height: 99px;
}
.Pet-Treeling-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -328px -300px;
width: 81px;
height: 99px;
}
.Pet-Treeling-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -410px 0px;
width: 81px;
height: 99px;
}
.Pet-Treeling-White {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -410px -100px;
width: 81px;
height: 99px;
}
.Pet-Treeling-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -410px -200px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Base {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -410px -400px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -492px -400px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -574px 0px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Desert {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -574px -100px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Golden {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -574px -200px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Red {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -574px -300px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -574px -400px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: 0px -500px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-White {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -82px -500px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -164px -500px;
width: 81px;
height: 99px;
}
.Pet-Turkey-Base {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -246px -500px;
width: 81px;
height: 99px;
}
.Pet-Turkey-Gilded {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -328px -500px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Base {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -410px -500px;
width: 81px;
height: 99px;
}
.Pet-Turtle-CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -492px -500px;
width: 81px;
height: 99px;
}
.Pet-Turtle-CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -574px -500px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Desert {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -656px 0px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Golden {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -656px -100px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Red {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -656px -200px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -656px -300px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -656px -400px;
width: 81px;
height: 99px;
}
.Pet-Turtle-White {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -656px -500px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: 0px -600px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Base {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -82px -600px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -164px -600px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -246px -600px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Desert {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -328px -600px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Golden {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -410px -600px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Red {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -492px -600px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -574px -600px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -656px -600px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-White {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -738px 0px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -738px -100px;
width: 81px;
height: 99px;
}
.Pet-Velociraptor-Base {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -738px -200px;
width: 81px;
height: 99px;
}
.Pet-Velociraptor-CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -738px -300px;
width: 81px;
height: 99px;
}
.Pet-Velociraptor-CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -738px -400px;
width: 81px;
height: 99px;
}
.Pet-Velociraptor-Desert {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -738px -500px;
width: 81px;
height: 99px;
}
.Pet-Velociraptor-Golden {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -738px -600px;
width: 81px;
height: 99px;
}
.Pet-Velociraptor-Red {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: 0px -700px;
width: 81px;
height: 99px;
}
.Pet-Velociraptor-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -82px -700px;
width: 81px;
height: 99px;
}
.Pet-Velociraptor-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -164px -700px;
width: 81px;
height: 99px;
}
.Pet-Velociraptor-White {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -246px -700px;
width: 81px;
height: 99px;
}
.Pet-Velociraptor-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -328px -700px;
width: 81px;
height: 99px;
}
.Pet-Whale-Base {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -410px -700px;
width: 81px;
height: 99px;
}
.Pet-Whale-CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -492px -700px;
width: 81px;
height: 99px;
}
.Pet-Whale-CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -574px -700px;
width: 81px;
height: 99px;
}
.Pet-Whale-Desert {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -656px -700px;
width: 81px;
height: 99px;
}
.Pet-Whale-Golden {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -738px -700px;
width: 81px;
height: 99px;
}
.Pet-Whale-Red {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -820px 0px;
width: 81px;
height: 99px;
}
.Pet-Whale-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -820px -100px;
width: 81px;
height: 99px;
}
.Pet-Whale-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: 0px 0px;
width: 81px;
height: 99px;
}
.Pet-Whale-White {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -820px -300px;
width: 81px;
height: 99px;
}
.Pet-Whale-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -820px -400px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Aquatic {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -820px -500px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Base {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -820px -600px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Bronze {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -820px -700px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Celestial {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: 0px -800px;
width: 81px;
height: 99px;
}
.Pet-Wolf-CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -82px -800px;
width: 81px;
height: 99px;
}
.Pet-Wolf-CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -164px -800px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Cupid {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -246px -800px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Desert {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -328px -800px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Ember {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -410px -800px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Fairy {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -492px -800px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Floral {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -574px -800px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Frost {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -656px -800px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Ghost {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -738px -800px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Glass {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -820px -800px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Glow {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -902px 0px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Golden {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -902px -100px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Holly {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -902px -200px;
width: 81px;
height: 99px;
}
.Pet-Wolf-IcySnow {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -902px -300px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Peppermint {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -902px -400px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Rainbow {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -902px -500px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Red {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -902px -600px;
width: 81px;
height: 99px;
}
.Pet-Wolf-RoseQuartz {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -902px -700px;
width: 81px;
height: 99px;
}
.Pet-Wolf-RoyalPurple {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -902px -800px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -984px 0px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Shimmer {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -984px -100px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -984px -200px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Spooky {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -984px -300px;
width: 81px;
height: 99px;
}
.Pet-Wolf-StarryNight {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -984px -400px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Sunshine {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -984px -500px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Thunderstorm {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -984px -600px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Veggie {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -984px -700px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Veteran {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -984px -800px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Watery {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: 0px -900px;
width: 81px;
height: 99px;
}
.Pet-Wolf-White {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -82px -900px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -164px -900px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Base {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -246px -900px;
width: 81px;
height: 99px;
}
.Pet-Yarn-CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -328px -900px;
width: 81px;
height: 99px;
}
.Pet-Yarn-CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -410px -900px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Desert {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -492px -900px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Golden {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -574px -900px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Red {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -656px -900px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -738px -900px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -820px -900px;
width: 81px;
height: 99px;
}
.Pet-Yarn-White {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -902px -900px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -984px -900px;
width: 81px;
height: 99px;
}
.Pet_HatchingPotion_Aquatic {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px -69px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Base {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -207px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Bronze {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px -138px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Celestial {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px -207px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px -276px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px -345px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Cupid {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px -414px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Desert {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px -483px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Ember {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px -552px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Fairy {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px -621px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Floral {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px -690px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Frost {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px -759px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Ghost {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px -828px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Glass {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px -897px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Glow {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: 0px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Golden {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -69px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Holly {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -138px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_IcySnow {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1066px 0px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Peppermint {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -276px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Purple {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -345px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Rainbow {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -414px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Red {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -483px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_RoseQuartz {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -552px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_RoyalPurple {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -621px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Shade {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -690px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Shimmer {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -759px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -828px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Spooky {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -897px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_StarryNight {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -966px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Sunshine {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1035px -1000px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Thunderstorm {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1135px 0px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Watery {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1135px -69px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_White {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1135px -138px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-25.png');
background-position: -1135px -207px;
width: 68px;
height: 68px;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 561 KiB

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 580 KiB

After

Width:  |  Height:  |  Size: 587 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 KiB

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 115 KiB

View File

@@ -35,6 +35,7 @@ $maroon-50: #C92B2B;
$maroon-100: #DE3F3F;
$maroon-500: #F19595;
$yellow-5: #EE9109;
$yellow-10: #FFA623;
$yellow-50: #FFB445;
$yellow-100: #FFBE5D;
@@ -60,9 +61,6 @@ $green-50: #3FDAA2;
$green-100: #5AEAB2;
$green-500: #A6FFDF;
$orange-10: #ee9109;
$orange-50: #bf7d1a;
$suggested-item-color: #D5C8FF;
$healer-color: #cf8229;

View File

@@ -2,8 +2,8 @@
// possible values are: normal, fall, habitoween, thanksgiving, winter, nye, birthday, valentines, spring, summer
// more to be added on future seasons
$npc_market_flavor: 'normal';
$npc_quests_flavor: 'normal';
$npc_seasonal_flavor: 'normal';
$npc_timetravelers_flavor: 'normal';
$npc_tavern_flavor: 'normal';
$npc_market_flavor: 'summer';
$npc_quests_flavor: 'summer';
$npc_seasonal_flavor: 'summer';
$npc_timetravelers_flavor: 'summer';
$npc_tavern_flavor: 'summer';

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="9" viewBox="0 0 14 9">
<path fill="none" fill-rule="evenodd" stroke="#BDA8FF" stroke-width="2.5" d="M13 1L7 7 1 1"/>
</svg>

After

Width:  |  Height:  |  Size: 187 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd">
<path fill="#FFF" d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0m0 2c3.308 0 6 2.692 6 6s-2.692 6-6 6-6-2.692-6-6 2.692-6 6-6"/>
<path stroke="#FFF" stroke-linecap="round" stroke-width="2" d="M8 5v3.031L10 10"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 358 B

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#E1E0E3" fill-rule="evenodd" d="M8 0c2.173 0 2.445.01 3.298.048.852.04 1.433.174 1.942.372.526.205.973.478 1.418.922.444.445.717.892.922 1.418.198.509.333 1.09.372 1.942C15.99 5.555 16 5.827 16 8s-.01 2.445-.048 3.298c-.04.852-.174 1.433-.372 1.942a3.924 3.924 0 0 1-.922 1.418 3.924 3.924 0 0 1-1.418.922c-.509.198-1.09.333-1.942.372-.853.04-1.125.048-3.298.048s-2.445-.009-3.298-.048c-.852-.04-1.433-.174-1.942-.372a3.924 3.924 0 0 1-1.418-.922A3.924 3.924 0 0 1 .42 13.24c-.198-.509-.333-1.09-.372-1.942C.01 10.445 0 10.173 0 8s.01-2.445.048-3.298C.088 3.85.222 3.269.42 2.76c.205-.526.478-.973.922-1.418A3.924 3.924 0 0 1 2.76.42C3.269.222 3.85.087 4.702.048 5.555.01 5.827 0 8 0zm0 3.892a4.108 4.108 0 1 0 0 8.216 4.108 4.108 0 0 0 0-8.216zm5.23-.162a.96.96 0 1 0-1.92 0 .96.96 0 0 0 1.92 0zM8 10.667a2.666 2.666 0 1 1 0-5.333 2.666 2.666 0 0 1 0 5.333z"/>
<path fill-rule="evenodd" d="M8 0c2.173 0 2.445.01 3.298.048.852.04 1.433.174 1.942.372.526.205.973.478 1.418.922.444.445.717.892.922 1.418.198.509.333 1.09.372 1.942C15.99 5.555 16 5.827 16 8s-.01 2.445-.048 3.298c-.04.852-.174 1.433-.372 1.942a3.924 3.924 0 0 1-.922 1.418 3.924 3.924 0 0 1-1.418.922c-.509.198-1.09.333-1.942.372-.853.04-1.125.048-3.298.048s-2.445-.009-3.298-.048c-.852-.04-1.433-.174-1.942-.372a3.924 3.924 0 0 1-1.418-.922A3.924 3.924 0 0 1 .42 13.24c-.198-.509-.333-1.09-.372-1.942C.01 10.445 0 10.173 0 8s.01-2.445.048-3.298C.088 3.85.222 3.269.42 2.76c.205-.526.478-.973.922-1.418A3.924 3.924 0 0 1 2.76.42C3.269.222 3.85.087 4.702.048 5.555.01 5.827 0 8 0zm0 3.892a4.108 4.108 0 1 0 0 8.216 4.108 4.108 0 0 0 0-8.216zm5.23-.162a.96.96 0 1 0-1.92 0 .96.96 0 0 0 1.92 0zM8 10.667a2.666 2.666 0 1 1 0-5.333 2.666 2.666 0 0 1 0 5.333z"/>
</svg>

Before

Width:  |  Height:  |  Size: 969 B

After

Width:  |  Height:  |  Size: 954 B

View File

@@ -164,30 +164,31 @@ export default {
classGear (heroClass) {
if (heroClass === 'rogue') {
return {
armor: 'armor_rogue_5',
head: 'head_rogue_5',
shield: 'shield_rogue_6',
weapon: 'weapon_rogue_6',
armor: 'armor_special_summer2019Rogue',
head: 'head_special_summer2019Rogue',
shield: 'shield_special_summer2019Rogue',
weapon: 'weapon_special_summer2019Rogue',
};
} else if (heroClass === 'wizard') {
return {
armor: 'armor_wizard_5',
head: 'head_wizard_5',
weapon: 'weapon_wizard_6',
armor: 'armor_special_summer2019Mage',
head: 'head_special_summer2019Mage',
shield: 'shield_special_summer2019Mage',
weapon: 'weapon_special_summer2019Mage',
};
} else if (heroClass === 'healer') {
return {
armor: 'armor_healer_5',
head: 'head_healer_5',
shield: 'shield_healer_5',
weapon: 'weapon_healer_6',
armor: 'armor_special_summer2019Healer',
head: 'head_special_summer2019Healer',
shield: 'shield_special_summer2019Healer',
weapon: 'weapon_special_summer2019Healer',
};
} else {
return {
armor: 'armor_warrior_5',
head: 'head_warrior_5',
shield: 'shield_warrior_5',
weapon: 'weapon_warrior_6',
armor: 'armor_special_summer2019Warrior',
head: 'head_special_summer2019Warrior',
shield: 'shield_special_summer2019Warrior',
weapon: 'weapon_special_summer2019Warrior',
};
}
},

View File

@@ -0,0 +1,42 @@
<template lang="pug">
b-modal#generic-achievement(:title='data.message', size='md', :hide-footer='true')
.modal-body
.col-12
achievement-avatar.avatar
.col-6.offset-3.text-center
p(v-html='data.modalText')
button.btn.btn-primary(@click='close()') {{ $t('huzzah') }}
achievement-footer
</template>
<style scoped>
.avatar {
width: 140px;
margin: 0 auto;
margin-bottom: 1.5em;
margin-top: 1.5em;
}
</style>
<script>
import achievementFooter from './achievementFooter';
import achievementAvatar from './achievementAvatar';
import {mapState} from 'client/libs/store';
export default {
components: {
achievementFooter,
achievementAvatar,
},
props: ['data'],
computed: {
...mapState({user: 'user.data'}),
},
methods: {
close () {
this.$root.$emit('bv::hide::modal', 'generic-achievement');
},
},
};
</script>

View File

@@ -0,0 +1,46 @@
<template lang="pug">
b-modal#just-add-water(:title='title', size='md', :hide-footer='true')
.modal-body
.col-12
achievement-avatar.avatar
.col-6.offset-3.text-center
p {{ $t('achievementJustAddWaterModalText') }}
button.btn.btn-primary(@click='close()') {{ $t('huzzah') }}
achievement-footer
</template>
<style scoped>
.avatar {
width: 140px;
margin: 0 auto;
margin-bottom: 1.5em;
margin-top: 1.5em;
}
</style>
<script>
import achievementFooter from './achievementFooter';
import achievementAvatar from './achievementAvatar';
import {mapState} from 'client/libs/store';
export default {
components: {
achievementFooter,
achievementAvatar,
},
computed: {
...mapState({user: 'user.data'}),
},
data () {
return {
title: `${this.$t('modalAchievement')} ${this.$t('achievementJustAddWater')}`,
};
},
methods: {
close () {
this.$root.$emit('bv::hide::modal', 'just-add-water');
},
},
};
</script>

View File

@@ -0,0 +1,46 @@
<template lang="pug">
b-modal#lost-masterclasser(:title='title', size='md', :hide-footer='true')
.modal-body
.col-12
achievement-avatar.avatar
.col-6.offset-3.text-center
p {{ $t('achievementLostMasterclasserModalText') }}
button.btn.btn-primary(@click='close()') {{ $t('huzzah') }}
achievement-footer
</template>
<style scoped>
.avatar {
width: 140px;
margin: 0 auto;
margin-bottom: 1.5em;
margin-top: 1.5em;
}
</style>
<script>
import achievementFooter from './achievementFooter';
import achievementAvatar from './achievementAvatar';
import {mapState} from 'client/libs/store';
export default {
components: {
achievementFooter,
achievementAvatar,
},
computed: {
...mapState({user: 'user.data'}),
},
data () {
return {
title: `${this.$t('modalAchievement')} ${this.$t('achievementLostMasterclasser')}`,
};
},
methods: {
close () {
this.$root.$emit('bv::hide::modal', 'lost-masterclasser');
},
},
};
</script>

View File

@@ -0,0 +1,46 @@
<template lang="pug">
b-modal#mind-over-matter(:title='title', size='md', :hide-footer='true')
.modal-body
.col-12
achievement-avatar.avatar
.col-6.offset-3.text-center
p {{ $t('achievementMindOverMatterModalText') }}
button.btn.btn-primary(@click='close()') {{ $t('huzzah') }}
achievement-footer
</template>
<style scoped>
.avatar {
width: 140px;
margin: 0 auto;
margin-bottom: 1.5em;
margin-top: 1.5em;
}
</style>
<script>
import achievementFooter from './achievementFooter';
import achievementAvatar from './achievementAvatar';
import {mapState} from 'client/libs/store';
export default {
components: {
achievementFooter,
achievementAvatar,
},
computed: {
...mapState({user: 'user.data'}),
},
data () {
return {
title: `${this.$t('modalAchievement')} ${this.$t('achievementMindOverMatter')}`,
};
},
methods: {
close () {
this.$root.$emit('bv::hide::modal', 'mind-over-matter');
},
},
};
</script>

View File

@@ -139,7 +139,7 @@ export default {
let val = '28px';
if (!this.avatarOnly) {
if (this.member.items.currentPet) val = '24.5px';
if (this.member.items.currentPet) val = '24px';
if (this.member.items.currentMount) val = '0px';
}

View File

@@ -87,7 +87,7 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
button.btn.btn-secondary.purchase-all(@click='unlock(`skin.${set.keys.join(",skin.")}`)') {{ $t('purchaseAll') }}
#hair.section.customize-section(v-if='activeTopPage === "hair"')
.row.col-12.sub-menu.text-center
.col-3.text-center.sub-menu-item(@click='changeSubPage("color")', :class='{active: activeSubPage === "color"}')
.col-3.text-center.sub-menu-item(@click='changeSubPage("color")', :class='{active: activeSubPage === "color", "offset-2": !editing}')
strong(v-once) {{$t('color')}}
.col-3.text-center.sub-menu-item(@click='changeSubPage("bangs")', :class='{active: activeSubPage === "bangs"}')
strong(v-once) {{$t('bangs')}}
@@ -139,6 +139,7 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
span 5
button.btn.btn-secondary.purchase-all(@click='unlock(`hair.base.${baseHair4Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }}
.col-12.customize-options
.head_0.option(v-if="!editing", @click='set({"preferences.hair.base": 0})', :class="[{ active: user.preferences.hair.base === 0 }, 'hair_base_0_' + user.preferences.hair.color]")
.option(v-for='option in baseHair1',
:class='{active: user.preferences.hair.base === option}')
.base.sprite.customize-option(:class="`hair_base_${option}_${user.preferences.hair.color}`", @click='set({"preferences.hair.base": option})')
@@ -377,8 +378,8 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
username-form(@usernameConfirmed='modalPage += 1', :avatarIntro='true')
.small.text-center(v-html="$t('usernameTOSRequirements')")
.section.container.footer
.row(v-if='!editing && !(modalPage === 1)')
.section.container.footer(v-if='!editing && !(modalPage === 1)')
.row
.col-3.offset-1.text-center
div(v-if='modalPage > 1', @click='prev()')
.prev-arrow
@@ -406,6 +407,10 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
#avatar-modal___BV_modal_body_, #avatar-modal___BV_modal_body_ {
padding: 0;
}
#avatar-modal .modal-dialog {
margin-top: 7rem;
}
</style>
<style lang="scss" scoped>
@@ -1125,7 +1130,10 @@ export default {
return options;
},
eyewear () {
let keys = ['blackTopFrame', 'blueTopFrame', 'greenTopFrame', 'pinkTopFrame', 'redTopFrame', 'whiteTopFrame', 'yellowTopFrame'];
let keys = [
'blackTopFrame', 'blueTopFrame', 'greenTopFrame', 'pinkTopFrame', 'redTopFrame', 'whiteTopFrame', 'yellowTopFrame',
'blackHalfMoon', 'blueHalfMoon', 'greenHalfMoon', 'pinkHalfMoon', 'redHalfMoon', 'whiteHalfMoon', 'yellowHalfMoon',
];
let options = keys.map(key => {
let newKey = `eyewear_special_${key}`;
let option = {};

View File

@@ -25,7 +25,7 @@ router-link.card-link(:to="{ name: 'guild', params: { groupId: guild._id } }")
div.guild-bank(v-if='displayGemBank', v-once) {{$t('guildBank')}}
.row
category-tags.col-md-12(:categories="guild.categories", :owner="isOwner", v-once)
span.recommend-text(v-if='showSuggested(guild._id)') Suggested because youre new to Habitica.
span.recommend-text(v-if='showSuggested(guild._id)') {{$t('suggestedGroup')}}
</template>
<style lang="scss" scoped>
@@ -175,8 +175,9 @@ export default {
methods: {
showSuggested (guildId) {
let habiticaHelpingGuildId = '5481ccf3-5d2d-48a9-a871-70a7380cee5a';
let createdAfterRedesign = moment(this.user.auth.timestamps.created).isAfter('2017-08-01');
return guildId === habiticaHelpingGuildId && createdAfterRedesign;
let sixtyDaysAgoFromNow = moment().subtract(60, 'days');
let isUserNew = moment(this.user.auth.timestamps.created).isAfter(sixtyDaysAgoFromNow);
return guildId === habiticaHelpingGuildId && isUserNew;
},
async join () {
// @TODO: This needs to be in the notifications where users will now accept invites

View File

@@ -35,7 +35,7 @@ sidebar-section(:title="$t('questDetailsTitle')")
.grey-progress-bar
.collect-progress-bar(:style="{width: (group.quest.progress.collect[key] / value.count) * 100 + '%'}")
strong {{group.quest.progress.collect[key]}} / {{value.count}}
div.text-right {{parseFloat(user.party.quest.progress.collectedItems) || 0}} items found
div.text-right(v-if='userIsOnQuest') {{parseFloat(user.party.quest.progress.collectedItems) || 0}} items found
.boss-info(v-if='questData.boss')
.row
.col-6

View File

@@ -6,49 +6,61 @@ div
report-flag-modal
send-gems-modal
b-navbar.topbar.navbar-inverse.static-top(toggleable="lg", type="dark", :class="navbarZIndexClass")
b-navbar-brand.brand
b-navbar-brand.brand(aria-label="Habitica")
.logo.svg-icon.d-none.d-xl-block(v-html="icons.logo")
.svg-icon.gryphon.d-xs-block.d-xl-none
b-navbar-toggle(target='menu_collapse').menu-toggle
.quick-menu.mobile-only.form-inline
a.item-with-icon(@click="sync", v-b-tooltip.hover.bottom="$t('sync')")
a.item-with-icon(@click="sync", v-b-tooltip.hover.bottom="$t('sync')", :aria-label="$t('sync')")
.top-menu-icon.svg-icon(v-html="icons.sync")
notification-menu.item-with-icon
user-dropdown.item-with-icon
b-collapse#menu_collapse.collapse.navbar-collapse
b-collapse#menu_collapse(v-model="menuIsOpen").collapse.navbar-collapse
b-navbar-nav.menu-list
b-nav-item.topbar-item(tag="li", :to="{name: 'tasks'}", exact) {{ $t('tasks') }}
li.topbar-item(:class="{'active': $route.path.startsWith('/inventory')}")
b-nav-item.topbar-item(:class="{'active': $route.path === '/'}" tag="li", :to="{name: 'tasks'}", exact) {{ $t('tasks') }}
li.topbar-item(:class="{'active': $route.path.startsWith('/inventory'), 'down': $route.path.startsWith('/inventory') && this.isDesktop()}").droppable
.chevron.rotate(@click='dropdownMobile($event)')
.chevron-icon-down(v-html="icons.chevronDown", v-once)
router-link.nav-link(:to="{name: 'items'}") {{ $t('inventory') }}
.topbar-dropdown
router-link.topbar-dropdown-item.dropdown-item(:to="{name: 'items'}", exact) {{ $t('items') }}
router-link.topbar-dropdown-item.dropdown-item(:to="{name: 'equipment'}") {{ $t('equipment') }}
router-link.topbar-dropdown-item.dropdown-item(:to="{name: 'stable'}") {{ $t('stable') }}
li.topbar-item(:class="{'active': $route.path.startsWith('/shop')}")
li.topbar-item(:class="{'active': $route.path.startsWith('/shop'), 'down': $route.path.startsWith('/shop') && this.isDesktop()}").droppable
.chevron.rotate(@click='dropdownMobile($event)')
.chevron-icon-down(v-html="icons.chevronDown", v-once)
router-link.nav-link(:to="{name: 'market'}") {{ $t('shops') }}
.topbar-dropdown
router-link.topbar-dropdown-item.dropdown-item(:to="{name: 'market'}", exact) {{ $t('market') }}
router-link.topbar-dropdown-item.dropdown-item(:to="{name: 'quests'}") {{ $t('quests') }}
router-link.topbar-dropdown-item.dropdown-item(:to="{name: 'seasonal'}") {{ $t('titleSeasonalShop') }}
router-link.topbar-dropdown-item.dropdown-item(:to="{name: 'time'}") {{ $t('titleTimeTravelers') }}
b-nav-item.topbar-item(tag="li", :to="{name: 'party'}", v-if='this.user.party._id') {{ $t('party') }}
b-nav-item.topbar-item(@click='openPartyModal()', v-if='!this.user.party._id') {{ $t('party') }}
li.topbar-item(:class="{'active': $route.path.startsWith('/guilds')}")
b-nav-item.topbar-item(:class="{'active': $route.path.startsWith('/party')}" tag="li", :to="{name: 'party'}", v-if='this.user.party._id') {{ $t('party') }}
b-nav-item.topbar-item(:class="{'active': $route.path.startsWith('/party')}" @click='openPartyModal()', v-if='!this.user.party._id') {{ $t('party') }}
li.topbar-item(:class="{'active': $route.path.startsWith('/groups'), 'down': $route.path.startsWith('/groups') && this.isDesktop()}").droppable
.chevron.rotate(@click='dropdownMobile($event)')
.chevron-icon-down(v-html="icons.chevronDown", v-once)
router-link.nav-link(:to="{name: 'tavern'}") {{ $t('guilds') }}
.topbar-dropdown
router-link.topbar-dropdown-item.dropdown-item(:to="{name: 'tavern'}") {{ $t('tavern') }}
router-link.topbar-dropdown-item.dropdown-item(:to="{name: 'myGuilds'}") {{ $t('myGuilds') }}
router-link.topbar-dropdown-item.dropdown-item(:to="{name: 'guildsDiscovery'}") {{ $t('guildsDiscovery') }}
li.topbar-item(:class="{'active': $route.path.startsWith('/group-plans')}")
li.topbar-item(:class="{'active': $route.path.startsWith('/group-plans'), 'down': $route.path.startsWith('/group-plans') && this.isDesktop()}").droppable
.chevron.rotate(v-if="groupPlans.length > 0", @click='dropdownMobile($event)')
.chevron-icon-down(v-html="icons.chevronDown", v-once)
router-link.nav-link(:to="{name: 'groupPlan'}") {{ $t('group') }}
.topbar-dropdown
router-link.topbar-dropdown-item.dropdown-item(v-for='group in groupPlans', :key='group._id', :to="{name: 'groupPlanDetailTaskInformation', params: {groupId: group._id}}") {{ group.name }}
li.topbar-item(:class="{'active': $route.path.startsWith('/challenges')}")
li.topbar-item(:class="{'active': $route.path.startsWith('/challenges'), 'down': $route.path.startsWith('/challenges') && this.isDesktop()}").droppable
.chevron.rotate(@click='dropdownMobile($event)')
.chevron-icon-down(v-html="icons.chevronDown", v-once)
router-link.nav-link(:to="{name: 'myChallenges'}") {{ $t('challenges') }}
.topbar-dropdown
router-link.topbar-dropdown-item.dropdown-item(:to="{name: 'myChallenges'}") {{ $t('myChallenges') }}
router-link.topbar-dropdown-item.dropdown-item(:to="{name: 'findChallenges'}") {{ $t('findChallenges') }}
li.topbar-item(:class="{'active': $route.path.startsWith('/help')}")
li.topbar-item(:class="{'active': $route.path.startsWith('/help'), 'down': $route.path.startsWith('/help') && this.isDesktop()}").droppable
.chevron.rotate(@click='dropdownMobile($event)')
.chevron-icon-down(v-html="icons.chevronDown", v-once)
router-link.nav-link(:to="{name: 'faq'}") {{ $t('help') }}
.topbar-dropdown
router-link.topbar-dropdown-item.dropdown-item(:to="{name: 'faq'}") {{ $t('faq') }}
@@ -64,13 +76,13 @@ div
.top-menu-icon.svg-icon(v-html="icons.hourglasses", v-b-tooltip.hover.bottom="$t('mysticHourglassesTooltip')")
span {{ userHourglasses }}
.item-with-icon
.top-menu-icon.svg-icon.gem(v-html="icons.gem", @click='showBuyGemsModal("gems")', v-b-tooltip.hover.bottom="$t('gems')")
a.top-menu-icon.svg-icon.gem(:aria-label="$t('gems')", href="#buy-gems" v-html="icons.gem", @click.prevent='showBuyGemsModal("gems")', v-b-tooltip.hover.bottom="$t('gems')")
span {{userGems}}
.item-with-icon.gold
.top-menu-icon.svg-icon(v-html="icons.gold", v-b-tooltip.hover.bottom="$t('gold')")
.top-menu-icon.svg-icon(:aria-label="$t('gold')", v-html="icons.gold", v-b-tooltip.hover.bottom="$t('gold')")
span {{Math.floor(user.stats.gp * 100) / 100}}
.form-inline.desktop-only
a.item-with-icon(@click="sync", v-b-tooltip.hover.bottom="$t('sync')")
a.item-with-icon(@click="sync", @keyup.enter="sync", role="link", :aria-label="$t('sync')", tabindex="0", v-b-tooltip.hover.bottom="$t('sync')")
.top-menu-icon.svg-icon(v-html="icons.sync")
notification-menu.item-with-icon
user-dropdown.item-with-icon
@@ -81,6 +93,10 @@ div
@import '~client/assets/scss/utils.scss';
@media only screen and (max-width: 1200px) {
.chevron {
display: none
}
.gryphon {
background-image: url('~assets/images/melior@3x.png');
width: 30px;
@@ -96,6 +112,10 @@ div
}
@media only screen and (min-width: 992px) {
.chevron {
display: none
}
.mobile-only {
display: none !important;
}
@@ -111,6 +131,10 @@ div
padding-top: 5px;
height: 56px;
&:hover {
background: $purple-200;
}
&.active:not(:hover) {
box-shadow: 0px -4px 0px $purple-300 inset;
}
@@ -144,12 +168,40 @@ div
order: 1;
text-align: center;
.topbar-dropdown {
transition: max-height 0.25s ease;
}
.topbar-dropdown-item {
background: #432874;
border-bottom: #6133b4 solid 1px;
}
.chevron {
width: 20%;
height: 42px;
position: absolute;
right: 0;
top: 0;
display: block;
}
.chevron-icon-down {
width: 14px;
top: 11px;
right: 12px;
position: absolute;
display: block;
transition: transform 0.25s ease;
}
.down .rotate .chevron-icon-down {
transform: rotate(-180deg);
}
.topbar-item {
position: relative;
&.active {
background: #6133b4;
}
@@ -223,20 +275,25 @@ div
font-weight: bold;
transition: none;
.topbar-dropdown {
display: none; // Display is set to block on hover.
.topbar-dropdown {
overflow: hidden;
max-height: 0;
.topbar-dropdown-item {
line-height: 1.5;
font-size: 16px;
}
}
>a {
padding: .8em 1em !important;
}
&:hover {
&.down {
color: $white !important;
background: $purple-200;
.topbar-dropdown {
display: block; // Open drop-down on hover.
margin-top: 0; // Remove gap between navbar and drop-down.
background: $purple-200;
border-radius: 0px;
@@ -290,6 +347,7 @@ div
margin-right: 24px;
}
&:focus /deep/ .top-menu-icon.svg-icon,
&:hover /deep/ .top-menu-icon.svg-icon {
color: $white;
}
@@ -343,6 +401,7 @@ import gemIcon from 'assets/svg/gem.svg';
import goldIcon from 'assets/svg/gold.svg';
import syncIcon from 'assets/svg/sync.svg';
import svgHourglasses from 'assets/svg/hourglass.svg';
import chevronDownIcon from 'assets/svg/chevron-down.svg';
import logo from 'assets/svg/logo.svg';
import creatorIntro from '../creatorIntro';
@@ -369,12 +428,14 @@ export default {
data () {
return {
isUserDropdownOpen: false,
menuIsOpen: false,
icons: Object.freeze({
gem: gemIcon,
gold: goldIcon,
hourglasses: svgHourglasses,
sync: syncIcon,
logo,
chevronDown: chevronDownIcon,
}),
};
},
@@ -397,6 +458,13 @@ export default {
},
mounted () {
this.getUserGroupPlans();
Array.from(document.getElementById('menu_collapse').getElementsByTagName('a')).forEach(link => {
link.addEventListener('click', this.closeMenu);
});
Array.from(document.getElementsByClassName('topbar-item')).forEach(link => {
link.addEventListener('mouseenter', this.dropdownDesktop);
link.addEventListener('mouseleave', this.dropdownDesktop);
});
},
methods: {
modForm () {
@@ -423,7 +491,40 @@ export default {
this.$root.$emit('bv::show::modal', 'buy-gems', {alreadyTracked: true});
},
dropdownDesktop (hover) {
if (this.isDesktop() && hover.target.classList.contains('droppable')) {
this.dropdown(hover.target);
}
},
dropdownMobile (click) {
this.dropdown(click.currentTarget.parentElement);
},
dropdown (element) {
let droppedElement = document.getElementsByClassName('down')[0];
if (droppedElement && droppedElement !== element) {
droppedElement.classList.remove('down');
droppedElement.lastChild.style.maxHeight = 0;
}
element.classList.toggle('down');
element.lastChild.style.maxHeight = element.classList.contains('down') ? `${element.lastChild.scrollHeight}px` : 0;
},
closeMenu () {
if (this.isMobile()) {
this.menuIsOpen = false;
Array.from(document.getElementsByClassName('droppable')).forEach(droppableElement => {
droppableElement.classList.remove('down');
droppableElement.lastChild.style.maxHeight = 0;
});
}
},
isMobile () {
return document.documentElement.clientWidth < 992;
},
isDesktop () {
return !this.isMobile();
},
},
};
</script>

View File

@@ -0,0 +1,30 @@
<template lang="pug">
base-notification(
:can-remove="canRemove",
:notification="notification",
:read-after-click="true",
@click="action"
)
div(slot="content", v-html="achievementString")
</template>
<script>
import BaseNotification from './base';
export default {
props: ['notification', 'canRemove'],
components: {
BaseNotification,
},
computed: {
achievementString () {
return `<strong>${this.$t('achievement')}</strong>: ${this.$t('achievementJustAddWater')}`;
},
},
methods: {
action () {
this.$root.$emit('bv::show::modal', 'just-add-water');
},
},
};
</script>

View File

@@ -0,0 +1,30 @@
<template lang="pug">
base-notification(
:can-remove="canRemove",
:notification="notification",
:read-after-click="true",
@click="action"
)
div(slot="content", v-html="achievementString")
</template>
<script>
import BaseNotification from './base';
export default {
props: ['notification', 'canRemove'],
components: {
BaseNotification,
},
computed: {
achievementString () {
return `<strong>${this.$t('achievement')}</strong>: ${this.$t('achievementLostMasterclasser')}`;
},
},
methods: {
action () {
this.$root.$emit('bv::show::modal', 'lost-masterclasser');
},
},
};
</script>

View File

@@ -0,0 +1,30 @@
<template lang="pug">
base-notification(
:can-remove="canRemove",
:notification="notification",
:read-after-click="true",
@click="action"
)
div(slot="content", v-html="achievementString")
</template>
<script>
import BaseNotification from './base';
export default {
props: ['notification', 'canRemove'],
components: {
BaseNotification,
},
computed: {
achievementString () {
return `<strong>${this.$t('achievement')}</strong>: ${this.$t('achievementMindOverMatter')}`;
},
},
methods: {
action () {
this.$root.$emit('bv::show::modal', 'mind-over-matter');
},
},
};
</script>

View File

@@ -1,7 +1,7 @@
<template lang="pug">
menu-dropdown.item-notifications(:right="true", @toggled="handleOpenStatusChange", :openStatus="openStatus")
div(slot="dropdown-toggle")
div(v-b-tooltip.hover.bottom="$t('notifications')")
div(:aria-label="$t('notifications')", v-b-tooltip.hover.bottom="$t('notifications')")
message-count(
v-if='notificationsCount > 0',
:count="notificationsCount",
@@ -95,6 +95,9 @@ import NEW_INBOX_MESSAGE from './notifications/newInboxMessage';
import NEW_CHAT_MESSAGE from './notifications/newChatMessage';
import WORLD_BOSS from './notifications/worldBoss';
import VERIFY_USERNAME from './notifications/verifyUsername';
import ACHIEVEMENT_JUST_ADD_WATER from './notifications/justAddWater';
import ACHIEVEMENT_LOST_MASTERCLASSER from './notifications/lostMasterclasser';
import ACHIEVEMENT_MIND_OVER_MATTER from './notifications/mindOverMatter';
export default {
components: {
@@ -106,6 +109,7 @@ export default {
QUEST_INVITATION, GROUP_TASK_APPROVAL, GROUP_TASK_APPROVED, GROUP_TASK_ASSIGNED,
UNALLOCATED_STATS_POINTS, NEW_MYSTERY_ITEMS, CARD_RECEIVED,
NEW_INBOX_MESSAGE, NEW_CHAT_MESSAGE,
ACHIEVEMENT_JUST_ADD_WATER, ACHIEVEMENT_LOST_MASTERCLASSER, ACHIEVEMENT_MIND_OVER_MATTER,
WorldBoss: WORLD_BOSS,
VERIFY_USERNAME,
},
@@ -130,6 +134,7 @@ export default {
'QUEST_INVITATION', 'GROUP_TASK_ASSIGNED', 'GROUP_TASK_APPROVAL', 'GROUP_TASK_APPROVED',
'NEW_MYSTERY_ITEMS', 'CARD_RECEIVED',
'NEW_INBOX_MESSAGE', 'NEW_CHAT_MESSAGE', 'UNALLOCATED_STATS_POINTS',
'ACHIEVEMENT_JUST_ADD_WATER', 'ACHIEVEMENT_LOST_MASTERCLASSER', 'ACHIEVEMENT_MIND_OVER_MATTER',
'VERIFY_USERNAME',
],
};

View File

@@ -1,7 +1,7 @@
<template lang="pug">
menu-dropdown.item-user(:right="true")
div(slot="dropdown-toggle")
div(v-b-tooltip.hover.bottom="$t('user')")
div(:aria-label="$t('user')", v-b-tooltip.hover.bottom="$t('user')")
message-count(v-if='user.inbox.newMessages > 0', :count="user.inbox.newMessages", :top="true")
.top-menu-icon.svg-icon.user(v-html="icons.user")
.user-dropdown(slot="dropdown-content")

View File

@@ -26,6 +26,10 @@ div
quest-completed
quest-invitation
verify-username
generic-achievement(:data='notificationData')
just-add-water
lost-masterclasser
mind-over-matter
</template>
<style lang='scss'>
@@ -118,6 +122,10 @@ import rebirth from './achievements/rebirth';
import streak from './achievements/streak';
import ultimateGear from './achievements/ultimateGear';
import wonChallenge from './achievements/wonChallenge';
import genericAchievement from './achievements/genericAchievement';
import justAddWater from './achievements/justAddWater';
import lostMasterclasser from './achievements/lostMasterclasser';
import mindOverMatter from './achievements/mindOverMatter';
import loginIncentives from './achievements/login-incentives';
import verifyUsername from './settings/verifyUsername';
@@ -147,6 +155,16 @@ const NOTIFICATIONS = {
label: ($t) => $t('modalContribAchievement'),
modalId: 'contributor',
},
ACHIEVEMENT_ALL_YOUR_BASE: {
achievement: true,
label: ($t) => `${$t('achievement')}: ${$t('achievementAllYourBase')}`,
modalId: 'generic-achievement',
},
ACHIEVEMENT_BACK_TO_BASICS: {
achievement: true,
label: ($t) => `${$t('achievement')}: ${$t('achievementBackToBasics')}`,
modalId: 'generic-achievement',
},
};
export default {
@@ -176,6 +194,10 @@ export default {
contributor,
loginIncentives,
verifyUsername,
genericAchievement,
lostMasterclasser,
mindOverMatter,
justAddWater,
},
data () {
// Levels that already display modals and should not trigger generic Level Up
@@ -197,7 +219,8 @@ export default {
'GUILD_PROMPT', 'DROPS_ENABLED', 'REBIRTH_ENABLED', 'WON_CHALLENGE', 'STREAK_ACHIEVEMENT',
'ULTIMATE_GEAR_ACHIEVEMENT', 'REBIRTH_ACHIEVEMENT', 'GUILD_JOINED_ACHIEVEMENT',
'CHALLENGE_JOINED_ACHIEVEMENT', 'INVITED_FRIEND_ACHIEVEMENT', 'NEW_CONTRIBUTOR_LEVEL',
'CRON', 'SCORED_TASK', 'LOGIN_INCENTIVE',
'CRON', 'SCORED_TASK', 'LOGIN_INCENTIVE', 'ACHIEVEMENT_ALL_YOUR_BASE', 'ACHIEVEMENT_BACK_TO_BASICS',
'GENERIC_ACHIEVEMENT',
].forEach(type => {
handledNotifications[type] = true;
});
@@ -342,8 +365,8 @@ export default {
this.playSound('Death');
this.$root.$emit('bv::show::modal', 'death');
},
showNotificationWithModal (type, forceToModal) {
const config = NOTIFICATIONS[type];
showNotificationWithModal (notification, forceToModal) {
const config = NOTIFICATIONS[notification.type];
if (!config) {
return;
@@ -355,6 +378,10 @@ export default {
this.playSound(config.sound);
}
if (notification.data) {
this.notificationData = notification.data;
}
if (forceToModal) {
this.$root.$emit('bv::show::modal', config.modalId);
} else {
@@ -566,7 +593,10 @@ export default {
case 'CHALLENGE_JOINED_ACHIEVEMENT':
case 'INVITED_FRIEND_ACHIEVEMENT':
case 'NEW_CONTRIBUTOR_LEVEL':
this.showNotificationWithModal(notification.type);
case 'ACHIEVEMENT_ALL_YOUR_BASE':
case 'ACHIEVEMENT_BACK_TO_BASICS':
case 'GENERIC_ACHIEVEMENT':
this.showNotificationWithModal(notification);
break;
case 'CRON':
if (notification.data) {

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