b-modal#amazon-payment(title="Amazon", :hide-footer="true", size='md')
h2.text-center Continue with Amazon
- #AmazonPayButton
#AmazonPayWallet(v-if="amazonPayments.loggedIn", style="width: 400px; height: 228px;")
template(v-if="amazonPayments.loggedIn && amazonPayments.type === 'subscription'")
br
@@ -19,7 +18,7 @@
margin-bottom: 12px;
}
- #AmazonPayButton, #AmazonPayWallet, #AmazonPayRecurring {
+ #AmazonPayWallet, #AmazonPayRecurring {
margin: 0 auto;
}
@@ -54,7 +53,6 @@ export default {
subscription: null,
coupon: null,
},
- OffAmazonPayments: {},
isAmazonSetup: false,
amazonButtonEnabled: false,
groupToCreate: null, // creating new group
@@ -74,12 +72,6 @@ export default {
},
},
mounted () {
- if (this.isAmazonReady) return this.setupAmazon();
-
- this.$store.watch(state => state.isAmazonReady, (isAmazonReady) => {
- if (isAmazonReady) return this.setupAmazon();
- });
-
this.$root.$on('habitica::pay-with-amazon', (amazonPaymentsData) => {
if (!amazonPaymentsData) return;
@@ -90,67 +82,30 @@ export default {
this.amazonPayments = Object.assign({}, amazonPayments, amazonPaymentsData);
this.$root.$emit('bv::show::modal', 'amazon-payment');
+
+ this.$nextTick(async () => {
+ if (this.amazonPayments.type === 'subscription') {
+ this.amazonInitWidgets();
+ } else {
+ let url = '/amazon/createOrderReferenceId';
+ let response = await axios.post(url, {
+ billingAgreementId: this.amazonPayments.billingAgreementId,
+ });
+
+ if (response.status <= 400) {
+ this.amazonPayments.orderReferenceId = response.data.data.orderReferenceId;
+ this.amazonInitWidgets();
+ } else {
+ alert(response.message);
+ }
+ }
+ });
});
},
destroyed () {
this.$root.$off('habitica::pay-with-amazon');
},
methods: {
- setupAmazon () {
- if (this.isAmazonSetup) return false;
- this.isAmazonSetup = true;
- this.OffAmazonPayments = window.OffAmazonPayments;
- this.showButton();
- },
- showButton () {
- // @TODO: prevent modal close form clicking outside
- let amazonButton = this.OffAmazonPayments.Button( // eslint-disable-line
- 'AmazonPayButton',
- AMAZON_PAYMENTS.SELLER_ID,
- {
- type: 'PwA',
- color: 'Gold',
- size: 'small',
- agreementType: 'BillingAgreement',
- onSignIn: async (contract) => {
- this.amazonPayments.billingAgreementId = contract.getAmazonBillingAgreementId();
-
- this.$set(this.amazonPayments, 'loggedIn', true);
-
- if (this.amazonPayments.type === 'subscription') {
- this.amazonInitWidgets();
- } else {
- let url = '/amazon/createOrderReferenceId';
- let response = await axios.post(url, {
- billingAgreementId: this.amazonPayments.billingAgreementId,
- });
-
- if (response.status <= 400) {
- this.amazonPayments.orderReferenceId = response.data.data.orderReferenceId;
- this.amazonInitWidgets();
- return;
- }
-
- alert(response.message);
- }
- },
- authorization: () => {
- window.amazon.Login.authorize({
- scope: 'payments:widget',
- popup: true,
- }, function amazonSuccess (response) {
- if (response.error) return alert(response.error);
-
- let url = '/amazon/verifyAccessToken';
- axios.post(url, response)
- .catch((e) => {
- alert(e.message);
- });
- });
- },
- onError: this.amazonOnError,
- });
- },
amazonInitWidgets () {
let walletParams = {
sellerId: AMAZON_PAYMENTS.SELLER_ID, // @TODO: Import
@@ -167,15 +122,14 @@ export default {
walletParams.onReady = (billingAgreement) => {
this.amazonPayments.billingAgreementId = billingAgreement.getAmazonBillingAgreementId();
- new this.OffAmazonPayments.Widgets.Consent({
+ new window.OffAmazonPayments.Widgets.Consent({
sellerId: AMAZON_PAYMENTS.SELLER_ID,
amazonBillingAgreementId: this.amazonPayments.billingAgreementId,
design: {
designMode: 'responsive',
},
onReady: (consent) => {
- let getConsent = consent.getConsentStatus;
- this.$set(this.amazonPayments, 'recurringConsent', getConsent ? Boolean(getConsent()) : false);
+ this.$set(this.amazonPayments, 'recurringConsent', consent.getConsentStatus ? Boolean(consent.getConsentStatus()) : false);
this.$set(this, 'amazonButtonEnabled', true);
},
onConsent: (consent) => {
@@ -189,7 +143,7 @@ export default {
walletParams.amazonOrderReferenceId = this.amazonPayments.orderReferenceId;
}
- new this.OffAmazonPayments.Widgets.Wallet(walletParams).bind('AmazonPayWallet');
+ new window.OffAmazonPayments.Widgets.Wallet(walletParams).bind('AmazonPayWallet');
},
storePaymentStatusAndReload (url) {
let paymentType;
@@ -305,30 +259,6 @@ export default {
amazonOnPaymentSelect () {
this.$set(this.amazonPayments, 'paymentSelected', true);
},
- amazonOnError (error) {
- alert(error.getErrorMessage());
- this.reset();
- },
- reset () {
- // @TODO: Ensure we are using all of these
- // some vars are set in the payments mixin. We should try to edit in one place
- this.amazonPayments.modal = null;
- this.amazonPayments.type = null;
- this.amazonPayments.loggedIn = false;
-
- // Gift
- this.amazonPayments.gift = null;
- this.amazonPayments.giftReceiver = null;
-
- this.amazonPayments.billingAgreementId = null;
- this.amazonPayments.orderReferenceId = null;
- this.amazonPayments.paymentSelected = false;
- this.amazonPayments.recurringConsent = false;
- this.amazonPayments.subscription = null;
- this.amazonPayments.coupon = null;
- this.amazonPayments.groupToCreate = null;
- this.amazonPayments.group = null;
- },
},
};
diff --git a/website/client/components/payments/buyGemsModal.vue b/website/client/components/payments/buyGemsModal.vue
index 1d98396be6..f99244006f 100644
--- a/website/client/components/payments/buyGemsModal.vue
+++ b/website/client/components/payments/buyGemsModal.vue
@@ -44,16 +44,15 @@
button.btn.btn-primary(@click='gemAmount === 20 ? gemAmount = 0 : gemAmount = 20') {{gemAmount === 20 ? $t('selected') : '$5.00'}}
.row.text-center
h2.mx-auto.text-payment {{ $t('choosePaymentMethod') }}
- .card-deck
- .card.text-center.payment-method(@click='showStripe({})')
- .card-body
- .mx-auto(v-html='icons.creditCard', style='"height: 56px; width: 159px; margin-top: 1em;"')
- .card.text-center.payment-method
- a.card-body.paypal(@click="openPaypal(paypalCheckoutLink, 'gems')")
- img(src='~assets/images/paypal.png')
- .card.text-center.payment-method(@click="amazonPaymentsInit({type: 'single'})")
- .card-body.amazon
- img(src='~assets/images/amazon-payments.png')
+ .payments-column
+ button.purchase.btn.btn-primary.payment-button.payment-item(@click='showStripe({})')
+ .svg-icon.credit-card-icon(v-html="icons.creditCardIcon")
+ | {{ $t('card') }}
+ button.btn.payment-item.paypal-checkout.payment-button(@click="openPaypal(paypalCheckoutLink, 'gems')")
+ |
+ img(src='~assets/images/paypal-checkout.png', :alt="$t('paypal')")
+ |
+ amazon-button.payment-item(:amazon-data="{type: 'single'}")
.row.text-center
.svg-icon.mx-auto(v-html='icons.heart', style='"height: 24px; width: 24px;"')
.row.text-center.text-outtro
@@ -127,16 +126,16 @@
h2.mx-auto.text-payment(v-once) {{ $t('choosePaymentMethod') }}
.row.text-center
a.mx-auto(v-once) {{ $t('haveCouponCode') }}
- .card-deck(v-if='subscriptionPlan')
- .card.text-center.payment-method
- .card-body(@click='showStripe({subscription: subscriptionPlan})')
- .mx-auto(v-html='icons.creditCard', style='"height: 56px; width: 159px; margin-top: 1em;"')
- .card.text-center.payment-method
- a.card-body.paypal(@click="openPaypal(paypalSubscriptionLink, 'subscription')")
- img(src='~assets/images/paypal.png')
- .card.text-center.payment-method
- .card-body.amazon(@click="amazonPaymentsInit({type: 'subscription', subscription: subscriptionPlan})")
- img(src='~assets/images/amazon-payments.png')
+ .payments-column(v-if='subscriptionPlan')
+ button.purchase.btn.btn-primary.payment-button.payment-item(@click='showStripe({subscription: subscriptionPlan})')
+ .svg-icon.credit-card-icon(v-html="icons.creditCardIcon")
+ | {{ $t('card') }}
+ button.btn.payment-item.paypal-checkout.payment-button(@click="openPaypal(paypalSubscriptionLink, 'subscription')")
+ |
+ img(src='~assets/images/paypal-checkout.png', :alt="$t('paypal')")
+ |
+ amazon-button.payment-item(:amazon-data="{type: 'subscription', subscription: subscriptionPlan}")
+
.row.text-center
.svg-icon.mx-auto(v-html='icons.heart', style='"height: 24px; width: 24px;"')
.row.text-center.text-outtro
@@ -162,6 +161,11 @@
@@ -339,7 +313,7 @@
import paymentsMixin from 'client/mixins/payments';
import checkIcon from 'assets/svg/check.svg';
- import creditCard from 'assets/svg/credit-card.svg';
+ import creditCardIcon from 'assets/svg/credit-card-icon.svg';
import heart from 'assets/svg/health.svg';
import logo from 'assets/svg/habitica-logo.svg';
@@ -348,10 +322,13 @@
import fortyTwoGems from 'assets/svg/42-gems.svg';
import eightyFourGems from 'assets/svg/84-gems.svg';
+ import amazonButton from 'client/components/payments/amazonButton';
+
export default {
mixins: [paymentsMixin],
components: {
planGemLimits,
+ amazonButton,
},
computed: {
...mapState({user: 'user.data'}),
@@ -373,7 +350,7 @@
icons: Object.freeze({
logo,
check: checkIcon,
- creditCard,
+ creditCardIcon,
fourGems,
heart,
twentyOneGems,
@@ -383,7 +360,6 @@
gemAmount: 0,
subscriptionPlan: '',
selectedPage: 'subscribe',
- amazonPayments: {},
planGemLimits,
};
},
diff --git a/website/client/components/payments/sendGemsModal.vue b/website/client/components/payments/sendGemsModal.vue
index b03f521f57..79ddea17ff 100644
--- a/website/client/components/payments/sendGemsModal.vue
+++ b/website/client/components/payments/sendGemsModal.vue
@@ -1,6 +1,6 @@
-b-modal#send-gems(:title="title", :hide-footer="true", size='lg', @hide='onHide()')
- .modal-body(v-if='userReceivingGems')
+b-modal#send-gems(:title="title", :hide-footer="true", size='md', @hide='onHide()')
+ div(v-if='userReceivingGems')
.panel.panel-default(
:class="gift.type === 'gems' ? 'panel-primary' : 'transparent'",
@click='gift.type = "gems"'
@@ -32,7 +32,7 @@ b-modal#send-gems(:title="title", :hide-footer="true", size='lg', @hide='onHide(
h3.panel-heading {{ $t('subscription') }}
.panel-body
.row
- .col-md-4
+ .col-md-12
.form-group
.radio(v-for='block in subscriptionBlocks', v-if="block.target !== 'group' && block.canSubscribe === true")
label
@@ -48,11 +48,18 @@ b-modal#send-gems(:title="title", :hide-footer="true", size='lg', @hide='onHide(
@click="sendGift()",
:disabled="sendingInProgress"
) {{ $t("send") }}
- template(v-else)
- button.btn.btn-primary(@click='showStripe({gift, uuid: userReceivingGems._id, receiverName})') {{ $t('card') }}
- button.btn.btn-warning(@click='openPaypalGift({gift: gift, giftedTo: userReceivingGems._id, receiverName})') PayPal
- button.btn.btn-success(@click="amazonPaymentsInit({type: 'single', gift, giftedTo: userReceivingGems._id, receiverName})") Amazon Payments
- button.btn.btn-secondary(@click='close()') {{$t('cancel')}}
+ .payments-column.mx-auto(v-else, :class="{'payments-disabled': !gift.subscription.key && gift.gems.amount < 1}")
+ button.purchase.btn.btn-primary.payment-button.payment-item(@click='showStripe({gift, uuid: userReceivingGems._id, receiverName})', :disabled="!gift.subscription.key && gift.gems.amount < 1")
+ .svg-icon.credit-card-icon(v-html="icons.creditCardIcon")
+ | {{ $t('card') }}
+ button.btn.payment-item.paypal-checkout.payment-button(@click="openPaypalGift({gift: gift, giftedTo: userReceivingGems._id, receiverName})", :disabled="!gift.subscription.key && gift.gems.amount < 1")
+ |
+ img(src='~assets/images/paypal-checkout.png', :alt="$t('paypal')")
+ |
+ amazon-button.payment-item.mb-0(
+ :amazon-data="{type: 'single', gift, giftedTo: userReceivingGems._id, receiverName}",
+ :amazon-disabled="!gift.subscription.key && gift.gems.amount < 1",
+ )
+
+
-
diff --git a/website/client/components/shops/market/index.vue b/website/client/components/shops/market/index.vue
index ccf3f432de..6b8349bc27 100644
--- a/website/client/components/shops/market/index.vue
+++ b/website/client/components/shops/market/index.vue
@@ -21,7 +21,7 @@
h1.mb-4.page-header(v-once) {{ $t('market') }}
equipment-section(
- v-if="viewOptions['equipment'].selected",
+ v-if="!anyFilterSelected || viewOptions['equipment'].selected",
:hidePinned="hidePinned",
:hideLocked="hideLocked",
:searchBy="searchTextThrottled"
@@ -39,17 +39,17 @@
span.text {{ $t(ctx.item.id) }}
div(
v-for="category in categories",
- v-if="viewOptions[category.identifier].selected && category.identifier !== 'equipment'"
+ v-if="!anyFilterSelected || viewOptions[category.identifier].selected && category.identifier !== 'equipment'"
)
h4 {{ category.text }}
- category-row(
- :hidePinned="hidePinned",
- :hideLocked="hideLocked",
- :searchBy="searchTextThrottled",
- :sortBy="selectedSortItemsBy.id",
- :category="category"
- )
- keys-to-kennel(v-if='category.identifier === "special"')
+ category-row(
+ :hidePinned="hidePinned",
+ :hideLocked="hideLocked",
+ :searchBy="searchTextThrottled",
+ :sortBy="selectedSortItemsBy.id",
+ :category="category"
+ )
+ keys-to-kennel(v-if='category.identifier === "special"')
div.fill-height
inventoryDrawer(:showEggs="true", :showPotions="true")
@@ -290,13 +290,16 @@ export default {
categories.map((category) => {
if (!this.viewOptions[category.identifier]) {
this.$set(this.viewOptions, category.identifier, {
- selected: true,
+ selected: false,
});
}
});
return categories;
},
+ anyFilterSelected () {
+ return Object.values(this.viewOptions).some(g => g.selected);
+ },
},
methods: {
sellItem (itemScope) {
diff --git a/website/client/components/shops/market/sellModal.vue b/website/client/components/shops/market/sellModal.vue
index 2f4aa75f5e..31d1a80b05 100644
--- a/website/client/components/shops/market/sellModal.vue
+++ b/website/client/components/shops/market/sellModal.vue
@@ -26,7 +26,7 @@
br
div(v-else)
- div(v-once)
+ div
div.text {{ item.notes() }}
div
diff --git a/website/client/components/shops/quests/index.vue b/website/client/components/shops/quests/index.vue
index 7194137872..1fad6f5d12 100644
--- a/website/client/components/shops/quests/index.vue
+++ b/website/client/components/shops/quests/index.vue
@@ -52,11 +52,6 @@
:popoverPosition="'top'",
@click="selectItem(item)"
)
- template(slot="popoverContent", slot-scope="ctx")
- div.questPopover
- h4.popover-content-title {{ item.text }}
- questInfo(:quest="item")
-
template(slot="itemBadge", slot-scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
:class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
@@ -81,7 +76,7 @@
div(
v-for="category in categories",
- v-if="viewOptions[category.identifier].selected"
+ v-if="!anyFilterSelected || viewOptions[category.identifier].selected"
)
h2.mb-3 {{ category.text }}
@@ -244,20 +239,22 @@
display: inline-block;
width: 33%;
margin-bottom: 24px;
+ vertical-align: top;
.items {
border-radius: 2px;
background-color: #edecee;
display: inline-block;
- padding: 8px;
+ padding: 0;
+ margin-right: 12px;
}
.item-wrapper {
margin-bottom: 0;
}
- .items > div:not(:last-of-type) {
- margin-right: 16px;
+ .items > div {
+ margin: 8px;
}
}
@@ -415,7 +412,7 @@ export default {
if (this.shop.categories) {
this.shop.categories.map((category) => {
this.$set(this.viewOptions, category.identifier, {
- selected: true,
+ selected: false,
});
});
@@ -424,6 +421,10 @@ export default {
return [];
}
},
+
+ anyFilterSelected () {
+ return Object.values(this.viewOptions).some(g => g.selected);
+ },
},
methods: {
questItems (category, sortBy, searchBy, hideLocked, hidePinned) {
diff --git a/website/client/components/shops/quests/questInfo.vue b/website/client/components/shops/quests/questInfo.vue
index a395eca04e..ac8f78a98d 100644
--- a/website/client/components/shops/quests/questInfo.vue
+++ b/website/client/components/shops/quests/questInfo.vue
@@ -1,41 +1,60 @@
.row(:class="{'small-version': smallVersion}")
- dl
- template(v-if="quest.collect")
- dt(:class="smallVersion ? 'col-3' : 'col-4'") {{ $t('collect') + ':' }}
- dd.col-8
+ .table-row(v-if="quest.collect")
+ dt {{ $t('collect') + ':' }}
+ dd
div(v-for="(collect, key) of quest.collect")
span {{ collect.count }} {{ getCollectText(collect) }}
- template(v-if="quest.boss")
- dt(:class="smallVersion ? 'col-3' : 'col-4'") {{ $t('bossHP') + ':' }}
- dd.col-8 {{ quest.boss.hp }}
+ .table-row(v-if="quest.boss")
+ dt {{ $t('bossHP') + ':' }}
+ dd {{ quest.boss.hp }}
- dt(:class="smallVersion ? 'col-3' : 'col-4'") {{ $t('difficulty') + ':' }}
- dd.col-8
- .svg-icon.inline(
- v-for="star of stars()", v-html="icons[star]",
- :class="smallVersion ? 'icon-12' : 'icon-16'",
- )
+ .table-row
+ dt {{ $t('difficulty') + ':' }}
+ dd
+ .svg-icon.inline(
+ v-for="star of stars()", v-html="icons[star]",
+ :class="smallVersion ? 'icon-12' : 'icon-16'",
+ )
diff --git a/website/client/components/tasks/task.vue b/website/client/components/tasks/task.vue
index 16a03ed681..18c35ceab5 100644
--- a/website/client/components/tasks/task.vue
+++ b/website/client/components/tasks/task.vue
@@ -20,7 +20,7 @@
v-if="isUser && !isRunningYesterdailies",
:right="task.type === 'reward'",
ref="taskDropdown",
- v-b-tooltip.hover.top="$t('showMore')"
+ v-b-tooltip.hover.top="$t('options')"
)
div(slot="dropdown-toggle", draggable=false)
.svg-icon.dropdown-icon(v-html="icons.menu")
@@ -46,7 +46,7 @@
v-markdown="task.notes",
:class="{'has-checklist': task.notes && hasChecklist}",
)
- .checklist(v-if="canViewchecklist")
+ .checklist(v-if="canViewchecklist", :class="{isOpen: !task.collapseChecklist}")
.d-inline-flex
.collapse-checklist.d-flex.align-items-center.expand-toggle(
v-if="isUser",
@@ -141,6 +141,11 @@
min-width: 0px;
overflow-wrap: break-word;
+ // markdown p-tag, can't find without /deep/
+ /deep/ p {
+ margin-bottom: 0;
+ }
+
&.has-notes {
padding-bottom: 4px;
}
@@ -229,7 +234,7 @@
overflow-wrap: break-word;
&.has-checklist {
- padding-bottom: 8px;
+ padding-bottom: 2px;
}
}
@@ -254,19 +259,25 @@
}
.checklist {
- margin-bottom: 2px;
+ &.isOpen {
+ margin-bottom: 2px;
+ }
+
margin-top: -3px;
}
.collapse-checklist {
padding: 2px 6px;
- margin-bottom: 9px;
border-radius: 1px;
background-color: $gray-600;
font-size: 10px;
line-height: 1.2;
text-align: center;
color: $gray-200;
+ margin-bottom: 9px;
+
+ &.open {
+ }
span {
margin: 0px 4px;
@@ -285,7 +296,7 @@
margin-bottom: -3px;
min-height: 0px;
width: 100%;
- margin-left: 8px;
+ margin-left: 0;
padding-right: 20px;
overflow-wrap: break-word;
@@ -427,7 +438,7 @@
border-left: none;
}
- .task-control, .reward-control {
+ .task-control:not(.task-disabled-habit-control-inner), .reward-control {
cursor: pointer;
}
diff --git a/website/client/components/tasks/taskModal.vue b/website/client/components/tasks/taskModal.vue
index 793ffeafe3..0cd33c9dcc 100644
--- a/website/client/components/tasks/taskModal.vue
+++ b/website/client/components/tasks/taskModal.vue
@@ -88,7 +88,8 @@
:clearButtonText='$t("clear")',
:todayButton='!challengeAccessRequired',
:todayButtonText='$t("today")',
- :disabled-picker='challengeAccessRequired'
+ :disabled-picker='challengeAccessRequired',
+ :highlighted='calendarHighlights'
)
.option(v-if="task.type === 'daily'")
.form-group
@@ -99,7 +100,8 @@
:clearButton="false",
:todayButton="!challengeAccessRequired",
:todayButtonText="$t('today')",
- :disabled-picker="challengeAccessRequired"
+ :disabled-picker="challengeAccessRequired",
+ :highlighted='calendarHighlights'
)
.option(v-if="task.type === 'daily'")
.form-group
@@ -259,6 +261,10 @@
font-weight: bold;
}
+ .input-group > * {
+ height: 40px;
+ }
+
input, textarea {
border: none;
background: rgba(0, 0, 0, 0.24);
@@ -717,6 +723,7 @@ export default {
con: 'constitution',
per: 'perception',
},
+ calendarHighlights: { dates: [new Date()]},
};
},
mounted () {
diff --git a/website/client/components/tasks/user.vue b/website/client/components/tasks/user.vue
index e9652ccabc..4986ecec29 100644
--- a/website/client/components/tasks/user.vue
+++ b/website/client/components/tasks/user.vue
@@ -12,7 +12,7 @@
.col-12.col-md-4.offset-md-4
.d-flex
input.form-control.input-search(type="text", :placeholder="$t('search')", v-model="searchText")
- button.btn.btn-secondary.dropdown-toggle.ml-2.d-flex.align-items-center(
+ button.btn.btn-secondary.dropdown-toggle.ml-2.d-flex.align-items-center.search-button(
type="button",
@click="toggleFilterPanel()",
:class="{active: selectedTags.length > 0}",
@@ -110,6 +110,10 @@
padding-top: 16px;
}
+ .input-search, .search-button {
+ height: 40px;
+ }
+
.tasks-navigation {
margin-bottom: 20px;
}
diff --git a/website/client/components/ui/countBadge.vue b/website/client/components/ui/countBadge.vue
index 14bef71a4c..a33f2e98e7 100644
--- a/website/client/components/ui/countBadge.vue
+++ b/website/client/components/ui/countBadge.vue
@@ -15,6 +15,7 @@ span.badge.badge-pill.badge-item.badge-count(
min-width: 24px;
height: 24px;
box-shadow: 0 1px 1px 0 rgba($black, 0.12);
+ z-index: 1;
}
diff --git a/website/client/components/ui/statsbar.vue b/website/client/components/ui/statsbar.vue
new file mode 100644
index 0000000000..dc7ddb37c0
--- /dev/null
+++ b/website/client/components/ui/statsbar.vue
@@ -0,0 +1,126 @@
+
+ .progress-container(ref="container", :id="elementId", :class="{condensed}")
+ .svg-icon(v-html="icon")
+ .progress
+ .progress-bar(:class="progressClass", :style="{width: `${percent(value, maxValue)}%`}")
+ span.small-text {{value | statFloor}} / {{maxValue}}
+ b-tooltip.myClass(:target="() => $refs.container", :container="elementId", :title="tooltip", triggers="hover", placement="bottom")
+
+
+
+
+
diff --git a/website/client/components/userMenu/profile.vue b/website/client/components/userMenu/profile.vue
index cf544bfbda..e66208d0d8 100644
--- a/website/client/components/userMenu/profile.vue
+++ b/website/client/components/userMenu/profile.vue
@@ -324,6 +324,7 @@
}
.progress-container > .progress {
+ border-radius: 1px;
background-color: $gray-500;
}
}
@@ -371,8 +372,10 @@
.progress {
height: 8px;
+ border-radius: 1px;
.progress-bar {
+ border-radius: 1px;
background-color: $green-10 !important;
}
}
@@ -495,6 +498,9 @@ export default {
async userId () {
this.loadUser();
},
+ userLoggedIn () {
+ this.loadUser();
+ },
},
methods: {
async loadUser () {
diff --git a/website/client/components/userMenu/profileStats.vue b/website/client/components/userMenu/profileStats.vue
index 1d06c5ebce..689d0ce492 100644
--- a/website/client/components/userMenu/profileStats.vue
+++ b/website/client/components/userMenu/profileStats.vue
@@ -123,8 +123,8 @@
h3(v-if='userLevel100Plus', v-once, v-html="$t('noMoreAllocate')")
h3
| {{$t('statPoints')}}
- .counter.badge(v-if='user.stats.points || userLevel100Plus')
- | {{pointsRemaining}}
+ .counter.badge.badge-pill(v-if='user.stats.points || userLevel100Plus')
+ | {{pointsRemaining}}
.col-12.col-md-6
.float-right
toggle-switch(
@@ -409,9 +409,6 @@
color: #fff;
background-color: #ff944c;
box-shadow: 0 1px 1px 0 rgba(26, 24, 29, 0.12);
- width: 24px;
- height: 24px;
- border-radius: 50%;
}
.box {
diff --git a/website/client/directives/markdown.js b/website/client/directives/markdown.js
index 1b50190271..98c174f431 100644
--- a/website/client/directives/markdown.js
+++ b/website/client/directives/markdown.js
@@ -3,6 +3,8 @@ import habiticaMarkdown from 'habitica-markdown';
export default function markdown (el, {value, oldValue}) {
if (value === oldValue) return;
- el.innerHTML = habiticaMarkdown.render(value);
+ if (value) {
+ el.innerHTML = habiticaMarkdown.render(String(value));
+ }
el.classList.add('markdown');
}
\ No newline at end of file
diff --git a/website/client/mixins/notifications.js b/website/client/mixins/notifications.js
index 6e15c2c39f..26a121dfdc 100644
--- a/website/client/mixins/notifications.js
+++ b/website/client/mixins/notifications.js
@@ -46,7 +46,7 @@ export default {
},
markdown (val) {
if (!val) return;
- let parsedMarkdown = habiticaMarkdown.render(val);
+ let parsedMarkdown = habiticaMarkdown.render(String(val));
this.notify(parsedMarkdown, 'info');
},
mp (val) {
diff --git a/website/client/mixins/payments.js b/website/client/mixins/payments.js
index 84c653a45f..9b66a07b89 100644
--- a/website/client/mixins/payments.js
+++ b/website/client/mixins/payments.js
@@ -223,7 +223,6 @@ export default {
return true;
},
amazonPaymentsInit (data) {
- if (!this.checkGemAmount(data)) return;
if (data.type !== 'single' && data.type !== 'subscription') return;
if (data.gift) {
@@ -251,8 +250,30 @@ export default {
this.amazonPayments.gift = data.gift;
this.amazonPayments.type = data.type;
+ },
+ amazonOnError (error) {
+ alert(error.getErrorMessage());
+ this.reset();
+ },
+ reset () {
+ // @TODO: Ensure we are using all of these
+ // some vars are set in the payments mixin. We should try to edit in one place
+ this.amazonPayments.modal = null;
+ this.amazonPayments.type = null;
+ this.amazonPayments.loggedIn = false;
- this.$root.$emit('habitica::pay-with-amazon', this.amazonPayments);
+ // Gift
+ this.amazonPayments.gift = null;
+ this.amazonPayments.giftReceiver = null;
+
+ this.amazonPayments.billingAgreementId = null;
+ this.amazonPayments.orderReferenceId = null;
+ this.amazonPayments.paymentSelected = false;
+ this.amazonPayments.recurringConsent = false;
+ this.amazonPayments.subscription = null;
+ this.amazonPayments.coupon = null;
+ this.amazonPayments.groupToCreate = null;
+ this.amazonPayments.group = null;
},
async cancelSubscription (config) {
if (config && config.group && !confirm(this.$t('confirmCancelGroupPlan'))) return;
diff --git a/website/client/store/actions/chat.js b/website/client/store/actions/chat.js
index 0bb568a863..584db3bf75 100644
--- a/website/client/store/actions/chat.js
+++ b/website/client/store/actions/chat.js
@@ -48,10 +48,18 @@ export async function like (store, payload) {
}
export async function flag (store, payload) {
- const url = `/api/v4/groups/${payload.groupId}/chat/${payload.chatId}/flag`;
+ let url = '';
+
+ if (payload.groupId === 'privateMessage') {
+ url = `/api/v4/members/flag-private-message/${payload.chatId}`;
+ } else {
+ url = `/api/v4/groups/${payload.groupId}/chat/${payload.chatId}/flag`;
+ }
+
const response = await axios.post(url, {
comment: payload.comment,
});
+
return response.data.data;
}
diff --git a/website/client/store/actions/user.js b/website/client/store/actions/user.js
index d15bd3e805..fe6094fcc9 100644
--- a/website/client/store/actions/user.js
+++ b/website/client/store/actions/user.js
@@ -5,6 +5,7 @@ import axios from 'axios';
import { togglePinnedItem as togglePinnedItemOp } from 'common/script/ops/pinnedGearUtils';
import changeClassOp from 'common/script/ops/changeClass';
import disableClassesOp from 'common/script/ops/disableClasses';
+import openMysteryItemOp from 'common/script/ops/openMysteryItem';
export function fetch (store, options = {}) { // eslint-disable-line no-shadow
return loadAsyncResource({
@@ -127,7 +128,9 @@ export function castSpell (store, params) {
return axios.post(spellUrl, data);
}
-export function openMysteryItem () {
+export async function openMysteryItem (store) {
+ let user = store.state.user.data;
+ openMysteryItemOp(user);
return axios.post('/api/v4/user/open-mystery-item');
}
diff --git a/website/common/errors/apiErrorMessages.js b/website/common/errors/apiErrorMessages.js
index a1550fddad..f10747b12c 100644
--- a/website/common/errors/apiErrorMessages.js
+++ b/website/common/errors/apiErrorMessages.js
@@ -8,11 +8,14 @@ module.exports = {
missingTypeKeyEquip: '"key" and "type" are required parameters.',
+ chatIdRequired: 'req.params.chatId must contain a chatId.',
+ messageIdRequired: 'req.params.messageId must contain a message ID.',
+
guildsOnlyPaginate: 'Only public guilds support pagination.',
guildsPaginateBooleanString: 'req.query.paginate must be a boolean string.',
groupIdRequired: 'req.params.groupId must contain a groupId.',
groupRemainOrLeaveChallenges: 'req.query.keep must be either "remain-in-challenges" or "leave-challenges"',
- managerIdRequired: 'req.body.managerId must contain a user ID.',
+ managerIdRequired: 'req.body.managerId must contain a User ID.',
noSudoAccess: 'You don\'t have sudo access.',
eventRequired: '"req.params.event" is required.',
@@ -22,6 +25,4 @@ module.exports = {
missingCustomerId: 'Missing "req.query.customerId"',
missingPaypalBlock: 'Missing "req.session.paypalBlock"',
missingSubKey: 'Missing "req.query.sub"',
-
- messageIdRequired: '\"messageId\" must be a valid UUID.",',
};
diff --git a/website/common/locales/de/content.json b/website/common/locales/de/content.json
index bf8fc28f71..7a2ec6fab2 100644
--- a/website/common/locales/de/content.json
+++ b/website/common/locales/de/content.json
@@ -2,9 +2,9 @@
"potionText": "Heiltrank",
"potionNotes": "Heilt 15 Lebenspunkte (wird sofort angewendet)",
"armoireText": "Verzauberter Schrank",
- "armoireNotesFull": "Öffne den Schrank, um zufällig spezielle Ausrüstung, Erfahrung oder Futter zu erhalten! Verbleibende Ausrüstungsgegenstände:",
+ "armoireNotesFull": "Öffne den Schrank, um zufällig spezielle Ausrüstung, Erfahrung oder Futter zu erhalten! Verbleibende Ausrüstungsgegenstände: ",
"armoireLastItem": "Du hast das letzte Stück seltener Ausrüstung im verzauberten Schrank gefunden.",
- "armoireNotesEmpty": "Im verzauberten Schrank gibt es jeweils in der ersten Woche eines Monats neue Ausrüstung. Bis dahin, klicke weiter für Erfahrung und Futter.",
+ "armoireNotesEmpty": "Im verzauberten Schrank gibt es jeweils in der ersten Woche eines Monats neue Ausrüstung. Bis dahin, klicke weiter für Erfahrung und Futter!",
"dropEggWolfText": "Wolfsjunges",
"dropEggWolfMountText": "Wolfs-Reittier",
"dropEggWolfAdjective": "ein treues",
@@ -291,7 +291,7 @@
"foodCandyWhite": "Vanillebonbon",
"foodCandyWhiteThe": "das Vanillebonbon",
"foodCandyWhiteA": "ein Vanillebonbon",
- "foodCandyGolden": "Honigbonbon",
+ "foodCandyGolden": "Honigbonbon ",
"foodCandyGoldenThe": "das Honigbonbon",
"foodCandyGoldenA": "ein Honigbonbon",
"foodCandyZombie": "Verrottetes Bonbon",
@@ -306,5 +306,6 @@
"foodSaddleText": "Magischer Sattel",
"foodSaddleNotes": "Lässt eines Deiner Haustiere augenblicklich zum Reittier heranwachsen.",
"foodSaddleSellWarningNote": "Hey! Das ist ein sehr nützlicher Gegenstand! Bist Du vertraut damit, wie Du den Sattel mit Deinen Haustieren nutzt?",
- "foodNotes": "Verfüttere das an ein Haustier und es wächst bald zu einem kräftigen Reittier heran."
-}
\ No newline at end of file
+ "foodNotes": "Verfüttere das an ein Haustier und es wächst bald zu einem kräftigen Reittier heran.",
+ "hatchingPotionRoseQuartz": "Rosenquarz"
+}
diff --git a/website/common/locales/de/settings.json b/website/common/locales/de/settings.json
index 443dda2540..e246ec24e1 100644
--- a/website/common/locales/de/settings.json
+++ b/website/common/locales/de/settings.json
@@ -84,7 +84,7 @@
"resetDo": "Ja, setzt mein Konto jetzt zurück!",
"resetComplete": "Zurückgesetzt!",
"fixValues": "Werte reparieren",
- "fixValuesText1": "Wenn Du Opfer eines Bugs geworden bist oder einen Fehler gemacht hast, der Deinen Charakter unfair beeinflusst hat (Schaden, den Du nicht hättest erleiden dürfen, Gold das Du nicht verdient hast, usw.), dann kannst Du das hier manuell korrigieren. Ja, das eröffnet die Möglichkeit zu cheaten: Verwende dieses Feature mit Bedacht, oder Du verdirbst Dir das Ausbilden Deiner Gewohnheiten! ",
+ "fixValuesText1": "Wenn Du Opfer eines Bugs geworden bist oder einen Fehler gemacht hast, der Deinen Charakter unfair beeinflusst hat (Schaden, den Du nicht hättest erleiden dürfen, Gold das Du nicht verdient hast, usw.), dann kannst Du das hier manuell korrigieren. Ja, das eröffnet die Möglichkeit zu cheaten: Verwende dieses Feature mit Bedacht, oder Du verdirbst Dir das Ausbilden Deiner Gewohnheiten!",
"fixValuesText2": "Beachte, dass Du hier keine Strähnen einzelner Aufgaben wiederherstellen kannst. Um das zu tun, bearbeite eine Tagesaufgabe unter erweiterte Optionen. Dort wirst Du ein \"Strähne wiederherstellen\"-Feld finden.",
"disabledWinterEvent": "Während des Winter-Wunderland-Events Teil 4 geschlossen (Weil die Belohnungen mit Gold erworben werden).",
"fix21Streaks": "21-Tage-Strähnen",
@@ -205,4 +205,4 @@
"usernameNotVerified": "Bitte bestätige Deinen Benutzernamen.",
"changeUsernameDisclaimer": "Wir werden bald die Anmeldenamen zu eindeutigen, öffentlichen Benutzernamen umstellen. Dieser Benutzername wird für Einladungen, @Erwähnungen im Chat und Nachrichten verwendet werden.",
"verifyUsernameVeteranPet": "Eines dieser Veteranen-Haustiere wartet auf Dich wenn Du die Bestätigung abgeschlossen hast"
-}
\ No newline at end of file
+}
diff --git a/website/common/locales/en/backgrounds.json b/website/common/locales/en/backgrounds.json
index d23e96845e..a32a664302 100644
--- a/website/common/locales/en/backgrounds.json
+++ b/website/common/locales/en/backgrounds.json
@@ -473,5 +473,13 @@
"backgroundOldFashionedBakeryText": "Old-Fashioned Bakery",
"backgroundOldFashionedBakeryNotes": "Enjoy delicious smells outside an Old-Fashioned Bakery.",
"backgroundValentinesDayFeastingHallText": "Valentine's Day Feasting Hall",
- "backgroundValentinesDayFeastingHallNotes": "Feel the love in a Valentine's Day Feasting Hall."
+ "backgroundValentinesDayFeastingHallNotes": "Feel the love in a Valentine's Day Feasting Hall.",
+
+ "backgrounds032019": "SET 58: Released March 2019",
+ "backgroundDuckPondText": "Duck Pond",
+ "backgroundDuckPondNotes": "Feed aquatic birds at the Duck Pond.",
+ "backgroundFieldWithColoredEggsText": "Field with Colored Eggs",
+ "backgroundFieldWithColoredEggsNotes": "Hunt for springtime treasure in a Field with Colored Eggs.",
+ "backgroundFlowerMarketText": "Flower Market",
+ "backgroundFlowerMarketNotes": "Find the perfect colors for bouquet or garden in a Flower Market."
}
diff --git a/website/common/locales/en/content.json b/website/common/locales/en/content.json
index c4ca376975..b30e6eb8e4 100644
--- a/website/common/locales/en/content.json
+++ b/website/common/locales/en/content.json
@@ -3,7 +3,7 @@
"potionNotes": "Recover 15 Health (Instant Use)",
"armoireText": "Enchanted Armoire",
- "armoireNotesFull": "Open the Armoire to randomly receive special Equipment, Experience, or food! Equipment pieces remaining: ",
+ "armoireNotesFull": "Open the Armoire to randomly receive special Equipment, Experience, or food! Equipment pieces remaining:",
"armoireLastItem": "You've found the last piece of rare Equipment in the Enchanted Armoire.",
"armoireNotesEmpty": "The Armoire will have new Equipment in the first week of every month. Until then, keep clicking for Experience and Food!",
@@ -280,6 +280,7 @@
"hatchingPotionFrost": "Frost",
"hatchingPotionIcySnow": "Icy Snow",
"hatchingPotionRoseQuartz": "Rose Quartz",
+ "hatchingPotionCelestial": "Celestial",
"hatchingPotionNotes": "Pour this on an egg, and it will hatch as a <%= potText(locale) %> pet.",
"premiumPotionAddlNotes": "Not usable on quest pet eggs.",
@@ -364,7 +365,7 @@
"foodCandyWhite": "Vanilla Candy",
"foodCandyWhiteThe": "the Vanilla Candy",
"foodCandyWhiteA": "Vanilla Candy",
- "foodCandyGolden": "Honey Candy ",
+ "foodCandyGolden": "Honey Candy",
"foodCandyGoldenThe": "the Honey Candy",
"foodCandyGoldenA": "Honey Candy",
"foodCandyZombie": "Rotten Candy",
@@ -377,6 +378,37 @@
"foodCandyRedThe": "the Cinnamon Candy",
"foodCandyRedA": "Cinnamon Candy",
+ "foodPieSkeleton": "Bone Marrow Pot Pie",
+ "foodPieSkeletonThe": "the Bone Marrow Pot Pie",
+ "foodPieSkeletonA": "a slice of Bone Marrow Pot Pie",
+ "foodPieBase": "Basic Apple Pie",
+ "foodPieBaseThe": "the Basic Apple Pie",
+ "foodPieBaseA": "a slice of Basic Apple Pie",
+ "foodPieCottonCandyBlue": "Blueberry Pie",
+ "foodPieCottonCandyBlueThe": "the Blueberry Pie",
+ "foodPieCottonCandyBlueA": "a slice of Blueberry Pie",
+ "foodPieCottonCandyPink": "Pink Rhubarb Pie",
+ "foodPieCottonCandyPinkThe": "the Pink Rhubarb Pie",
+ "foodPieCottonCandyPinkA": "a slice of Pink Rhubarb Pie",
+ "foodPieShade": "Dark Chocolate Pie",
+ "foodPieShadeThe": "the Dark Chocolate Pie",
+ "foodPieShadeA": "a slice of Dark Chocolate Pie",
+ "foodPieWhite": "Vanilla Pudding Pie",
+ "foodPieWhiteThe": "the Vanilla Pudding Pie",
+ "foodPieWhiteA": "a slice of Vanilla Pudding Pie",
+ "foodPieGolden": "Golden Banana Cream Pie",
+ "foodPieGoldenThe": "the Golden Banana Cream Pie",
+ "foodPieGoldenA": "a slice of Golden Banana Cream Pie",
+ "foodPieZombie": "Rotten Pie",
+ "foodPieZombieThe": "the Rotten Pie",
+ "foodPieZombieA": "a Rotten slice of Pie",
+ "foodPieDesert": "Desert Dessert Pie",
+ "foodPieDesertThe": "the Desert Dessert Pie",
+ "foodPieDesertA": "a slice of Desert Dessert Pie",
+ "foodPieRed": "Red Cherry Pie",
+ "foodPieRedThe": "the Red Cherry Pie",
+ "foodPieRedA": "a slice of Red Cherry Pie",
+
"foodSaddleText": "Saddle",
"foodSaddleNotes": "Instantly raises one of your pets into a mount.",
"foodSaddleSellWarningNote": "Hey! This is a pretty useful item! Are you familiar with how to use a Saddle with your Pets?",
diff --git a/website/common/locales/en/front.json b/website/common/locales/en/front.json
index be9e61a57a..06ead889ae 100644
--- a/website/common/locales/en/front.json
+++ b/website/common/locales/en/front.json
@@ -23,6 +23,7 @@
"communityBug": "Submit Bug",
"communityExtensions": "Add-ons & Extensions",
"communityFacebook": "Facebook",
+ "communityInstagram": "Instagram",
"communityFeature": "Request Feature",
"communityForum": "Forum",
"communityKickstarter": "Kickstarter",
@@ -275,7 +276,6 @@
"usernameTOSRequirements": "Usernames must conform to our Terms of Service and Community Guidelines. If you didn’t previously set a login name, your username was auto-generated.",
"usernameTaken": "Username already taken.",
"passwordConfirmationMatch": "Password confirmation doesn't match password.",
- "invalidLoginCredentials": "Incorrect username and/or email and/or password.",
"passwordResetPage": "Reset Password",
"passwordReset": "If we have your email on file, instructions for setting a new password have been sent to your email.",
"passwordResetEmailSubject": "Password Reset for Habitica",
diff --git a/website/common/locales/en/gear.json b/website/common/locales/en/gear.json
index 4264510ad4..aa2b1891d5 100644
--- a/website/common/locales/en/gear.json
+++ b/website/common/locales/en/gear.json
@@ -312,6 +312,15 @@
"weaponSpecialWinter2019HealerText": "Wand of Winter",
"weaponSpecialWinter2019HealerNotes": "Winter can be a time of rest and healing, and so this wand of winter magic can help to soothe the most grievous hurts. Increases Intelligence by <%= int %>. Limited Edition 2018-2019 Winter Gear.",
+ "weaponSpecialSpring2019RogueText": "Lightning Bolt",
+ "weaponSpecialSpring2019RogueNotes": "These weapons contain the power of the sky and rain. We recommend that you not use them while immersed in water. Increases Strength by <%= str %>. Limited Edition 2019 Spring Gear.",
+ "weaponSpecialSpring2019WarriorText": "Stem Sword",
+ "weaponSpecialSpring2019WarriorNotes": "Bad habits cower before this verdant blade. Increases Strength by <%= str %>. Limited Edition 2019 Spring Gear.",
+ "weaponSpecialSpring2019MageText": "Amber Staff",
+ "weaponSpecialSpring2019MageNotes": "There's a mosquito embedded in the stone at the end of this staff! May or may not include Dino DNA. Increases Intelligence by <%= int %> and Perception by <%= per %>. Limited Edition 2019 Spring Gear.",
+ "weaponSpecialSpring2019HealerText": "Spring Song",
+ "weaponSpecialSpring2019HealerNotes": "Your song of flowers and rain will soothe the spirits of all who hear. Increases Intelligence by <%= int %>. Limited Edition 2019 Spring Gear.",
+
"weaponMystery201411Text": "Pitchfork of Feasting",
"weaponMystery201411Notes": "Stab your enemies or dig in to your favorite foods - this versatile pitchfork does it all! Confers no benefit. November 2014 Subscriber Item.",
"weaponMystery201502Text": "Shimmery Winged Staff of Love and Also Truth",
@@ -406,7 +415,9 @@
"weaponArmoireArcaneScrollText": "Arcane Scroll",
"weaponArmoireArcaneScrollNotes": "This ancient To-Do list is filled with strange symbols and spells from a forgotten age. Increases Intelligence by <%= int %>. Enchanted Armoire: Scribe Set (Item 3 of 3).",
"weaponArmoireChefsSpoonText": "Chef's Spoon",
- "weaponArmoireChefsSpoonNotes": "Raise it as you release your battle cry: “SPOOOON!!” Increases Intelligence by <%= int %>. Enchanted Armoire: Chef Set (Item 3 of 4). ",
+ "weaponArmoireChefsSpoonNotes": "Raise it as you release your battle cry: “SPOOOON!!” Increases Intelligence by <%= int %>. Enchanted Armoire: Chef Set (Item 3 of 4).",
+ "weaponArmoireVernalTaperText": "Vernal Taper",
+ "weaponArmoireVernalTaperNotes": "The days are getting longer, but this candle will help you find your way before sunrise. Increases Constitution by <%= con %>. Enchanted Armoire: Vernal Vestments Set (Item 3 of 3).",
"armor": "armor",
"armorCapitalized": "Armor",
@@ -699,6 +710,15 @@
"armorSpecialWinter2019HealerText": "Midnight Robe",
"armorSpecialWinter2019HealerNotes": "Without darkness, there wouldn't be any light. These dark robes help bring peace and rest to promote healing. Increases Constitution by <%= con %>. Limited Edition 2018-2019 Winter Gear.",
+ "armorSpecialSpring2019RogueText": "Cloud Armor",
+ "armorSpecialSpring2019RogueNotes": "Some very tuff fluff. Increases Perception by <%= per %>. Limited Edition 2019 Spring Gear.",
+ "armorSpecialSpring2019WarriorText": "Orchid Armor",
+ "armorSpecialSpring2019WarriorNotes": "Steely armor of reinforced petals protects your heart and also looks pretty snazzy. Increases Constitution by <%= con %>. Limited Edition 2019 Spring Gear.",
+ "armorSpecialSpring2019MageText": "Amber Robes",
+ "armorSpecialSpring2019MageNotes": "These robes gather power from magic resin embedded in the fibers of ancient bark that compose the cloth. Increases Intelligence by <%= int %>. Limited Edition 2019 Spring Gear.",
+ "armorSpecialSpring2019HealerText": "Robin Costume",
+ "armorSpecialSpring2019HealerNotes": "Your bright feathers will let everyone know that the cold and dark of winter has passed. Increases Constitution by <%= con %>. Limited Edition 2019 Spring Gear.",
+
"armorMystery201402Text": "Messenger Robes",
"armorMystery201402Notes": "Shimmering and strong, these robes have many pockets to carry letters. Confers no benefit. February 2014 Subscriber Item.",
"armorMystery201403Text": "Forest Walker Armor",
@@ -773,6 +793,8 @@
"armorMystery201809Notes": "You are not only a small and fearsome leaf puff, you are sporting the most beautiful colors of the season! Confers no benefit. September 2018 Subscriber Item.",
"armorMystery201810Text": "Dark Forest Robes",
"armorMystery201810Notes": "These robes are extra warm to protect you from the ghastly cold of haunted realms. Confers no benefit. October 2018 Subscriber Item.",
+ "armorMystery201903Text": "Shell-ebration Armor",
+ "armorMystery201903Notes": "People are dye-ing to know where you got this egg-cellent outfit! Confers no benefit. March 2019 Subscriber Item.",
"armorMystery301404Text": "Steampunk Suit",
"armorMystery301404Notes": "Dapper and dashing, wot! Confers no benefit. February 3015 Subscriber Item.",
"armorMystery301703Text": "Steampunk Peacock Gown",
@@ -881,7 +903,9 @@
"armorArmoireScribesRobeText": "Scribe's Robes",
"armorArmoireScribesRobeNotes": "These velvety robes are woven with inspirational and motivational magic. Increases Perception and Intelligence by <%= attrs %> each. Enchanted Armoire: Scribe Set (Item 1 of 3).",
"armorArmoireChefsJacketText": "Chef's Jacket",
- "armorArmoireChefsJacketNotes": "This thick cotton jacket is double-breasted to protect you from spills (and conveniently reversible…). Increases Intelligence by <%= int %>. Enchanted Armoire: Chef Set (Item 2 of 4). ",
+ "armorArmoireChefsJacketNotes": "This thick cotton jacket is double-breasted to protect you from spills (and conveniently reversible…). Increases Intelligence by <%= int %>. Enchanted Armoire: Chef Set (Item 2 of 4).",
+ "armorArmoireVernalVestmentText": "Vernal Vestment",
+ "armorArmoireVernalVestmentNotes": "This silky garment is perfect for enjoying mild spring weather in style. Increases Strength and Intelligence by <%= attrs %> each. Enchanted Armoire: Vernal Vestments Set (Item 2 of 3).",
"headgear": "helm",
"headgearCapitalized": "Headgear",
@@ -971,6 +995,8 @@
"headSpecialTurkeyHelmBaseNotes": "Your Turkey Day look will be complete when you don this beaked helm! Confers no benefit.",
"headSpecialTurkeyHelmGildedText": "Gilded Turkey Helm",
"headSpecialTurkeyHelmGildedNotes": "Gobble gobble! Bling bling! Confers no benefit.",
+ "headSpecialPiDayText": "Pi Hat",
+ "headSpecialPiDayNotes": "Try to balance this slice of delicious pie on your head while walking in a circle. Or throw it at a red Daily! Or you could just eat it. Your choice! Confers no benefit.",
"headSpecialNyeText": "Absurd Party Hat",
"headSpecialNyeNotes": "You've received an Absurd Party Hat! Wear it with pride while ringing in the New Year! Confers no benefit.",
@@ -1173,6 +1199,15 @@
"headSpecialWinter2019HealerText": "Starry Crown",
"headSpecialWinter2019HealerNotes": "On the darkest, coldest winter night, one particular star shines its brightest. This crown is made from metal from that star, to help you shine! Increases Intelligence by <%= int %>. Limited Edition 2018-2019 Winter Gear.",
+ "headSpecialSpring2019RogueText": "Cloud Helm",
+ "headSpecialSpring2019RogueNotes": "No one will notice a cloud quietly drifting toward their stash of Gold, right? Increases Perception by <%= per %>. Limited Edition 2019 Spring Gear.",
+ "headSpecialSpring2019WarriorText": "Orchid Helm",
+ "headSpecialSpring2019WarriorNotes": "This helm is unbreakable and tough! Also it attracts butterflies. Increases Strength by <%= str %>. Limited Edition 2019 Spring Gear.",
+ "headSpecialSpring2019MageText": "Amber Hat",
+ "headSpecialSpring2019MageNotes": "A glowing amber gem grants this hat the power of arcane natural forces. Increases Perception by <%= per %>. Limited Edition 2019 Spring Gear.",
+ "headSpecialSpring2019HealerText": "Robin Helm",
+ "headSpecialSpring2019HealerNotes": "Be ready for the first day of spring with this cute beaky helm. Increases Intelligence by <%= int %>. Limited Edition 2019 Spring Gear.",
+
"headSpecialGaymerxText": "Rainbow Warrior Helm",
"headSpecialGaymerxNotes": "In celebration of the GaymerX Conference, this special helmet is decorated with a radiant, colorful rainbow pattern! GaymerX is a game convention celebrating LGTBQ and gaming and is open to everyone.",
@@ -1258,6 +1293,8 @@
"headMystery201811Notes": "Wear this feathered hat to stand out at even the fanciest wizardly gatherings! Confers no benefit. November 2018 Subscriber Item.",
"headMystery201901Text": "Polaris Helm",
"headMystery201901Notes": "The glowing gems on this helm contain light magically captured from winter auroras. Confers no benefit. January 2019 Subscriber Item.",
+ "headMystery201903Text": "Sunny Side Up Helm",
+ "headMystery201903Notes": "Some may call you an egghead, but that's OK because you know how to take a yolk. Confers no benefit. March 2019 Subscriber Item.",
"headMystery301404Text": "Fancy Top Hat",
"headMystery301404Notes": "A fancy top hat for the finest of gentlefolk! January 3015 Subscriber Item. Confers no benefit.",
"headMystery301405Text": "Basic Top Hat",
@@ -1278,7 +1315,7 @@
"headArmoireRancherHatText": "Rancher Hat",
"headArmoireRancherHatNotes": "Round up your pets and wrangle your mounts while wearing this magical Rancher Hat! Increases Strength by <%= str %>, Perception by <%= per %>, and Intelligence by <%= int %>. Enchanted Armoire: Rancher Set (Item 1 of 3).",
"headArmoireBlueHairbowText": "Blue Hairbow",
- "headArmoireBlueHairbowNotes": "Become perceptive, tough, and smart while wearing this beautiful Blue Hairbow! Increases Perception by <%= per %>, Constitution by <%= con %>, and Intelligence by <%= int %>. Enchanted Armoire: Independent Item.",
+ "headArmoireBlueHairbowNotes": "Become perceptive, tough, and smart while wearing this beautiful Blue Hairbow! Increases Perception by <%= per %>, Constitution by <%= con %>, and Intelligence by <%= int %>. Enchanted Armoire: Blue Hairbow Set (Item 1 of 2).",
"headArmoireRoyalCrownText": "Royal Crown",
"headArmoireRoyalCrownNotes": "Hooray for the ruler, mighty and strong! Increases Strength by <%= str %>. Enchanted Armoire: Royal Set (Item 1 of 3).",
"headArmoireGoldenLaurelsText": "Golden Laurels",
@@ -1366,7 +1403,9 @@
"headArmoireVeilOfSpadesText": "Veil of Spades",
"headArmoireVeilOfSpadesNotes": "A shadowy and mysterious veil that will boost your stealth. Increases Perception by <%= per %>. Enchanted Armoire: Ace of Spades Set (Item 1 of 3).",
"headArmoireToqueBlancheText": "Toque Blanche",
- "headArmoireToqueBlancheNotes": "According to legend, the number of folds in this hat indicate the number of ways you know how to cook an egg! Is it accurate? Increases Perception by <%= per %>. Enchanted Armoire: Chef Set (Item 1 of 4). ",
+ "headArmoireToqueBlancheNotes": "According to legend, the number of folds in this hat indicate the number of ways you know how to cook an egg! Is it accurate? Increases Perception by <%= per %>. Enchanted Armoire: Chef Set (Item 1 of 4).",
+ "headArmoireVernalHenninText": "Vernal Hennin",
+ "headArmoireVernalHenninNotes": "More than just a pretty hat, this conical chapeau can also hold a rolled-up To-Do list inside. Increases Perception by <%= per %>. Enchanted Armoire: Vernal Vestments Set (Item 1 of 3).",
"offhand": "off-hand item",
"offhandCapitalized": "Off-Hand Item",
@@ -1418,6 +1457,8 @@
"shieldSpecialWintryMirrorNotes": "How else to best admire your wintry look? Increases Intelligence by <%= int %>.",
"shieldSpecialWakizashiText": "Wakizashi",
"shieldSpecialWakizashiNotes": "This short sword is perfect for close-quarters battles with your Dailies! Increases Constitution by <%= con %>.",
+ "shieldSpecialPiDayText": "Pi Shield",
+ "shieldSpecialPiDayNotes": "We dare you to calculate the ratio of this shield's circumference to its deliciousness! Confers no benefit.",
"shieldSpecialYetiText": "Yeti-Tamer Shield",
"shieldSpecialYetiNotes": "This shield reflects light from the snow. Increases Constitution by <%= con %>. Limited Edition 2013-2014 Winter Gear.",
@@ -1558,6 +1599,11 @@
"shieldSpecialWinter2019HealerText": "Enchanted Ice Crystals",
"shieldSpecialWinter2019HealerNotes": "Thin ice may break, but these perfect crystals will turn back any blow before it lands. Increases Constitution by <%= con %>. Limited Edition 2018-2019 Winter Gear.",
+ "shieldSpecialSpring2019WarriorText": "Leafy Shield",
+ "shieldSpecialSpring2019WarriorNotes": "Let the power of chlorophyll keep your enemies at bay! Increases Constitution by <%= con %>. Limited Edition 2019 Spring Gear.",
+ "shieldSpecialSpring2019HealerText": "Eggshell Shield",
+ "shieldSpecialSpring2019HealerNotes": "This bright shield is actually made of candy-coated chocolate. Increases Constitution by <%= con %>. Limited Edition 2019 Spring Gear.",
+
"shieldMystery201601Text": "Resolution Slayer",
"shieldMystery201601Notes": "This blade can be used to parry away all distractions. Confers no benefit. January 2016 Subscriber Item.",
"shieldMystery201701Text": "Time-Freezer Shield",
diff --git a/website/common/locales/en/generic.json b/website/common/locales/en/generic.json
index 57adad943b..f8fa686631 100644
--- a/website/common/locales/en/generic.json
+++ b/website/common/locales/en/generic.json
@@ -34,6 +34,7 @@
"saveEdits": "Save Edits",
"showMore": "Show More",
"showLess": "Show Less",
+ "options": "Options",
"expandToolbar": "Expand Toolbar",
"collapseToolbar": "Collapse Toolbar",
diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json
index 31b5c08887..496f1277f7 100644
--- a/website/common/locales/en/groups.json
+++ b/website/common/locales/en/groups.json
@@ -162,6 +162,7 @@
"abuseFlagModalBody": "Are you sure you want to report this post? You should only report a post that violates the <%= firstLinkStart %>Community Guidelines<%= linkEnd %> and/or <%= secondLinkStart %>Terms of Service<%= linkEnd %>. Inappropriately reporting a post is a violation of the Community Guidelines and may give you an infraction.",
"abuseFlagModalButton": "Report Violation",
"abuseReported": "Thank you for reporting this violation. The moderators have been notified.",
+ "pmReported": "Thank you for reporting this message.",
"abuseAlreadyReported": "You have already reported this message.",
"whyReportingPost": "Why are you reporting this post?",
"whyReportingPostPlaceholder": "Please help our moderators by letting us know why you are reporting this post for a violation, e.g., spam, swearing, religious oaths, bigotry, slurs, adult topics, violence.",
@@ -230,9 +231,9 @@
"memberCannotRemoveYourself": "You cannot remove yourself!",
"groupMemberNotFound": "User not found among group's members",
"mustBeGroupMember": "Must be member of the group.",
- "canOnlyInviteEmailUuid": "Can only invite using user IDs, emails, or usernames.",
+ "canOnlyInviteEmailUuid": "Can only invite using User IDs, emails, or usernames.",
"inviteMissingEmail": "Missing email address in invite.",
- "inviteMissingUuid": "Missing user id in invite",
+ "inviteMissingUuid": "Missing User ID in invite",
"inviteMustNotBeEmpty": "Invite must not be empty.",
"partyMustbePrivate": "Parties must be private",
"userAlreadyInGroup": "UserID: <%= userId %>, User \"<%= username %>\" already in that group.",
diff --git a/website/common/locales/en/limited.json b/website/common/locales/en/limited.json
index 9405563f19..9251f9ee59 100644
--- a/website/common/locales/en/limited.json
+++ b/website/common/locales/en/limited.json
@@ -134,6 +134,10 @@
"winter2019PyrotechnicSet": "Pyrotechnic (Mage)",
"winter2019WinterStarSet": "Winter Star (Healer)",
"winter2019PoinsettiaSet": "Poinsettia (Rogue)",
+ "spring2019OrchidWarriorSet": "Orchid (Warrior)",
+ "spring2019AmberMageSet": "Amber (Mage)",
+ "spring2019RobinHealerSet": "Robin (Healer)",
+ "spring2019CloudRogueSet": "Cloud (Rogue)",
"eventAvailability": "Available for purchase until <%= date(locale) %>.",
"dateEndMarch": "April 30",
"dateEndApril": "April 19",
diff --git a/website/common/locales/en/messages.json b/website/common/locales/en/messages.json
index 0a0e9b0c15..b1aa99d069 100644
--- a/website/common/locales/en/messages.json
+++ b/website/common/locales/en/messages.json
@@ -45,7 +45,7 @@
"messageAuthEmailTaken": "Email already taken",
"messageAuthNoUserFound": "No user found.",
"messageAuthMustBeLoggedIn": "You must be logged in.",
- "messageAuthMustIncludeTokens": "You must include a token and uid (user id) in your request",
+ "messageAuthMustIncludeTokens": "You must include a token and uid (User ID) in your request",
"messageGroupAlreadyInParty": "Already in a party, try refreshing.",
"messageGroupOnlyLeaderCanUpdate": "Only the group leader can update the group!",
@@ -71,6 +71,7 @@
"beginningOfConversation": "This is the beginning of your conversation with <%= userName %>. Remember to be kind, respectful, and follow the Community Guidelines!",
"messageDeletedUser": "Sorry, this user has deleted their account.",
-
- "messageMissingDisplayName": "Missing display name."
+ "messageMissingDisplayName": "Missing display name.",
+ "reportedMessage": "You have reported this message to moderators.",
+ "canDeleteNow": "You can now delete the message if you wish."
}
diff --git a/website/common/locales/en/npc.json b/website/common/locales/en/npc.json
index be80194f06..3e02837d6c 100644
--- a/website/common/locales/en/npc.json
+++ b/website/common/locales/en/npc.json
@@ -112,7 +112,7 @@
"donationDesc": "20 Gems, Donation to Habitica",
"payWithCard": "Pay with Card",
"payNote": "Note: PayPal sometimes takes a long time to clear. We recommend paying with card.",
- "card": "Credit Card (using Stripe)",
+ "card": "Credit Card",
"amazonInstructions": "Click the button to pay using Amazon Payments",
"paymentMethods": "Purchase using",
"paymentSuccessful": "Your payment was successful!",
@@ -120,6 +120,7 @@
"paymentYouSentGems": "You sent <%= name %>:",
"paymentYouSentSubscription": "You sent <%= name %> a <%= months %>-months Habitica subscription.",
"paymentSubBilling": "Your subscription will be billed $<%= amount %> every <%= months %> months.",
+ "paymentAutoRenew": "This subscription will auto-renew until it is canceled. If you need to cancel this subscription, you can do so from your settings.",
"success": "Success!",
"classGear": "Class Gear",
diff --git a/website/common/locales/en/subscriber.json b/website/common/locales/en/subscriber.json
index 65d2ea56a0..303284dfb3 100644
--- a/website/common/locales/en/subscriber.json
+++ b/website/common/locales/en/subscriber.json
@@ -155,6 +155,7 @@
"mysterySet201812": "Arctic Fox Set",
"mysterySet201901": "Polaris Set",
"mysterySet201902": "Cryptic Crush Set",
+ "mysterySet201903": "Egg-squisite Set",
"mysterySet301404": "Steampunk Standard Set",
"mysterySet301405": "Steampunk Accessories Set",
"mysterySet301703": "Peacock Steampunk Set",
diff --git a/website/common/locales/pt_BR/backgrounds.json b/website/common/locales/pt_BR/backgrounds.json
index e967ddf3bc..15dc2022b6 100644
--- a/website/common/locales/pt_BR/backgrounds.json
+++ b/website/common/locales/pt_BR/backgrounds.json
@@ -408,5 +408,7 @@
"backgroundArchaeologicalDigText": "Escavação Arqueológica",
"backgroundArchaeologicalDigNotes": "Desenterre segredos de um passado remoto em uma Escavação Arqueológica.",
"backgroundScribesWorkshopText": "Oficina do Escriba",
- "backgroundScribesWorkshopNotes": "Escreva seu próximo grande pergaminho na Oficina do Escriba."
-}
\ No newline at end of file
+ "backgroundScribesWorkshopNotes": "Escreva seu próximo grande pergaminho na Oficina do Escriba.",
+ "backgrounds022019": "Conjunto 57: Lançado em Fevereiro de 2019",
+ "backgroundMedievalKitchenText": "Cozinheiro Medieval"
+}
diff --git a/website/common/locales/ru/backgrounds.json b/website/common/locales/ru/backgrounds.json
index e34407708a..3c8dcf4f43 100644
--- a/website/common/locales/ru/backgrounds.json
+++ b/website/common/locales/ru/backgrounds.json
@@ -92,7 +92,7 @@
"backgroundDriftingRaftText": "Дрейфующий плот",
"backgroundDriftingRaftNotes": "Поплавайте на дрейфующем плоте.",
"backgroundShimmeryBubblesText": "Блестящие пузыри",
- "backgroundShimmeryBubblesNotes": "Переплывите море Блестящих пузырей",
+ "backgroundShimmeryBubblesNotes": "Переплывите море Блестящих пузырей.",
"backgroundIslandWaterfallsText": "Остров Водопадов",
"backgroundIslandWaterfallsNotes": "Устройте пикник на Острове Водопадов.",
"backgrounds072015": "Набор 14: Выпущен в июле 2015",
@@ -111,7 +111,7 @@
"backgroundTwinklyPartyLightsNotes": "Танцуйте под мерцающими праздничными огоньками!",
"backgrounds092015": "Набор 16: Выпущен в сентябре 2015",
"backgroundMarketText": "Рынок Habitica",
- "backgroundMarketNotes": "Покупайте на рынке Habitica",
+ "backgroundMarketNotes": "Покупайте на рынке Habitica.",
"backgroundStableText": "Стойла Habitica",
"backgroundStableNotes": "Заботьтесь о скакунах в стойлах Habitica.",
"backgroundTavernText": "Таверна Habitica",
@@ -157,7 +157,7 @@
"backgroundRainforestText": "Тропический лес",
"backgroundRainforestNotes": "Отправьтесь в тропический лес.",
"backgroundStoneCircleText": "Круг Каменных глыб",
- "backgroundStoneCircleNotes": "Творите заклинания в круге Каменных глыб!",
+ "backgroundStoneCircleNotes": "Творите заклинания в круге каменных глыб!",
"backgrounds042016": "Набор 23: Выпущен в апреле 2016",
"backgroundArcheryRangeText": "Лучное стрельбище",
"backgroundArcheryRangeNotes": "Практикуйтесь на лучном стрельбище.",
@@ -165,7 +165,7 @@
"backgroundGiantFlowersNotes": "Веселитесь над Гигантскими Цветами.",
"backgroundRainbowsEndText": "Там, где кончается радуга",
"backgroundRainbowsEndNotes": "Отыщите золото там, где кончается радуга.",
- "backgrounds052016": "Набор 24: Выпущен в Мае 2016",
+ "backgrounds052016": "Набор 24: Выпущен в мае 2016",
"backgroundBeehiveText": "Пчелиный улей",
"backgroundBeehiveNotes": "Жужжите и танцуйте в Пчелином улье.",
"backgroundGazeboText": "Беседка",
@@ -207,12 +207,12 @@
"backgroundStrangeSewersNotes": "Пройдите сквозь Странные стоки.",
"backgroundRainyCityText": "Дождливый город",
"backgroundRainyCityNotes": "Шлепайте по лужам Дождливого города.",
- "backgrounds112016": "Набор 30: Выпущен в октябре 2016",
+ "backgrounds112016": "Набор 30: Выпущен в ноябре 2016",
"backgroundMidnightCloudsText": "Полуночные облака",
"backgroundMidnightCloudsNotes": "Совершите полет сквозь Полуночные Облака.",
"backgroundStormyRooftopsText": "Крыши, открытые всем ветрам",
"backgroundStormyRooftopsNotes": "Преодолейте путь по Крышам, открытым всем ветрам.",
- "backgroundWindyAutumnText": "Ветренная Осень ",
+ "backgroundWindyAutumnText": "Ветреная осень",
"backgroundWindyAutumnNotes": "Ветреная Осень — время гоняться за листьями.",
"incentiveBackgrounds": "Набор простых фонов",
"backgroundVioletText": "Фиолетовый",
@@ -243,26 +243,26 @@
"backgroundStoikalmVolcanoesNotes": "Исследуйте Стойкальмский вулкан.",
"backgrounds022017": "Набор 33: Выпущен в феврале 2017",
"backgroundBellTowerText": "Колокольня",
- "backgroundBellTowerNotes": "Поднимитесь на Колокольню",
+ "backgroundBellTowerNotes": "Поднимитесь на колокольню.",
"backgroundTreasureRoomText": "Сокровищница",
- "backgroundTreasureRoomNotes": "Окунитесь в роскошь Сокровищницы",
+ "backgroundTreasureRoomNotes": "Окунитесь в роскошь сокровищницы.",
"backgroundWeddingArchText": "Свадебная Арка",
- "backgroundWeddingArchNotes": "Покрасуйтесь под Свадебной Аркой",
+ "backgroundWeddingArchNotes": "Покрасуйтесь под свадебной аркой.",
"backgrounds032017": "Набор 34: Выпущен в марте 2017",
"backgroundMagicBeanstalkText": "Волшебный бобовый стебель",
- "backgroundMagicBeanstalkNotes": "Подняться по Волшебному Бобовому Стеблю",
+ "backgroundMagicBeanstalkNotes": "Поднимитесь по волшебному бобовому стеблю.",
"backgroundMeanderingCaveText": "Извилистая пещера",
- "backgroundMeanderingCaveNotes": "Исследуйте Извилистую пещеру",
+ "backgroundMeanderingCaveNotes": "Исследуйте извилистую пещеру.",
"backgroundMistiflyingCircusText": "Мистический Цирк",
- "backgroundMistiflyingCircusNotes": "Пирушка в Мистическом Цирке",
- "backgrounds042017": "Набор 35: Выпущен в Апреле 2017",
+ "backgroundMistiflyingCircusNotes": "Попируйте в мистическом цирке.",
+ "backgrounds042017": "Набор 35: Выпущен в апреле 2017",
"backgroundBugCoveredLogText": "Покрытое жучками бревно",
"backgroundBugCoveredLogNotes": "Исследуйте покрытое жучками бревно.",
- "backgroundGiantBirdhouseText": "Гигантский скворечник ",
- "backgroundGiantBirdhouseNotes": "Взгромоздиться на птичий скворечник",
+ "backgroundGiantBirdhouseText": "Гигантский скворечник",
+ "backgroundGiantBirdhouseNotes": "Взгромоздитесь на птичий скворечник.",
"backgroundMistShroudedMountainText": "Туманная гора",
"backgroundMistShroudedMountainNotes": "Встреча на вершине Туманной горы.",
- "backgrounds052017": "Набор 36: Выпущен в Мае 2017",
+ "backgrounds052017": "Набор 36: Выпущен в мае 2017",
"backgroundGuardianStatuesText": "Статуи стражей",
"backgroundGuardianStatuesNotes": "Встаньте на дежурство перед Статуями Стражей.",
"backgroundHabitCityStreetsText": "Улицы города Habit",
@@ -276,11 +276,11 @@
"backgroundOceanSunriseNotes": "Восхититься рассветом у океана.",
"backgroundSandcastleText": "Замок из песка",
"backgroundSandcastleNotes": "Править замком из песка.",
- "backgrounds072017": "Набор 38: выпущен в июле 2017 года",
+ "backgrounds072017": "Набор 38: Выпущен в июле 2017",
"backgroundGiantSeashellText": "Огромная ракушка",
"backgroundGiantSeashellNotes": "Развалитесь в огромной ракушке.",
"backgroundKelpForestText": "Лес вородослей",
- "backgroundKelpForestNotes": "Проплывите по лесу из водорослей",
+ "backgroundKelpForestNotes": "Проплывите по лесу из водорослей.",
"backgroundMidnightLakeText": "Полуночное озеро",
"backgroundMidnightLakeNotes": "Отдохните в полночь у озера.",
"backgrounds082017": "Набор 39: Выпущен в августе 2017",
@@ -288,9 +288,9 @@
"backgroundBackOfGiantBeastNotes": "Ехать на Спине Гиганта.",
"backgroundDesertDunesText": "Пустынные дюны",
"backgroundDesertDunesNotes": "Смело обследовать Пустынные Дюны.",
- "backgroundSummerFireworksText": "Летние фейерверки ",
+ "backgroundSummerFireworksText": "Летние фейерверки",
"backgroundSummerFireworksNotes": "Отпраздновать День Именования Habitica летними фейерверками!",
- "backgrounds092017": "Набор 40: выпущен в сентябре 2017",
+ "backgrounds092017": "Набор 40: Выпущен в сентябре 2017",
"backgroundBesideWellText": "Возле колодца",
"backgroundBesideWellNotes": "Прогуляйтесь возле колодца.",
"backgroundGardenShedText": "Сарай в саду",
@@ -306,11 +306,11 @@
"backgroundTarPitsNotes": "Пройдитесь на цыпочках по смоляным ямам.",
"backgrounds112017": "Набор 42: Выпущен в ноябре 2017",
"backgroundFiberArtsRoomText": "Прядильная комната",
- "backgroundFiberArtsRoomNotes": "Сплетите нить в прядильной комнате",
+ "backgroundFiberArtsRoomNotes": "Сплетите нить в прядильной комнате.",
"backgroundMidnightCastleText": "Полуночной замок",
"backgroundMidnightCastleNotes": "Совершите ночную прогулку у замка.",
"backgroundTornadoText": "Торнадо",
- "backgroundTornadoNotes": "Пролетите сквозь торнадо",
+ "backgroundTornadoNotes": "Пролетите сквозь торнадо.",
"backgrounds122017": "Набор 43: Выпущен в декабре 2017",
"backgroundCrosscountrySkiTrailText": "Лыжная трасса в лесу",
"backgroundCrosscountrySkiTrailNotes": "Проскользите по Лыжной трассе в лесу.",
@@ -339,35 +339,35 @@
"backgroundElegantBalconyNotes": "Осмотрите пейзаж с Элегантного балкона.",
"backgroundDrivingACoachText": "Кучер повозки",
"backgroundDrivingACoachNotes": "Наслаждайтесь видом на поля цветов, управляя повозкой.",
- "backgrounds042018": "Набор 47: Выпущен в Апреле 2018",
+ "backgrounds042018": "Набор 47: Выпущен в апреле 2018",
"backgroundTulipGardenText": "Сад тюльпанов",
"backgroundTulipGardenNotes": "Пройдитесь на цыпочках через Сад Тюльпанов.",
"backgroundFlyingOverWildflowerFieldText": "Поле цветов",
"backgroundFlyingOverWildflowerFieldNotes": "Парите над полем цветов.",
"backgroundFlyingOverAncientForestText": "Древний лес",
"backgroundFlyingOverAncientForestNotes": "Летите над куполом Древнего Леса.",
- "backgrounds052018": "Набор 48: Выпущен в Мае 2018",
+ "backgrounds052018": "Набор 48: Выпущен в мае 2018",
"backgroundTerracedRiceFieldText": "Рисовое поле",
- "backgroundTerracedRiceFieldNotes": "Насладитесь усеянным рисовым полем в сезон созревания",
+ "backgroundTerracedRiceFieldNotes": "Насладитесь усеянным рисовым полем в сезон созревания.",
"backgroundFantasticalShoeStoreText": "Удивительная обувная лавка",
"backgroundFantasticalShoeStoreNotes": "Найдите себе новую пару обуви в Удивительной обувной лавке.",
"backgroundChampionsColosseumText": "Колизей Чемпионов",
"backgroundChampionsColosseumNotes": "Окунитесь в лучах славы на Колизеи Чемпионов.",
- "backgrounds062018": "Набор 49: Выпущен в Июне 2018",
+ "backgrounds062018": "Набор 49: Выпущен в июне 2018",
"backgroundDocksText": "Причал",
- "backgroundDocksNotes": "Рыбачить с поверхности причала",
+ "backgroundDocksNotes": "Порыбачьте на причале.",
"backgroundRowboatText": "Гребная лодка",
- "backgroundRowboatNotes": "Петь песни в гребной лодке",
+ "backgroundRowboatNotes": "Спойте в гребной лодке.",
"backgroundPirateFlagText": "Пиратский флаг",
- "backgroundPirateFlagNotes": "Расправить устрашающий Пиратский флаг",
- "backgrounds072018": "Набор 50: Выпущен в Июле 2018",
+ "backgroundPirateFlagNotes": "Расправьте устрашающий пиратский флаг.",
+ "backgrounds072018": "Набор 50: Выпущен в июле 2018",
"backgroundDarkDeepText": "Темные глубины",
"backgroundDarkDeepNotes": "Ныряйте в темные глубины среди светящихся обитателей.",
"backgroundDilatoryCityText": "Город Промедления",
"backgroundDilatoryCityNotes": "Побродите по подводному городу Промедления.",
"backgroundTidePoolText": "Приливной бассейн",
"backgroundTidePoolNotes": "Исследуйте морскую фауну возле приливного бассейна.",
- "backgrounds082018": "Набор 51: Выпущен в Августе 2018",
+ "backgrounds082018": "Набор 51: Выпущен в августе 2018",
"backgroundTrainingGroundsText": "Тренировочная площадка",
"backgroundTrainingGroundsNotes": "Сразитесь на тренировочной площадке.",
"backgroundFlyingOverRockyCanyonText": "Скалистый каньон",
@@ -381,7 +381,7 @@
"backgroundGiantBookNotes": "Прочтите гигантскую книгу, пройдя по её страницам.",
"backgroundCozyBarnText": "Уютный амбар",
"backgroundCozyBarnNotes": "Отдохните со своими питомцами и скакунами в вашем уютном амбаре.",
- "backgrounds102018": "Набор 53: Выпущен в Октябре 2018",
+ "backgrounds102018": "Набор 53: Выпущен в октябре 2018",
"backgroundBayouText": "Болото",
"backgroundBayouNotes": "Насладитесь блеском светлячков на болоте.",
"backgroundCreepyCastleText": "Пугающий замок",
@@ -408,5 +408,12 @@
"backgroundArchaeologicalDigText": "Археологические раскопки",
"backgroundArchaeologicalDigNotes": "Раскройте тайны древнего прошлого на археологических раскопках.",
"backgroundScribesWorkshopText": "Мастерская писца",
- "backgroundScribesWorkshopNotes": "Напишите свой следующий великий свиток в мастерской писца."
-}
\ No newline at end of file
+ "backgroundScribesWorkshopNotes": "Напишите свой следующий великий свиток в мастерской писца.",
+ "backgrounds022019": "Набор 57: Выпущен в феврале 2019",
+ "backgroundMedievalKitchenText": "Средневековая кухня",
+ "backgroundMedievalKitchenNotes": "Приготовьте бурю на средневековой кухне.",
+ "backgroundOldFashionedBakeryText": "Старомодная пекарня",
+ "backgroundOldFashionedBakeryNotes": "Насладитесь вкусными запахами из старомодной пекарни.",
+ "backgroundValentinesDayFeastingHallText": "Праздничный зал Дня святого Валентина",
+ "backgroundValentinesDayFeastingHallNotes": "Почувствуйте любовь в праздничном зале Дня святого Валентина."
+}
diff --git a/website/common/locales/ru/challenge.json b/website/common/locales/ru/challenge.json
index 24e7747f86..62b64ff8f7 100644
--- a/website/common/locales/ru/challenge.json
+++ b/website/common/locales/ru/challenge.json
@@ -87,7 +87,7 @@
"onlyLeaderUpdateChal": "Только лидер испытания может обновить его.",
"winnerNotFound": "Победитель с ID \"<%= userId %>\" не найден или не участвует в этом испытании.",
"noCompletedTodosChallenge": "\"includeCompletedTodos\" не поддерживается при получении заданий испытания.",
- "userTasksNoChallengeId": "Когда \"tasksOwner\" - \"user\", \"challengeId\" не может быть передано",
+ "userTasksNoChallengeId": "Когда \"tasksOwner\" - \"user\", \"challengeId\" не может быть передано.",
"onlyChalLeaderEditTasks": "Задания, связанные с испытанием, может редактировать только лидер.",
"userAlreadyInChallenge": "Пользователь уже участвует в этом испытании.",
"cantOnlyUnlinkChalTask": "Только испорченные задания испытаний могут быть откреплены.",
@@ -97,7 +97,7 @@
"myChallenges": "Мои испытания",
"findChallenges": "Найти испытания",
"noChallengeTitle": "У вас нет испытаний.",
- "challengeDescription1": "Испытания это мероприятия сообщества, в которых игроки получают награду за прохождение групповых заданий",
+ "challengeDescription1": "Испытания – это мероприятия сообщества, в которых игроки получают награду за прохождение групповых заданий.",
"challengeDescription2": "Найдите рекомендованные испытания по вашим интересам, посмотрите общедоступные испытания или создайте свое собственное.",
"noChallengeMatchFilters": "Не найдены подходящие Испытания.",
"createdBy": "Создано",
@@ -136,4 +136,4 @@
"selectMember": "Выбрать учасника",
"confirmKeepChallengeTasks": "Вы хотите оставить задания испытания?",
"selectParticipant": "Выбрать участника"
-}
\ No newline at end of file
+}
diff --git a/website/common/locales/ru/content.json b/website/common/locales/ru/content.json
index 00db77210c..99077da185 100644
--- a/website/common/locales/ru/content.json
+++ b/website/common/locales/ru/content.json
@@ -201,7 +201,7 @@
"hatchingPotionThunderstorm": "Грозовой",
"hatchingPotionGhost": "Призрачный",
"hatchingPotionRoyalPurple": "Королевский пурпурный",
- "hatchingPotionHolly": "Oстролистный",
+ "hatchingPotionHolly": "Остролистный",
"hatchingPotionCupid": "Амурный",
"hatchingPotionShimmer": "Мерцающий",
"hatchingPotionFairy": "Сказочный",
@@ -306,5 +306,6 @@
"foodSaddleText": "Седло",
"foodSaddleNotes": "Моментально делает одного из питомцев скакуном.",
"foodSaddleSellWarningNote": "Эй! Это довольно полезная вещь! Знаете ли вы, как пользоваться седлом?",
- "foodNotes": "Кормите этим питомца, и он сможет вырасти выносливым скакуном."
-}
\ No newline at end of file
+ "foodNotes": "Кормите этим питомца, и он сможет вырасти выносливым скакуном.",
+ "hatchingPotionRoseQuartz": "Розовый кварцевый"
+}
diff --git a/website/common/locales/ru/gear.json b/website/common/locales/ru/gear.json
index 81621cd3f8..2f3cd32674 100644
--- a/website/common/locales/ru/gear.json
+++ b/website/common/locales/ru/gear.json
@@ -131,7 +131,7 @@
"weaponSpecialSpringHealerText": "Милая косточка",
"weaponSpecialSpringHealerNotes": "АПОРТ! Увеличивает интеллект на <%= int %>. Ограниченный выпуск весны 2014.",
"weaponSpecialSummerRogueText": "Пиратская сабля",
- "weaponSpecialSummerRogueNotes": "Встать на якорь! Пусть задания прогуляются по доске! Увеличивает силу на <%= str %>. Ограниченный выпуск лета 2014.",
+ "weaponSpecialSummerRogueNotes": "Тысяча чертей! Заставь свои задания прогуляться по доске! Увеличивает силу на <%= str %>. Ограниченный выпуск лета 2014.",
"weaponSpecialSummerWarriorText": "Нож морехода",
"weaponSpecialSummerWarriorNotes": "Ни одно задание не посмеет связаться с этим зазубренным ножом! Увеличивает силу на <%= str %>. Ограниченный выпуск лета 2014.",
"weaponSpecialSummerMageText": "Ловец ламинарий",
@@ -175,8 +175,8 @@
"weaponSpecialFall2015WarriorText": "Деревянная доска",
"weaponSpecialFall2015WarriorNotes": "Отлично подходит для поднимания вещей в кукурузных полях и/или для расправы с заданиями. Увеличивает силу на <%= str %>. Ограниченный выпуск осени 2015.",
"weaponSpecialFall2015MageText": "Волшебная нить",
- "weaponSpecialFall2015MageNotes": "Властительная Лоскутная Ведьма может управлять этой волшебной нитью, даже не прикасаясь к ней! Увеличивает интеллект на <%= int %> и восприятие на <%= per %>. Ограниченный выпуск осени 2015.",
- "weaponSpecialFall2015HealerText": "Болотно-слизневое зелье ",
+ "weaponSpecialFall2015MageNotes": "Властительная лоскутная колдунья может управлять этой волшебной нитью, даже не прикасаясь к ней! Увеличивает интеллект на <%= int %> и восприятие на <%= per %>. Ограниченный выпуск осени 2015.",
+ "weaponSpecialFall2015HealerText": "Болотно-слизневое зелье",
"weaponSpecialFall2015HealerNotes": "Сварено на славу! Теперь вам только остаётся заставить себя это выпить. Увеличивает интеллект на <%= int %>. Ограниченный выпуск осени 2015.",
"weaponSpecialWinter2016RogueText": "Кружка какао",
"weaponSpecialWinter2016RogueNotes": "Согревающее питье или раскаленный снаряд? Решать вам... Увеличивает силу на <%= str %>. Ограниченный выпуск зимы 2015-2016.",
@@ -197,7 +197,7 @@
"weaponSpecialSummer2016RogueText": "Электрический стержень",
"weaponSpecialSummer2016RogueNotes": "Тот, кто сражается с вами, получит шокирующий сюрприз... Увеличивает силу на <%= str %>. Ограниченный выпуск лета 2016.",
"weaponSpecialSummer2016WarriorText": "Меч с крюками",
- "weaponSpecialSummer2016WarriorNotes": "Разрубите трудные задачи этим крючковатым мечем! Увеличивает силу на <%= str %>. Ограниченный выпуск лета 2016.",
+ "weaponSpecialSummer2016WarriorNotes": "Разрубите трудные задачи этим крючковатым мечем! Увеличивает силу на <%= str %>. Ограниченный выпуск лета 2016.",
"weaponSpecialSummer2016MageText": "Посох морской пены",
"weaponSpecialSummer2016MageNotes": "Вся сила морских фильтров протекает в этом посохе. Увеличивает интеллект на <%= int %> и восприятие на <%= per %>. Ограниченный выпуск лета 2016.",
"weaponSpecialSummer2016HealerText": "Целительный трезубец",
@@ -207,7 +207,7 @@
"weaponSpecialFall2016WarriorText": "Корни-убийцы",
"weaponSpecialFall2016WarriorNotes": "Расправьтесь с задачами извивающимися корнями! Увеличивают силу на <%= str %>. Ограниченный выпуск осени 2016.",
"weaponSpecialFall2016MageText": "Зловещий шар",
- "weaponSpecialFall2016MageNotes": "Не стоит просить предсказаний у этого шара... Увеличивает интеллект на <%= int %> и восприятие на <%= per %>. Ограниченный выпуск лета 2016.",
+ "weaponSpecialFall2016MageNotes": "Не стоит просить предсказаний у этого шара... Увеличивает интеллект на <%= int %> и восприятие на <%= per %>. Ограниченный выпуск осени 2016.",
"weaponSpecialFall2016HealerText": "Ядовитый змей",
"weaponSpecialFall2016HealerNotes": "Один укус отравляет, другой лечит. Увеличивает интеллект на <%= int %>. Ограниченный выпуск осени 2016.",
"weaponSpecialWinter2017RogueText": "Ледяной топор",
@@ -271,7 +271,7 @@
"weaponSpecialFall2018WarriorText": "Кнут Минотавра",
"weaponSpecialFall2018WarriorNotes": "Не такой длинный, чтобы, распутываясь, провести вас через весь лабиринт. Ну, может, если только лабиринт очень маленький. Увеличивает силу на <%= str %>. Ограниченый выпуск осени 2018.",
"weaponSpecialFall2018MageText": "Сладостный посох",
- "weaponSpecialFall2018MageNotes": "Это не простой леденец! Сияющая сфера из магического сахара на верхушке посоха заставит хорошие привычки к вам буквально прилипнуть. Увеличивает интеллект на<%= int %> и восприятие на <%= per %>. Ограниченный выпуск осени 2018.",
+ "weaponSpecialFall2018MageNotes": "Это не простой леденец! Сияющая сфера из магического сахара на верхушке посоха заставит хорошие привычки к вам буквально прилипнуть. Увеличивает интеллект на <%= int %> и восприятие на <%= per %>. Ограниченный выпуск осени 2018.",
"weaponSpecialFall2018HealerText": "Проголодавшийся посох",
"weaponSpecialFall2018HealerNotes": "Просто следите, чтобы этот посох был сыт, и он будет одаривать вас Благословениями. Если же вы забыли его покормить, что ж, лучше держите пальцы подальше. Увеличивает интеллект на<%= int %>. Ограниченный выпуск осени 2018.",
"weaponSpecialWinter2019RogueText": "Букет из пуансеттии",
@@ -462,18 +462,18 @@
"armorSpecialCandycaneNotes": "Спрядена из сахара и шелка. Увеличивает интеллект на <%= int %>. Ограниченный выпуск зимы 2013-2014.",
"armorSpecialSnowflakeText": "Мантия «Снежинка»",
"armorSpecialSnowflakeNotes": "Эта мантия согреет вас даже в пургу и метель. Увеличивает телосложение на <%= con %>. Ограниченный выпуск зимы 2013-2014.",
- "armorSpecialBirthdayText": "Мантия праздника абсурда",
+ "armorSpecialBirthdayText": "Абсурдная праздничная мантия",
"armorSpecialBirthdayNotes": "С днём рождения, Habitica! Примерьте эти нелепые наряды, чтобы отпраздновать этот замечательный день. Бонусов не дает.",
- "armorSpecialBirthday2015Text": "Глупая Праздничная Мантия",
+ "armorSpecialBirthday2015Text": "Глупая праздничная мантия",
"armorSpecialBirthday2015Notes": "С днём рождения, Habitica! Примерьте эти нелепые наряды, чтобы отпраздновать этот замечательный день. Бонусов не дает.",
"armorSpecialBirthday2016Text": "Смешная праздничная мантия",
"armorSpecialBirthday2016Notes": "С днем рождения, Habitica! Носите эту Смешную праздничную мантию, чтобы отметить этот чудесный день. Бонусов не дает.",
- "armorSpecialBirthday2017Text": "Причудливая Праздничная Мантия",
+ "armorSpecialBirthday2017Text": "Причудливая праздничная мантия",
"armorSpecialBirthday2017Notes": "С днем рождения, Habitica! Носите эту причудливую праздничную мантию, чтобы отметить этот чудесный день. Бонусов не дает.",
- "armorSpecialBirthday2018Text": "Причудливая праздничная мантия",
+ "armorSpecialBirthday2018Text": "Странная праздничная мантия",
"armorSpecialBirthday2018Notes": "С днем рождения, Habitica! Носите эту Странную праздничную мантию, чтобы отметить этот чудесный день. Бонусов не дает.",
- "armorSpecialBirthday2019Text": "Outlandish Party Robes",
- "armorSpecialBirthday2019Notes": "Happy Birthday, Habitica! Wear these Outlandish Party Robes to celebrate this wonderful day. Confers no benefit.",
+ "armorSpecialBirthday2019Text": "Диковинная праздничная мантия",
+ "armorSpecialBirthday2019Notes": "С днем рождения, Habitica! Носите эту Диковинную праздничную мантию, чтобы отметить этот чудесный день. Бонусов не дает.",
"armorSpecialGaymerxText": "Доспехи радужного воина",
"armorSpecialGaymerxNotes": "В честь Конференции GaymerX эти особые доспехи выкрашены в яркие радужные цвета! GaymerX это интернациональная игровая конвенция, поддерживающая ЛГБТ+ сообщества и видео игры. Она открыта каждому!",
"armorSpecialSpringRogueText": "Блестящий кошачий костюм",
@@ -485,9 +485,9 @@
"armorSpecialSpringHealerText": "Облачение пушистого щенка",
"armorSpecialSpringHealerNotes": "Теплый и уютный, но способен защитить владельца от урона. Увеличивает телосложение на <%= con %>. Экипировка ограниченного выпуска весны 2014.",
"armorSpecialSummerRogueText": "Пиратская роба",
- "armorSpecialSummerRogueNotes": "В этой удобной робе можно спрятать не одну бутылку рома, аррр! Увеличивает восприятие на <%= per %>. Ограниченный выпуск весны 2014.",
+ "armorSpecialSummerRogueNotes": "В этой удобной робе можно спрятать не одну бутылку рома, ар-р-р! Увеличивает восприятие на <%= per %>. Ограниченный выпуск весны 2014.",
"armorSpecialSummerWarriorText": "Роба головореза",
- "armorSpecialSummerWarriorNotes": "Изящная резьба выглядит эффектно, особенно на чьих-то головах. Добавляет <%= con %> очков к телосложению. Ограниченный выпуск лета 2014.",
+ "armorSpecialSummerWarriorNotes": "Изящная резьба выглядит эффектно, особенно на чьих-то головах. Увеличивает телосложение на <%= con %>. Ограниченный выпуск лета 2014.",
"armorSpecialSummerMageText": "Изумрудный хвост",
"armorSpecialSummerMageNotes": "Это одеяние из сияющих чешуек наделяет его владельца настоящей магией русалок! Увеличивает интеллект на <%= int %>. Ограниченный выпуск лета 2014.",
"armorSpecialSummerHealerText": "Хвост морского целителя",
@@ -529,8 +529,8 @@
"armorSpecialFall2015WarriorText": "Доспех пугала",
"armorSpecialFall2015WarriorNotes": "Эта броня, несмотря на то, что набита соломой, крайне тяжела! Увеличивает телосложение на <%= con %>. Ограниченный выпуск осени 2015.",
"armorSpecialFall2015MageText": "Клочковатая роба",
- "armorSpecialFall2015MageNotes": "Каждый шов в этих доспехах мерцает волшебством. Увеличивает интеллект на <%= int %>. Ограниченный выпуск осени 2015.",
- "armorSpecialFall2015HealerText": "Облачение знахаря",
+ "armorSpecialFall2015MageNotes": "Каждый шов в этих доспехах мерцает волшебством. Увеличивает интеллект на <%= int %>. Ограниченный выпуск осени 2015.",
+ "armorSpecialFall2015HealerText": "Облачение зельевара",
"armorSpecialFall2015HealerNotes": "Что? Конечно, это был эликсир телосложения. Нет, ты совершенно точно НЕ превращаешься в жабу! Не квакай больше. Увеличивает телосложение на <%= con %>. Ограниченный выпуск осени 2015.",
"armorSpecialWinter2016RogueText": "Какао-доспехи",
"armorSpecialWinter2016RogueNotes": "Эта какао-броня сохраняет тепло и уют. Она на самом деле сделана из какао? Кто знает... Увеличивает восприятие на <%= per %>. Ограниченный выпуск зимы 2015-2016.",
@@ -561,7 +561,7 @@
"armorSpecialFall2016WarriorText": "Слизистый доспех",
"armorSpecialFall2016WarriorNotes": "Влажный и липкий на ощупь! Увеличивает телосложение на <%= con %>. Ограниченный выпуск осени 2016.",
"armorSpecialFall2016MageText": "Накидка злорадности",
- "armorSpecialFall2016MageNotes": "Одевая накидку, слышно злорадный смех. Увеличивает интеллект на <%= int %>. Ограниченный выпуск осени 2016.",
+ "armorSpecialFall2016MageNotes": "Одевая накидку, вы услышите злорадный смех. Увеличивает интеллект на <%= int %>. Ограниченный выпуск осени 2016.",
"armorSpecialFall2016HealerText": "Одеяние горгоны",
"armorSpecialFall2016HealerNotes": "Оно полностью из камня. Почему оно такое удобное? Увеличивает телосложение на <%= con %>. Ограниченный выпуск осени 2016.",
"armorSpecialWinter2017RogueText": "Ледяные доспехи",
@@ -589,7 +589,7 @@
"armorSpecialSummer2017HealerText": "Серебряный морской хвост",
"armorSpecialSummer2017HealerNotes": "Эта одежда из серебряных чешуек превращает своего хозяина в настоящего Морского Целителя! Увеличивает телосложение на <%= con %>. Ограниченный выпуск лета 2017.",
"armorSpecialFall2017RogueText": "Тыквенная мантия",
- "armorSpecialFall2017RogueNotes": "Нужно спрятаться? Ползите у Джеков-Светильников и эта мантия вас скроет! Увеличивает восприятие на <%= per %>. Ограниченный выпуск осени 2017.",
+ "armorSpecialFall2017RogueNotes": "Нужно спрятаться? Проползите среди светильников Джека и эта мантия скроет вас! Увеличивает восприятие на <%= per %>. Ограниченный выпуск осени 2017.",
"armorSpecialFall2017WarriorText": "Прочные сладкие доспехи",
"armorSpecialFall2017WarriorNotes": "Эти доспехи защитят вас как вкусная корочка конфеты. Увеличивают телосложение на <%= con %>. Ограниченный выпуск осени 2017.",
"armorSpecialFall2017MageText": "Маскарадная мантия",
@@ -620,7 +620,7 @@
"armorSpecialSummer2018MageNotes": "Магия ядов относится к навыкам хитрости. Но броня ярких оттенков, давая ясно понять хищнику: я ядовитый! Увеличивает интеллект на <%= int %>. Ограниченный выпуск лета 2018.",
"armorSpecialSummer2018HealerText": "Роба амфибии",
"armorSpecialSummer2018HealerNotes": "Эти лазурные облачения приоткрывают тайну, что у вас есть ноги для ходьбы по суше. Ну... Даже монарх не настолько идеальный. Увеличивает телосложение на <%= con %>. Ограниченный выпуск лета 2018.",
- "armorSpecialFall2018RogueText": "Сюртук Альтер-эго",
+ "armorSpecialFall2018RogueText": "Сюртук альтер эго",
"armorSpecialFall2018RogueNotes": "Днем выглядит стильно. Ночью - дарует комфорт и защиту. Увеличивает восприятие на <%= per %>. Ограниченный выпуск осени 2018.",
"armorSpecialFall2018WarriorText": "Броня Минотавра",
"armorSpecialFall2018WarriorNotes": "И в завершение - копыта, чтобы отбивать успокаивающий ритм, пока вы блуждаете по медитативному лабиринту. Увеличивает телосложение на <%= con %>. Ограниченый выпуск осени 2018.",
@@ -898,8 +898,8 @@
"headSpecialTurkeyHelmBaseNotes": "Ваш костюм на день благодарения будет закончен с этим шлемом с клювом.",
"headSpecialTurkeyHelmGildedText": "Позолоченный индюшачий шлем",
"headSpecialTurkeyHelmGildedNotes": "Кулдык-кулдык! Драгоценности! Бонусов не дает.",
- "headSpecialNyeText": "Шляпа праздника абсурда",
- "headSpecialNyeNotes": "Вы получили Шляпу Праздника Абсурда! Носите ее с гордостью в Новый год! Бонусов не дает.",
+ "headSpecialNyeText": "Абсурдная праздничная шляпа",
+ "headSpecialNyeNotes": "Вы получили Абсурдную праздничную шляпу! Носите ее с гордостью в Новый год! Бонусов не дает.",
"headSpecialYetiText": "Шлем укротителя Йети",
"headSpecialYetiNotes": "Восхитительно страшная шляпа. Увеличивает силу на <%= str %>. Ограниченный выпуск зимы 2013-2014.",
"headSpecialSkiText": "Шлем лыжника-ассасина",
@@ -960,12 +960,12 @@
"headSpecialSummer2015HealerNotes": "Надев эту шляпу вы не пропадете даже в самый страшный шторм. Увеличивает интеллект на <%= int %>. Ограниченный выпуск лета 2015.",
"headSpecialFall2015RogueText": "Крылья мышехвоста",
"headSpecialFall2015RogueNotes": "Ваши враги будут найдены с помощью этой мощной шляпы-эхолота! Увеличивает восприятие на <%= per %>. Ограниченный выпуск осени 2015.",
- "headSpecialFall2015WarriorText": "Шапка пугала",
+ "headSpecialFall2015WarriorText": "Шляпа пугала",
"headSpecialFall2015WarriorNotes": "Все бы хотели эту шляпу... Если бы у них были мозги. Увеличивает силу на <%= str %>. Ограниченный выпуск осени 2015.",
"headSpecialFall2015MageText": "Клочковатая шапка",
"headSpecialFall2015MageNotes": "Каждый шов в этой шляпе прибавляет ей мощи. Увеличивает восприятие на <%= per %>. Ограниченный выпуск осени 2015.",
"headSpecialFall2015HealerText": "Шапка лягухи",
- "headSpecialFall2015HealerNotes": "Это чрезвычайно серьезная шляпа, которая достойна только самых передовых зельеваров. Увеличивает интеллект на <%= int %>. Ограниченный выпуск осени 2015.",
+ "headSpecialFall2015HealerNotes": "Это чрезвычайно серьезная шапка, которая достойна только самых передовых зельеваров. Увеличивает интеллект на <%= int %>. Ограниченный выпуск осени 2015.",
"headSpecialNye2015Text": "Смешная шляпа для вечеринок",
"headSpecialNye2015Notes": "Вы получили Смешную Праздничную Шляпу! Носите её с гордостью в Новый Год! Бонусов не дает.",
"headSpecialWinter2016RogueText": "Какао-шлем",
@@ -991,14 +991,14 @@
"headSpecialSummer2016MageText": "Шляпа-водомёт",
"headSpecialSummer2016MageNotes": "Волшебная вода все время струится из этой шляпы. Улучшает восприятие на <%= per %>. Ограниченный выпуск лета 2016.",
"headSpecialSummer2016HealerText": "Шлем морского конька",
- "headSpecialSummer2016HealerNotes": "Шлем демонстрирует, что владелец обучался у волшебных морских коньков-целителей в Медлительске. Увеличивает интеллект на <%= int %>. Ограниченный выпуск лета 2016.",
+ "headSpecialSummer2016HealerNotes": "Шлем демонстрирует, что владелец обучался у волшебных морских коньков-целителей в Промедлении. Увеличивает интеллект на <%= int %>. Ограниченный выпуск лета 2016.",
"headSpecialFall2016RogueText": "Шлем черной вдовы",
"headSpecialFall2016RogueNotes": "Лапки на шлеме непрерывно подергиваются. Улучшает восприятие на <%= per %>. Ограниченный выпуск осени 2016.",
"headSpecialFall2016WarriorText": "Шлем из корявой коры",
"headSpecialFall2016WarriorNotes": "Шлем покрыт тиной и кусочками мха. Увеличивает силу на <%= str %>. Ограниченный выпуск осени 2016.",
"headSpecialFall2016MageText": "Капюшон злорадности",
"headSpecialFall2016MageNotes": "Плети интриги под этим темным капюшоном. Увеличивает восприятие на <%= per %>. Ограниченный выпуск осени 2016.",
- "headSpecialFall2016HealerText": "Корона медузы",
+ "headSpecialFall2016HealerText": "Корона Медузы",
"headSpecialFall2016HealerNotes": "Горе посмотревшему в ваши очи... Увеличивает интеллект на <%= int %>. Ограниченный выпуск осени 2016.",
"headSpecialNye2016Text": "Причудливая шляпа для вечеринок",
"headSpecialNye2016Notes": "Вы получили причудливую праздничную шляпу! Носите её с гордостью в Новый Год! Бонусов не дает.",
@@ -1060,8 +1060,8 @@
"headSpecialSummer2018MageNotes": "Свирепо бросьте взгляд на всякого, кто посмеет сказать, что вы похожи на «вкусную рыбу». Увеличивает восприятие на <%= per %>. Ограниченный выпуск лета 2018.",
"headSpecialSummer2018HealerText": "Корона амфибии",
"headSpecialSummer2018HealerNotes": "Эта украшенная аквамарином диадема с плавниками выделяет лидерство народа, рыб, и тех, кто отчасти относится к обеим группам! Увеличивает интеллект на <%= int %>. Ограниченный выпуск лета 2018.",
- "headSpecialFall2018RogueText": "Лицо Альтер-эго",
- "headSpecialFall2018RogueNotes": "Большинство из нас скрывают свои переживания внутри. Но эта маска наглядно показывает, что у всех нас есть как положительные, так и отрицательные порывы. А в добавок к ней идет отличная шляпа! Увеличивает восприятие на <%= per %>. Ограниченый выпуск осени 2018.",
+ "headSpecialFall2018RogueText": "Лицо альтер эго",
+ "headSpecialFall2018RogueNotes": "Большинство из нас скрывают свои переживания внутри. Но эта маска наглядно показывает, что у всех нас есть как положительные, так и отрицательные порывы. А в добавок к ней идет отличная шляпа! Увеличивает восприятие на <%= per %>. Ограниченный выпуск осени 2018.",
"headSpecialFall2018WarriorText": "Морда Минотавра",
"headSpecialFall2018WarriorNotes": "Эта устрашающая маска показывает, что вы можете взять свои дела за рога! Увеличивает силу на <%= str %>. Ограниченный выпуск осени 2018.",
"headSpecialFall2018MageText": "Шляпа сладоманта",
@@ -1160,7 +1160,7 @@
"headMystery201810Notes": "Если вы обнаружите, что путешествуете по пугающему месту, то светящиеся красные глаза этой маски наверняка отпугнут всех врагов на вашем пути. Бонусов не дает. Подарок подписчикам октября 2018.",
"headMystery201811Text": "Шляпа Великолепного чародея",
"headMystery201811Notes": "Носите эту шляпу с пером, чтобы выделяться даже на самых странных колдовских собраниях! Бонусов не дает. Подарок подписчикам ноября 2018.",
- "headMystery201901Text": "Polaris Helm",
+ "headMystery201901Text": "Шлем полярной звезды",
"headMystery201901Notes": "The glowing gems on this helm contain light magically captured from winter auroras. Confers no benefit. January 2019 Subscriber Item.",
"headMystery301404Text": "Модный цилиндр",
"headMystery301404Notes": "Модный цилиндр для самых уважаемых господ! Подарок подписчикам января 3015. Бонусов не дает.",
@@ -1329,7 +1329,7 @@
"shieldSpecialSummerWarriorText": "Деревянный щит",
"shieldSpecialSummerWarriorNotes": "Этот щит из обломков корабельной древесины способен сдержать даже самые бурные задания. Увеличивает телосложение на <%= con %>. Ограниченный выпуск лета 2014.",
"shieldSpecialSummerHealerText": "Щит мелководья",
- "shieldSpecialSummerHealerNotes": "Никто не осмелится атаковать коралловый риф, увидав этот сияющий щит! Увеличивает телосложение на <%= con %>. Ограниченный выпуск лета 2014.",
+ "shieldSpecialSummerHealerNotes": "Никто не осмелится атаковать коралловый риф, увидав этот сияющий щит! Увеличивает телосложение на <%= con %>. Ограниченный выпуск лета 2014.",
"shieldSpecialFallRogueText": "Серебряная Конфета",
"shieldSpecialFallRogueNotes": "Убивает нежить. Также дает преимущество против оборотней, ведь осторожность никогда не помешает. Увеличивает силу на <%= str %>. Ограниченный выпуск осени 2014.",
"shieldSpecialFallWarriorText": "Мощный эликсир науки",
@@ -1359,7 +1359,7 @@
"shieldSpecialFall2015WarriorText": "Сумка птичьих семян",
"shieldSpecialFall2015WarriorNotes": "Это, конечно, правда, что вы должны быть ПУГАЛОМ и пугать ворон, но нет ничего плохого в том, чтобы с ними подружиться! Увеличивает телосложение на <%= con %>. Ограниченный выпуск осени 2015.",
"shieldSpecialFall2015HealerText": "Палочка для помешивания",
- "shieldSpecialFall2015HealerNotes": "Эта палка может помешивать всё без таяния, растворения, или воспламенения! Она может быть также использована, чтобы яростно тыкать в враждебные задачи. Увеличивает телосложение на <%= con %>. Ограниченный выпуск осени 2015.",
+ "shieldSpecialFall2015HealerNotes": "Эта палочка может помешивать всё без таяния, растворения, или воспламенения! Она может быть также использована, чтобы яростно тыкать в враждебные задачи. Увеличивает телосложение на <%= con %>. Ограниченный выпуск осени 2015.",
"shieldSpecialWinter2016RogueText": "Кружка какао",
"shieldSpecialWinter2016RogueNotes": "Согревающее питье или раскаленный снаряд? Решать тебе... Увеличивает силу на <%= str %>. Ограниченный выпуск зимы 2015-2016.",
"shieldSpecialWinter2016WarriorText": "Щит \"Сани\"",
@@ -1375,13 +1375,13 @@
"shieldSpecialSummer2016RogueText": "Электрический стержень",
"shieldSpecialSummer2016RogueNotes": "Тот, кто сражается с вами получит шокирующий сюрприз... Увеличивает силу на <%= str %>. Ограниченный выпуск лета 2016.",
"shieldSpecialSummer2016WarriorText": "Зуб акулы",
- "shieldSpecialSummer2016WarriorNotes": "Укусите трудные дела зубастым щитом! Усиливает телосложение на <%= con %>. Ограниченный выпуск лета 2016.",
+ "shieldSpecialSummer2016WarriorNotes": "Укусите трудные дела зубастым щитом! Увеличивает телосложение на <%= con %>. Ограниченный выпуск лета 2016.",
"shieldSpecialSummer2016HealerText": "Щит морской звезды",
"shieldSpecialSummer2016HealerNotes": "Иногда ошибочно называется Щитом Звёздной рыбы. Увеличивает телосложение на <%= con %>. Ограниченный выпуск лета 2016.",
"shieldSpecialFall2016RogueText": "Кинжал паучьего укуса",
"shieldSpecialFall2016RogueNotes": "Ощути остроту паучьего жала! Увеличивает силу на <%= str %>. Ограниченный выпуск осени 2016.",
"shieldSpecialFall2016WarriorText": "Корни-защитники",
- "shieldSpecialFall2016WarriorNotes": "Защищайтесь от ежедневных задач корчащимися корнями! Увеличивает телосложение на <%= con %>. Ограниченный выпуск осени 2016.",
+ "shieldSpecialFall2016WarriorNotes": "Защищайтесь от ежедневных задач корчащимися корнями! Увеличивает телосложение на <%= con %>. Ограниченный выпуск осени 2016.",
"shieldSpecialFall2016HealerText": "Щит горгоны",
"shieldSpecialFall2016HealerNotes": "Ваше отражение прекрасно, спору нет. Увеличивает телосложение на <%= con %>. Ограниченный выпуск осени 2016.",
"shieldSpecialWinter2017RogueText": "Ледяной топор",
@@ -1423,7 +1423,7 @@
"shieldSpecialSummer2018HealerText": "Герб амфибии",
"shieldSpecialSummer2018HealerNotes": "Этот щит может создать воздушный купол для удобства проживающих на суше посетителей вашего водяного царства. Увеличивает телосложение на <%= con %>. Ограниченный выпуск лета 2018.",
"shieldSpecialFall2018RogueText": "Флакон искушения",
- "shieldSpecialFall2018RogueNotes": "Эта бутылочка олицетворяет все проблемы и отвлекающие факторы, которые не дают вам быть лучшей версией себя. Держитесь! Мы верим в вас! Увеличивает силу на <%= str %>. Ограниченый выпуск осени 2018.",
+ "shieldSpecialFall2018RogueNotes": "Эта бутылочка олицетворяет все проблемы и отвлекающие факторы, которые не дают вам быть лучшей версией себя. Держитесь! Мы верим в вас! Увеличивает силу на <%= str %>. Ограниченный выпуск осени 2018.",
"shieldSpecialFall2018WarriorText": "Бриллиантовый щит",
"shieldSpecialFall2018WarriorNotes": "Отражающая поверхность заставит надоедливых Горгон дважды подумать, стоит ли неожиданно выпрыгивать на вас из-за угла! Увеличивает телосложение на <%= con %>. Ограниченый выпуск осени 2018.",
"shieldSpecialFall2018HealerText": "Голодный щит",
@@ -1588,7 +1588,7 @@
"bodySpecialAetherAmuletText": "Амулет эфира",
"bodySpecialAetherAmuletNotes": "Этот амулет имеет таинственную историю. Увеличивает телосложение и силу на <%= attrs %>.",
"bodySpecialSummerMageText": "Сияющее пончо",
- "bodySpecialSummerMageNotes": "Ни соленая, ни свежая вода не лишат блеска эту металическую пелерину. Бонусов не дает. Ограниченный выпуск лета 2014.",
+ "bodySpecialSummerMageNotes": "Ни соленая, ни свежая вода не лишат блеска эту металлическую пелерину. Бонусов не дает. Ограниченный выпуск лета 2014.",
"bodySpecialSummerHealerText": "Коралловое ожерелье",
"bodySpecialSummerHealerNotes": "Стильное ожерелье из настоящих кораллов. Бонусов не дает. Ограниченный выпуск лета 2014.",
"bodySpecialSummer2015RogueText": "Пояс Ренегата",
@@ -1607,7 +1607,7 @@
"bodyMystery201706Notes": "В этом плаще есть потайные карманы, чтобы прятать золото, награбленное у задач. Бонусов не даёт. Подарок подписчикам июня 2017.",
"bodyMystery201711Text": "Шарф коврового наездника",
"bodyMystery201711Notes": "Этот мягко связанный шарф выглядит довольно величественно, развеваясь на ветру. Бонусов не дает. Подарок подписчикам ноября 2017.",
- "bodyMystery201901Text": "Polaris Pauldrons",
+ "bodyMystery201901Text": "Наплечники полярной звезды",
"bodyMystery201901Notes": "These shimmering pauldrons are strong, but will rest on your shoulders as weightlessly as a ray of dancing light. Confers no benefit. January 2019 Subscriber Item.",
"bodyArmoireCozyScarfText": "Уютный шарф",
"bodyArmoireCozyScarfNotes": "Этот славный шарф сохранит вас в тепле, пока вы будете заниматься своими застывшими делами. Увеличивает телосложение и восприятие на <%= attrs %>. Зачарованный сундук: Набор фонарщика (предмет 4 из 4).",
@@ -1747,5 +1747,10 @@
"eyewearArmoirePlagueDoctorMaskNotes": "Такие маски носили доктора, боровшиеся с Чумой Прокрастинации. Увеличивает телосложение и интеллект на<%= attrs %>. Зачарованный сундук: Набор Чумного доктора (предмет 2 из 3).",
"eyewearArmoireGoofyGlassesText": "Глупые очки",
"eyewearArmoireGoofyGlassesNotes": "Идеально подходит, чтоб стать инкогнито или просто для того, чтобы ваши товарищи хихикали. Увеличивает восприятие на <%= per %>. Зачарованный Сундук: Независимый предмет.",
- "twoHandedItem": "Двуручник"
-}
\ No newline at end of file
+ "twoHandedItem": "Двуручник",
+ "weaponArmoireChefsSpoonText": "Ложка шеф-повара",
+ "armorArmoireChefsJacketText": "Фартук шеф-повара",
+ "headArmoireToqueBlancheText": "Тока шеф-повара",
+ "headArmoireToqueBlancheNotes": "Согласно легенде, количество сгибов в этом колпаке показывает сколько способов вы знаете для приготовления яичницы! Верно? Увеличивает восприятие на <%= per %>. Зачарованный сундук: Набор шеф-повара (предмет 1 из 4). ",
+ "shieldArmoireMightyPizzaText": "Могучая пицца"
+}
diff --git a/website/common/locales/ru/limited.json b/website/common/locales/ru/limited.json
index e47314e193..20a28f1451 100644
--- a/website/common/locales/ru/limited.json
+++ b/website/common/locales/ru/limited.json
@@ -47,7 +47,7 @@
"gingerbreadSet": "Пряничный воин (Воин)",
"snowDaySet": "Боец снежного дня (Воин)",
"snowboardingSet": "Сноуборд-Колдун (Маг)",
- "festiveFairySet": "Праздничная фея (Лекарь)",
+ "festiveFairySet": "Праздничная фея (Целитель)",
"cocoaSet": "Какао-разбойник (Разбойник)",
"toAndFromCard": "Для <%= toName %>, от <%= fromName %>",
"nyeCard": "Новогодняя открытка",
@@ -81,10 +81,10 @@
"sunfishWarriorSet": "Луна-рыба-воин (Воин)",
"shipSoothsayerSet": "Корабельный предсказатель (Маг)",
"strappingSailorSet": "Здоровый матрос (Целитель)",
- "reefRenegadeSet": "Предатель Рифа (Разбойник)",
+ "reefRenegadeSet": "Рифовый Ренегат (Разбойник)",
"scarecrowWarriorSet": "Боевое пугало (Воин)",
"stitchWitchSet": "Лоскутная колдунья (Маг)",
- "potionerSet": "Зельевар (Лекарь)",
+ "potionerSet": "Зельевар (Целитель)",
"battleRogueSet": "Разбойник-мышехвост (Разбойник) ",
"springingBunnySet": "Скачущий Зайчик (Целитель)",
"grandMalkinSet": "Великий Малкин (Маг)",
@@ -94,9 +94,9 @@
"summer2016DolphinMageSet": "Маг-дельфин (Маг)",
"summer2016SeahorseHealerSet": "Целитель - морской конёк (Целитель)",
"summer2016EelSet": "Разбойник-угорь (Разбойник)",
- "fall2016SwampThingSet": "Болотная штука (Воин)",
- "fall2016WickedSorcererSet": "Злой колдун (маг)",
- "fall2016GorgonHealerSet": "Горгон Целитель (Целитель)",
+ "fall2016SwampThingSet": "Болотная тварь (Воин)",
+ "fall2016WickedSorcererSet": "Злой колдун (Маг)",
+ "fall2016GorgonHealerSet": "Горгона-целитель (Целитель)",
"fall2016BlackWidowSet": "Черная вдова-разбойница (Разбойник)",
"winter2017IceHockeySet": "Хоккейная клюшка (воин)",
"winter2017WinterWolfSet": "Зимний волк (Маг)",
@@ -152,4 +152,4 @@
"discountBundle": "комплект",
"g1g1Announcement": "Подарите подписку и получите такую же бесплатно!",
"g1g1Details": "Подарите подписку другу из своего профиля и получите такую же бесплатно!"
-}
\ No newline at end of file
+}
diff --git a/website/common/locales/ru/loadingscreentips.json b/website/common/locales/ru/loadingscreentips.json
index fd3ad3fe64..7f946194f0 100644
--- a/website/common/locales/ru/loadingscreentips.json
+++ b/website/common/locales/ru/loadingscreentips.json
@@ -1,38 +1,38 @@
{
- "tipTitle": "Совет #<%= tipNumber %>",
- "tip1": "Отмечайте задания на ходу с помощью мобильных приложений Habitica.",
- "tip2": "Нажмите на любое снаряжение, чтобы предварительно осмотреть его, или сразу наденьте его, нажав на звезду в левом верхнем углу!",
- "tip3": "Используйте эмодзи, чтобы быстро различать задания.",
- "tip4": "Используйте символ # перед названием задания, чтобы сделать его очень большим!",
- "tip5": "Лучше всего использовать вызывающие баффы навыки с утра пораньше, чтобы они длились дольше.",
- "tip6": "Наведите мышь на задачу и нажмите на точки, чтобы получить доступ к расширенным элементам управления, таким как возможность отправить задачу в верхнюю или нижнюю часть списка.",
- "tip7": "Некоторые фоны идеально соединяются друг с другом, если члены команды используют одно и то же изображение. Например: Горное озеро, Пагода или Круглые холмы.",
- "tip8": "Чтобы отправить кому-нибудь сообщение, сначала нажмите на его имя в чате, а затем на значок конверта в верхней части его профиля!",
- "tip9": "Используйте фильтры и панель поиска в Инвентаре, Магазине, Гильдии и Испытаниях чтобы быстро находить что хотите.",
- "tip10": "Вы можете выиграть самоцветы, участвуя в испытаниях. Каждый день появляются новые!",
- "tip11": "Если в команде больше четырех человек, это увеличивает вашу ответственность!",
- "tip12": "Добавляйте списки в задачи для увеличения награды!",
- "tip13": "Нажмите «Теги» на странице задач, чтобы сделать громоздкий список дел очень легким в обращении! ",
- "tip14": "Вы можете добавить заголовки или вдохновляющие цитаты как привычки без (+/-).",
- "tip15": "Завершите квестовые линии ордена Мастеров, чтобы узнать о тайных знаниях страны Habitica.",
- "tip16": "Нажмите на ссылку «Анализ данных» внизу для получения ценных сведений о вашем развитии.",
- "tip17": "Используйте мобильное приложение, чтобы устанавливать напоминания на ваши задачи.",
- "tip18": "Только полезные или вредные привычки со временем «меркнут» и снова становятся желтыми.",
- "tip19": "Повышайте Интеллект, чтобы получать больше опыта при выполнении заданий.",
- "tip20": "Повышайте Восприятие, чтобы получить больше добычи и золота.",
- "tip21": "Повышайте свою Силу, чтобы наносить больше урона боссам или делать критические удары.",
- "tip22": "Повышайте Телосложение, чтобы невыполненные ежедневные задания наносили меньше урона.",
- "tip23": "Достигните 100 уровня, чтобы получить Шар возрождения и начать новое приключение!",
- "tip24": "Есть вопрос? Спросите в Гильдии Помощи страны Habitica!",
- "tip25": "Начало четырех сезонных Больших праздников приходится на солнцестояния и равноденствия.",
- "tip26": "Вы можете искать Команду или участников в вашу Команду - в гильдии Party Wanted Guild!",
- "tip27": "Если вы завершили свои дела, но забыли отметить их, то теперь с фичей «задним числом» у вас есть возможность отметить их перед началом вашего нового дня.",
- "tip28": "Установите персональное начало дня в разделе Значок Пользователя > Настройки, чтобы задать, когда ваш день начинается заново.",
- "tip29": "Выполните все ежедневные дела, чтобы получить баф «Прекрасный день», повышающий ваши характеристики!",
- "tip30": "Вы можете приглашать людей в гильдии так же, как в команды.",
- "tip31": "Посмотрите примеры заданий в гильдии Library of Tasks and Challenges.",
- "tip32": "Знаете ли вы, что значительная часть кода, текстов и картинок для Habitica была сделана самими участниками! Чтобы оказать участие, обращайтесь в гильдию Aspiring Legends.",
- "tip33": "Заглядывайте в гильдию The Bulletin Board, чтобы узнавать о новых гильдиях, испытаниях и других, созданных игроками, событиях - или даже анонсировать свое собственное!",
- "tip34": "Иногда пересматривайте свои задачи, чтобы удостовериться в их актуальности!",
- "tip35": "Пользователи, входящие в групповой план, получают возможность назначать задачи другим пользователям в этой группе для управления дополнительными задачами и возможности учета."
+ "tipTitle": "Совет #<%= tipNumber %>",
+ "tip1": "Отмечайте задания на ходу с помощью мобильных приложений Habitica.",
+ "tip2": "Нажмите на любое снаряжение, чтобы предварительно осмотреть его, или сразу наденьте его, нажав на звезду в левом верхнем углу!",
+ "tip3": "Используйте эмодзи, чтобы быстро различать задания.",
+ "tip4": "Используйте символ # перед названием задания, чтобы сделать его очень большим!",
+ "tip5": "Лучше всего использовать вызывающие баффы навыки с утра пораньше, чтобы они длились дольше.",
+ "tip6": "Наведите мышь на задачу и нажмите на точки, чтобы получить доступ к расширенным элементам управления, таким как возможность отправить задачу в верхнюю или нижнюю часть списка.",
+ "tip7": "Некоторые фоны идеально соединяются друг с другом, если члены команды используют одно и то же изображение. Например: Горное озеро, Пагода или Круглые холмы.",
+ "tip8": "Чтобы отправить кому-нибудь сообщение, сначала нажмите на его имя в чате, а затем на значок конверта в верхней части его профиля!",
+ "tip9": "Используйте фильтры и панель поиска в Инвентаре, Магазине, Гильдии и Испытаниях чтобы быстро находить что хотите.",
+ "tip10": "Вы можете выиграть самоцветы, участвуя в испытаниях. Каждый день появляются новые!",
+ "tip11": "Если в команде больше четырех человек, это увеличивает вашу ответственность!",
+ "tip12": "Добавляйте списки в задачи для увеличения награды!",
+ "tip13": "Нажмите «Теги» на странице задач, чтобы сделать громоздкий список дел очень легким в обращении!",
+ "tip14": "Вы можете добавить заголовки или вдохновляющие цитаты как привычки без (+/-).",
+ "tip15": "Завершите квестовые линии ордена Мастеров, чтобы узнать о тайных знаниях страны Habitica.",
+ "tip16": "Нажмите на ссылку «Анализ данных» внизу для получения ценных сведений о вашем развитии.",
+ "tip17": "Используйте мобильное приложение, чтобы устанавливать напоминания на ваши задачи.",
+ "tip18": "Только полезные или вредные привычки со временем «меркнут» и снова становятся желтыми.",
+ "tip19": "Повышайте Интеллект, чтобы получать больше опыта при выполнении заданий.",
+ "tip20": "Повышайте Восприятие, чтобы получить больше добычи и золота.",
+ "tip21": "Повышайте свою Силу, чтобы наносить больше урона боссам или делать критические удары.",
+ "tip22": "Повышайте Телосложение, чтобы невыполненные ежедневные задания наносили меньше урона.",
+ "tip23": "Достигните 100 уровня, чтобы получить Шар возрождения и начать новое приключение!",
+ "tip24": "Есть вопрос? Спросите в Гильдии Помощи страны Habitica!",
+ "tip25": "Начало четырех сезонных Больших праздников приходится на солнцестояния и равноденствия.",
+ "tip26": "Вы можете искать Команду или участников в вашу Команду - в гильдии Party Wanted Guild!",
+ "tip27": "Если вы завершили свои дела, но забыли отметить их, то теперь с фичей «задним числом» у вас есть возможность отметить их перед началом вашего нового дня.",
+ "tip28": "Установите персональное начало дня в разделе Значок Пользователя > Настройки, чтобы задать, когда ваш день начинается заново.",
+ "tip29": "Выполните все ежедневные дела, чтобы получить баф «Прекрасный день», повышающий ваши характеристики!",
+ "tip30": "Вы можете приглашать людей в гильдии так же, как в команды.",
+ "tip31": "Посмотрите примеры заданий в гильдии Library of Tasks and Challenges.",
+ "tip32": "Знаете ли вы, что значительная часть кода, текстов и картинок для Habitica была сделана самими участниками! Чтобы оказать участие, обращайтесь в гильдию Aspiring Legends.",
+ "tip33": "Заглядывайте в гильдию The Bulletin Board, чтобы узнавать о новых гильдиях, испытаниях и других, созданных игроками, событиях - или даже анонсировать свое собственное!",
+ "tip34": "Иногда пересматривайте свои задачи, чтобы удостовериться в их актуальности!",
+ "tip35": "Пользователи, входящие в групповой план, получают возможность назначать задачи другим пользователям в этой группе для управления дополнительными задачами и возможности учета."
}
diff --git a/website/common/locales/ru/npc.json b/website/common/locales/ru/npc.json
index bb330dd6ed..794659e67f 100644
--- a/website/common/locales/ru/npc.json
+++ b/website/common/locales/ru/npc.json
@@ -25,7 +25,7 @@
"sleepBullet4": "Ваш урон боссу или количество собранных предметов в квестах не изменятся пока вы не отметите выполненные дела",
"pauseDailies": "Отдохнуть в гостинице",
"unpauseDailies": "Возобновить получение урона",
- "staffAndModerators": "Сотрудники и Модераторы",
+ "staffAndModerators": "Сотрудники и модераторы",
"communityGuidelinesIntro": "Страна Habitica старается создавать приятную атмосферу для пользователей всех возрастов и народов, особенно в общественных местах, таких, как таверна. Если у вас возникнут вопросы, пожалуйста, обратитесь к нашим Правилам сообщества.",
"acceptCommunityGuidelines": "Я согласен следовать Правилам сообщества",
"daniel": "Даниэль",
@@ -55,7 +55,7 @@
"amountGold": "<%= amount %> золота",
"namedHatchingPotion": "<%= type %> инкубационный эликсир",
"buyGems": "Купить самоцветы",
- "purchaseGems": "Приобрести Самоцветы",
+ "purchaseGems": "Приобрести самоцветы",
"items": "Предметы",
"AZ": "А-Я",
"sort": "Сортировать",
@@ -64,7 +64,7 @@
"sortByName": "имени",
"quantity": "количеству",
"cost": "цене",
- "shops": "Магазины",
+ "shops": "Лавки",
"custom": "Сезонные",
"wishlist": "Отложенные",
"wrongItemType": "Тип элемента \"<%= type %>\" недопустим.",
@@ -168,4 +168,4 @@
"welcome5": "Теперь вы можете персонализировать аватар и настроить задачи...",
"imReady": "Войдите в Habitica",
"limitedOffer": "Доступно до <%= date %>"
-}
\ No newline at end of file
+}
diff --git a/website/common/locales/ru/tasks.json b/website/common/locales/ru/tasks.json
index 42a1573cae..5e335bc7e2 100644
--- a/website/common/locales/ru/tasks.json
+++ b/website/common/locales/ru/tasks.json
@@ -117,7 +117,7 @@
"fortifyName": "Эликсир укрепления",
"fortifyPop": "Вернуть все задания в нейтральное состояние (желтый цвет) и восстановить всё здоровье.",
"fortify": "Укрепление",
- "fortifyText": "Укрепление вернет все задания, кроме испытаний, в нейтральное (желтое) состояние, как если бы вы только что добавили их, и восстановит Здоровье до максимума. Это отличная возможность, если все ваши красные задания делают игру слишком тяжелой, или же все ваши голубые задания делают игру слишком легкой. Если сбросить все до начальной стадии и обновиться звучит мотивирующе, то потратье Самоцветы и сделайте передышку.",
+ "fortifyText": "Укрепление вернет все задания, кроме испытаний, в нейтральное (желтое) состояние, как если бы вы только что добавили их, и восстановит Здоровье до максимума. Это отличная возможность, если все ваши красные задания делают игру слишком тяжелой, или же все ваши голубые задания делают игру слишком легкой. Если сбросить все до начальной стадии и обновиться звучит мотивирующе, то потратьте Самоцветы и сделайте передышку.",
"confirmFortify": "Вы уверены?",
"fortifyComplete": "Укрепление завершено!",
"deleteTask": "Удалить это Задание",
@@ -145,11 +145,11 @@
"rewardHelp3": "Здесь появится особое снаряжение во время Мировых Событий.",
"rewardHelp4": "Не бойтесь назначать себе персональные награды! Посмотрите примеры наград здесь .",
"clickForHelp": "Помощь",
- "taskAliasAlreadyUsed": "Псевдоним задания уже используется другим заданием",
+ "taskAliasAlreadyUsed": "Псевдоним задания уже используется другим заданием.",
"taskNotFound": "Задача не найдена.",
"invalidTaskType": "Тип задачи должен быть \"habit\", \"daily\", \"todo\", либо \"reward\".",
"invalidTasksType": "Тип задачи должен быть одним из «Привычки», «Ежедневные задания», «Задачи», «Награды».",
- "invalidTasksTypeExtra": "Тип задачи должен быть одним из «Привычки», «Ежедневные задания», «Задачи», «Награды», «Выполненные задачи»",
+ "invalidTasksTypeExtra": "Тип задачи должен быть одним из «Привычки», «Ежедневные задания», «Задачи», «Награды», «Выполненные задачи».",
"cantDeleteChallengeTasks": "Задание, принадлежащее испытанию не может быть удалено.",
"checklistOnlyDailyTodo": "Списки могут быть только у ежедневных заданий и задач",
"checklistItemNotFound": "Пункт списка с заданным ID не найден.",
@@ -163,7 +163,7 @@
"strengthExample": "Относится к физическим упражнениям и активности",
"intelligenceExample": "Относится к академическим или умственно сложным занятиям",
"perceptionExample": "Относится к работе и финансовым заданиям",
- "constitutionExample": "Относится к здоровью, благополучию и социальным взаимодействиям.",
+ "constitutionExample": "Относится к здоровью, благополучию и социальным взаимодействиям",
"counterPeriod": "Счетчик сбрасывается каждые",
"counterPeriodDay": "День",
"counterPeriodWeek": "Неделя",
@@ -199,7 +199,7 @@
"monthlyRepeatHelpContent": "Это задание будет появляться каждые N месяцев",
"yearlyRepeatHelpContent": "Это задание будет появляться каждые N лет",
"resets": "Сбрасывается",
- "summaryStart": "Повторяется <%= frequency %> каждые <%= everyX %> <%= frequencyPlural %>",
+ "summaryStart": "Повторяется <%= frequency %> каждые <%= everyX %> <%= frequencyPlural %>. ",
"nextDue": "Следующие сроки выполнения",
"checkOffYesterDailies": "Отметьте любые ежедневные задания, которые вы выполнили вчера:",
"yesterDailiesTitle": "Вчера вы оставили некоторые ежедневные задания непроверенными! Хотите ли вы отметить какие-либо из них сейчас?",
@@ -210,4 +210,4 @@
"searchTasks": "Поиск заголовков и описаний...",
"sessionOutdated": "Ваша сессия истекла. Пожалуйста, обновите страницу или синхронизируйтесь.",
"errorTemporaryItem": "Это предмет временный и не может быть закреплен."
-}
\ No newline at end of file
+}
diff --git a/website/common/script/content/appearance/backgrounds.js b/website/common/script/content/appearance/backgrounds.js
index aefdd938db..e1eef9cc7d 100644
--- a/website/common/script/content/appearance/backgrounds.js
+++ b/website/common/script/content/appearance/backgrounds.js
@@ -801,6 +801,20 @@ let backgrounds = {
notes: t('backgroundValentinesDayFeastingHallNotes'),
},
},
+ backgrounds032019: {
+ duck_pond: {
+ text: t('backgroundDuckPondText'),
+ notes: t('backgroundDuckPondNotes'),
+ },
+ field_with_colored_eggs: {
+ text: t('backgroundFieldWithColoredEggsText'),
+ notes: t('backgroundFieldWithColoredEggsNotes'),
+ },
+ flower_market: {
+ text: t('backgroundFlowerMarketText'),
+ notes: t('backgroundFlowerMarketNotes'),
+ },
+ },
incentiveBackgrounds: {
violet: {
text: t('backgroundVioletText'),
diff --git a/website/common/script/content/constants.js b/website/common/script/content/constants.js
index 67f9045ddb..9c0a4fd99b 100644
--- a/website/common/script/content/constants.js
+++ b/website/common/script/content/constants.js
@@ -35,6 +35,7 @@ export const EVENTS = {
summer2018: { start: '2018-06-19', end: '2018-08-02' },
fall2018: { start: '2018-09-20', end: '2018-11-02' },
winter2019: { start: '2018-12-19', end: '2019-02-02' },
+ spring2019: { start: '2019-03-19', end: '2019-04-02' },
};
export const SEASONAL_SETS = {
@@ -105,6 +106,12 @@ export const SEASONAL_SETS = {
'spring2018SunriseWarriorSet',
'spring2018DucklingRogueSet',
'spring2018GarnetHealerSet',
+
+ // spring 2019
+ 'spring2019AmberMageSet',
+ 'spring2019OrchidWarriorSet',
+ 'spring2019CloudRogueSet',
+ 'spring2019RobinHealerSet',
],
summer: [
// summer 2014
diff --git a/website/common/script/content/gear/sets/armoire.js b/website/common/script/content/gear/sets/armoire.js
index a965d0f546..383fb4f6ac 100644
--- a/website/common/script/content/gear/sets/armoire.js
+++ b/website/common/script/content/gear/sets/armoire.js
@@ -444,6 +444,15 @@ let armor = {
set: 'chef',
canOwn: ownsItem('armor_armoire_chefsJacket'),
},
+ vernalVestment: {
+ text: t('armorArmoireVernalVestmentText'),
+ notes: t('armorArmoireVernalVestmentNotes', { attrs: 6 }),
+ value: 100,
+ str: 6,
+ int: 6,
+ set: 'vernalVestments',
+ canOwn: ownsItem('armor_armoire_vernalVestment'),
+ },
};
let body = {
@@ -907,6 +916,14 @@ let head = {
set: 'chef',
canOwn: ownsItem('head_armoire_toqueBlanche'),
},
+ vernalHennin: {
+ text: t('headArmoireVernalHenninText'),
+ notes: t('headArmoireVernalHenninNotes', { per: 12 }),
+ value: 100,
+ per: 12,
+ set: 'vernalVestments',
+ canOwn: ownsItem('head_armoire_vernalHennin'),
+ },
};
let shield = {
@@ -1528,6 +1545,14 @@ let weapon = {
set: 'chef',
canOwn: ownsItem('weapon_armoire_chefsSpoon'),
},
+ vernalTaper: {
+ text: t('weaponArmoireVernalTaperText'),
+ notes: t('weaponArmoireVernalTaperNotes', { con: 8 }),
+ value: 100,
+ con: 8,
+ set: 'vernalVestments',
+ canOwn: ownsItem('weapon_armoire_vernalTaper'),
+ },
};
let armoireSet = {
diff --git a/website/common/script/content/gear/sets/mystery.js b/website/common/script/content/gear/sets/mystery.js
index cbc25982ec..d7f46bc95e 100644
--- a/website/common/script/content/gear/sets/mystery.js
+++ b/website/common/script/content/gear/sets/mystery.js
@@ -223,6 +223,12 @@ let armor = {
mystery: '201810',
value: 0,
},
+ 201903: {
+ text: t('armorMystery201903Text'),
+ notes: t('armorMystery201903Notes'),
+ mystery: '201903',
+ value: 0,
+ },
301404: {
text: t('armorMystery301404Text'),
notes: t('armorMystery301404Notes'),
@@ -673,6 +679,12 @@ let head = {
mystery: '201901',
value: 0,
},
+ 201903: {
+ text: t('headMystery201903Text'),
+ notes: t('headMystery201903Notes'),
+ mystery: '201903',
+ value: 0,
+ },
301404: {
text: t('headMystery301404Text'),
notes: t('headMystery301404Notes'),
diff --git a/website/common/script/content/gear/sets/special/index.js b/website/common/script/content/gear/sets/special/index.js
index 7704437e33..ee65b82750 100644
--- a/website/common/script/content/gear/sets/special/index.js
+++ b/website/common/script/content/gear/sets/special/index.js
@@ -8,7 +8,7 @@ import takeThisGear from './special-takeThis';
import wonderconGear from './special-wondercon';
import t from '../../../translation';
-const CURRENT_SEASON = '_NONE_';
+const CURRENT_SEASON = 'spring';
let armor = {
0: backerGear.armorSpecial0,
@@ -965,6 +965,9 @@ let armor = {
notes: t('armorSpecialSpring2018RogueNotes', { per: 15 }),
value: 90,
per: 15,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
spring2018Warrior: {
event: EVENTS.spring2018,
@@ -974,6 +977,9 @@ let armor = {
notes: t('armorSpecialSpring2018WarriorNotes', { con: 9 }),
value: 90,
con: 9,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
spring2018Mage: {
event: EVENTS.spring2018,
@@ -983,6 +989,9 @@ let armor = {
notes: t('armorSpecialSpring2018MageNotes', { int: 9 }),
value: 90,
int: 9,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
spring2018Healer: {
event: EVENTS.spring2018,
@@ -992,6 +1001,9 @@ let armor = {
notes: t('armorSpecialSpring2018HealerNotes', { con: 15 }),
value: 90,
con: 15,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
summer2018Rogue: {
event: EVENTS.summer2018,
@@ -1113,6 +1125,42 @@ let armor = {
value: 0,
canOwn: ownsItem('armor_special_birthday2019'),
},
+ spring2019Rogue: {
+ event: EVENTS.spring2019,
+ specialClass: 'rogue',
+ set: 'spring2019CloudRogueSet',
+ text: t('armorSpecialSpring2019RogueText'),
+ notes: t('armorSpecialSpring2019RogueNotes', { per: 15 }),
+ value: 90,
+ per: 15,
+ },
+ spring2019Warrior: {
+ event: EVENTS.spring2019,
+ specialClass: 'warrior',
+ set: 'spring2019OrchidWarriorSet',
+ text: t('armorSpecialSpring2019WarriorText'),
+ notes: t('armorSpecialSpring2019WarriorNotes', { con: 9 }),
+ value: 90,
+ con: 9,
+ },
+ spring2019Mage: {
+ event: EVENTS.spring2019,
+ specialClass: 'wizard',
+ set: 'spring2019AmberMageSet',
+ text: t('armorSpecialSpring2019MageText'),
+ notes: t('armorSpecialSpring2019MageNotes', { int: 9 }),
+ value: 90,
+ int: 9,
+ },
+ spring2019Healer: {
+ event: EVENTS.spring2019,
+ specialClass: 'healer',
+ set: 'spring2019RobinHealerSet',
+ text: t('armorSpecialSpring2019HealerText'),
+ notes: t('armorSpecialSpring2019HealerNotes', { con: 15 }),
+ value: 90,
+ con: 15,
+ },
};
let back = {
@@ -2358,6 +2406,9 @@ let head = {
notes: t('headSpecialSpring2018RogueNotes', { per: 9 }),
value: 60,
per: 9,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
spring2018Warrior: {
event: EVENTS.spring2018,
@@ -2367,6 +2418,9 @@ let head = {
notes: t('headSpecialSpring2018WarriorNotes', { str: 9 }),
value: 60,
str: 9,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
spring2018Mage: {
event: EVENTS.spring2018,
@@ -2376,6 +2430,9 @@ let head = {
notes: t('headSpecialSpring2018MageNotes', { per: 7 }),
value: 60,
per: 7,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
spring2018Healer: {
event: EVENTS.spring2018,
@@ -2385,6 +2442,9 @@ let head = {
notes: t('headSpecialSpring2018HealerNotes', { int: 7 }),
value: 60,
int: 7,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
summer2018Rogue: {
event: EVENTS.summer2018,
@@ -2506,6 +2566,48 @@ let head = {
value: 0,
canOwn: ownsItem('head_special_nye2018'),
},
+ piDay: {
+ text: t('headSpecialPiDayText'),
+ notes: t('headSpecialPiDayNotes'),
+ value: 0,
+ canOwn: ownsItem('head_special_piDay'),
+ },
+ spring2019Rogue: {
+ event: EVENTS.spring2019,
+ specialClass: 'rogue',
+ set: 'spring2019CloudRogueSet',
+ text: t('headSpecialSpring2019RogueText'),
+ notes: t('headSpecialSpring2019RogueNotes', { per: 9 }),
+ value: 60,
+ per: 9,
+ },
+ spring2019Warrior: {
+ event: EVENTS.spring2019,
+ specialClass: 'warrior',
+ set: 'spring2019OrchidWarriorSet',
+ text: t('headSpecialSpring2019WarriorText'),
+ notes: t('headSpecialSpring2019WarriorNotes', { str: 9 }),
+ value: 60,
+ str: 9,
+ },
+ spring2019Mage: {
+ event: EVENTS.spring2019,
+ specialClass: 'wizard',
+ set: 'spring2019AmberMageSet',
+ text: t('headSpecialSpring2019MageText'),
+ notes: t('headSpecialSpring2019MageNotes', { per: 7 }),
+ value: 60,
+ per: 7,
+ },
+ spring2019Healer: {
+ event: EVENTS.spring2019,
+ specialClass: 'healer',
+ set: 'spring2019RobinHealerSet',
+ text: t('headSpecialSpring2019HealerText'),
+ notes: t('headSpecialSpring2019HealerNotes', { int: 7 }),
+ value: 60,
+ int: 7,
+ },
};
let headAccessory = {
@@ -3497,6 +3599,9 @@ let shield = {
notes: t('weaponSpecialSpring2018RogueNotes', { str: 8 }),
value: 80,
str: 8,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
spring2018Warrior: {
event: EVENTS.spring2018,
@@ -3506,6 +3611,9 @@ let shield = {
notes: t('shieldSpecialSpring2018WarriorNotes', { con: 7 }),
value: 70,
con: 7,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
spring2018Healer: {
event: EVENTS.spring2018,
@@ -3515,6 +3623,9 @@ let shield = {
notes: t('shieldSpecialSpring2018HealerNotes', { con: 9 }),
value: 70,
con: 9,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
summer2018Rogue: {
event: EVENTS.summer2018,
@@ -3597,6 +3708,39 @@ let shield = {
value: 70,
con: 9,
},
+ piDay: {
+ text: t('shieldSpecialPiDayText'),
+ notes: t('shieldSpecialPiDayNotes'),
+ value: 0,
+ canOwn: ownsItem('shield_special_piDay'),
+ },
+ spring2019Rogue: {
+ event: EVENTS.spring2019,
+ specialClass: 'rogue',
+ set: 'spring2019CloudRogueSet',
+ text: t('weaponSpecialSpring2019RogueText'),
+ notes: t('weaponSpecialSpring2019RogueNotes', { str: 8 }),
+ value: 80,
+ str: 8,
+ },
+ spring2019Warrior: {
+ event: EVENTS.spring2019,
+ specialClass: 'warrior',
+ set: 'spring2019OrchidWarriorSet',
+ text: t('shieldSpecialSpring2019WarriorText'),
+ notes: t('shieldSpecialSpring2019WarriorNotes', { con: 7 }),
+ value: 70,
+ con: 7,
+ },
+ spring2019Healer: {
+ event: EVENTS.spring2019,
+ specialClass: 'healer',
+ set: 'spring2019RobinHealerSet',
+ text: t('shieldSpecialSpring2019HealerText'),
+ notes: t('shieldSpecialSpring2019HealerNotes', { con: 9 }),
+ value: 70,
+ con: 9,
+ },
};
let weapon = {
@@ -4558,6 +4702,9 @@ let weapon = {
notes: t('weaponSpecialSpring2018RogueNotes', { str: 8 }),
value: 80,
str: 8,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
spring2018Warrior: {
event: EVENTS.spring2018,
@@ -4567,6 +4714,9 @@ let weapon = {
notes: t('weaponSpecialSpring2018WarriorNotes', { str: 15 }),
value: 90,
str: 15,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
spring2018Mage: {
event: EVENTS.spring2018,
@@ -4578,6 +4728,9 @@ let weapon = {
value: 160,
int: 15,
per: 7,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
spring2018Healer: {
event: EVENTS.spring2018,
@@ -4587,6 +4740,9 @@ let weapon = {
notes: t('weaponSpecialSpring2018HealerNotes', { int: 9 }),
value: 90,
int: 9,
+ canBuy: () => {
+ return CURRENT_SEASON === 'spring';
+ },
},
summer2018Rogue: {
event: EVENTS.summer2018,
@@ -4702,6 +4858,44 @@ let weapon = {
value: 90,
int: 9,
},
+ spring2019Rogue: {
+ event: EVENTS.spring2019,
+ specialClass: 'rogue',
+ set: 'spring2019CloudRogueSet',
+ text: t('weaponSpecialSpring2019RogueText'),
+ notes: t('weaponSpecialSpring2019RogueNotes', { str: 8 }),
+ value: 80,
+ str: 8,
+ },
+ spring2019Warrior: {
+ event: EVENTS.spring2019,
+ specialClass: 'warrior',
+ set: 'spring2019OrchidWarriorSet',
+ text: t('weaponSpecialSpring2019WarriorText'),
+ notes: t('weaponSpecialSpring2019WarriorNotes', { str: 15 }),
+ value: 90,
+ str: 15,
+ },
+ spring2019Mage: {
+ event: EVENTS.spring2019,
+ specialClass: 'wizard',
+ set: 'spring2019AmberMageSet',
+ twoHanded: true,
+ text: t('weaponSpecialSpring2019MageText'),
+ notes: t('weaponSpecialSpring2019MageNotes', { int: 15, per: 7 }),
+ value: 160,
+ int: 15,
+ per: 7,
+ },
+ spring2019Healer: {
+ event: EVENTS.spring2019,
+ specialClass: 'healer',
+ set: 'spring2019RobinHealerSet',
+ text: t('weaponSpecialSpring2019HealerText'),
+ notes: t('weaponSpecialSpring2019HealerNotes', { int: 9 }),
+ value: 90,
+ int: 9,
+ },
};
let specialSet = {
diff --git a/website/common/script/content/hatching-potions.js b/website/common/script/content/hatching-potions.js
index e05bb1ade2..d4395c8fcb 100644
--- a/website/common/script/content/hatching-potions.js
+++ b/website/common/script/content/hatching-potions.js
@@ -3,7 +3,7 @@ import defaults from 'lodash/defaults';
import each from 'lodash/each';
import t from './translation';
-const CURRENT_SEASON = 'February';
+const CURRENT_SEASON = 'March';
let drops = {
Base: {
@@ -58,43 +58,43 @@ let premium = {
value: 2,
text: t('hatchingPotionCupid'),
limited: true,
- _season: 'February',
+ _season: '_PENDING_',
},
Shimmer: {
value: 2,
text: t('hatchingPotionShimmer'),
limited: true,
- _season: 'March',
+ _season: '_PENDING_',
},
Fairy: {
value: 2,
text: t('hatchingPotionFairy'),
limited: true,
- _season: 'May',
+ _season: '_PENDING_',
},
Floral: {
value: 2,
text: t('hatchingPotionFloral'),
limited: true,
- _season: 'June',
+ _season: '_PENDING_',
},
Aquatic: {
value: 2,
text: t('hatchingPotionAquatic'),
limited: true,
- _season: 'July',
+ _season: '_PENDING_',
},
Ember: {
value: 2,
text: t('hatchingPotionEmber'),
limited: true,
- _season: 'September',
+ _season: '_PENDING_',
},
Thunderstorm: {
value: 2,
text: t('hatchingPotionThunderstorm'),
limited: true,
- _season: 'November',
+ _season: '_PENDING_',
},
Spooky: {
value: 2,
@@ -106,7 +106,7 @@ let premium = {
value: 2,
text: t('hatchingPotionGhost'),
limited: true,
- _season: 'October',
+ _season: '_PENDING_',
},
Holly: {
value: 2,
@@ -118,13 +118,13 @@ let premium = {
value: 2,
text: t('hatchingPotionPeppermint'),
limited: true,
- _season: 'January',
+ _season: '_PENDING_',
},
StarryNight: {
value: 2,
text: t('hatchingPotionStarryNight'),
limited: true,
- _season: 'January',
+ _season: '_PENDING_',
},
Rainbow: {
value: 2,
@@ -136,31 +136,37 @@ let premium = {
value: 2,
text: t('hatchingPotionGlass'),
limited: true,
- _season: 'July',
+ _season: '_PENDING_',
},
Glow: {
value: 2,
text: t('hatchingPotionGlow'),
limited: true,
- _season: 'October',
+ _season: '_PENDING_',
},
Frost: {
value: 2,
text: t('hatchingPotionFrost'),
limited: true,
- _season: 'November',
+ _season: '_PENDING_',
},
IcySnow: {
value: 2,
text: t('hatchingPotionIcySnow'),
limited: true,
- _season: 'January',
+ _season: '_PENDING_',
},
RoseQuartz: {
value: 2,
text: t('hatchingPotionRoseQuartz'),
limited: true,
- _season: 'February',
+ _season: '_PENDING_',
+ },
+ Celestial: {
+ value: 2,
+ text: t('hatchingPotionCelestial'),
+ limited: true,
+ _season: 'March',
},
};
diff --git a/website/common/script/content/index.js b/website/common/script/content/index.js
index e87ace6cbd..09c8e9fda5 100644
--- a/website/common/script/content/index.js
+++ b/website/common/script/content/index.js
@@ -265,7 +265,7 @@ api.armoire = {
if (user.flags.armoireEmpty) {
return t('armoireNotesEmpty')();
}
- return t('armoireNotesFull')() + count;
+ return `${t('armoireNotesFull')()} ${count}`;
},
value: 100,
key: 'armoire',
@@ -370,13 +370,9 @@ api.premiumMounts = stable.premiumMounts;
api.specialMounts = stable.specialMounts;
api.mountInfo = stable.mountInfo;
-// For seasonal events, change these booleans:
-let canBuyNormalFood = true;
-let canDropNormalFood = true;
-let canBuyCandyFood = false;
-let canDropCandyFood = false;
-let canBuyCakeFood = false;
-let canDropCakeFood = false;
+// For seasonal events, change this constant:
+
+const FOOD_SEASON = 'Normal';
api.food = {
Meat: {
@@ -385,9 +381,9 @@ api.food = {
textThe: t('foodMeatThe'),
target: 'Base',
canBuy () {
- return canBuyNormalFood;
+ return FOOD_SEASON === 'Normal';
},
- canDrop: canDropNormalFood,
+ canDrop: FOOD_SEASON === 'Normal',
},
Milk: {
text: t('foodMilk'),
@@ -395,9 +391,9 @@ api.food = {
textThe: t('foodMilkThe'),
target: 'White',
canBuy () {
- return canBuyNormalFood;
+ return FOOD_SEASON === 'Normal';
},
- canDrop: canDropNormalFood,
+ canDrop: FOOD_SEASON === 'Normal',
},
Potatoe: {
text: t('foodPotatoe'),
@@ -405,9 +401,9 @@ api.food = {
textThe: t('foodPotatoeThe'),
target: 'Desert',
canBuy () {
- return canBuyNormalFood;
+ return FOOD_SEASON === 'Normal';
},
- canDrop: canDropNormalFood,
+ canDrop: FOOD_SEASON === 'Normal',
},
Strawberry: {
text: t('foodStrawberry'),
@@ -415,9 +411,9 @@ api.food = {
textThe: t('foodStrawberryThe'),
target: 'Red',
canBuy () {
- return canBuyNormalFood;
+ return FOOD_SEASON === 'Normal';
},
- canDrop: canDropNormalFood,
+ canDrop: FOOD_SEASON === 'Normal',
},
Chocolate: {
text: t('foodChocolate'),
@@ -425,9 +421,9 @@ api.food = {
textThe: t('foodChocolateThe'),
target: 'Shade',
canBuy () {
- return canBuyNormalFood;
+ return FOOD_SEASON === 'Normal';
},
- canDrop: canDropNormalFood,
+ canDrop: FOOD_SEASON === 'Normal',
},
Fish: {
text: t('foodFish'),
@@ -435,9 +431,9 @@ api.food = {
textThe: t('foodFishThe'),
target: 'Skeleton',
canBuy () {
- return canBuyNormalFood;
+ return FOOD_SEASON === 'Normal';
},
- canDrop: canDropNormalFood,
+ canDrop: FOOD_SEASON === 'Normal',
},
RottenMeat: {
text: t('foodRottenMeat'),
@@ -445,9 +441,9 @@ api.food = {
textThe: t('foodRottenMeatThe'),
target: 'Zombie',
canBuy () {
- return canBuyNormalFood;
+ return FOOD_SEASON === 'Normal';
},
- canDrop: canDropNormalFood,
+ canDrop: FOOD_SEASON === 'Normal',
},
CottonCandyPink: {
text: t('foodCottonCandyPink'),
@@ -455,9 +451,9 @@ api.food = {
textThe: t('foodCottonCandyPinkThe'),
target: 'CottonCandyPink',
canBuy () {
- return canBuyNormalFood;
+ return FOOD_SEASON === 'Normal';
},
- canDrop: canDropNormalFood,
+ canDrop: FOOD_SEASON === 'Normal',
},
CottonCandyBlue: {
text: t('foodCottonCandyBlue'),
@@ -465,9 +461,9 @@ api.food = {
textThe: t('foodCottonCandyBlueThe'),
target: 'CottonCandyBlue',
canBuy () {
- return canBuyNormalFood;
+ return FOOD_SEASON === 'Normal';
},
- canDrop: canDropNormalFood,
+ canDrop: FOOD_SEASON === 'Normal',
},
Honey: {
text: t('foodHoney'),
@@ -475,9 +471,9 @@ api.food = {
textThe: t('foodHoneyThe'),
target: 'Golden',
canBuy () {
- return canBuyNormalFood;
+ return FOOD_SEASON === 'Normal';
},
- canDrop: canDropNormalFood,
+ canDrop: FOOD_SEASON === 'Normal',
},
Saddle: {
canBuy () {
@@ -495,9 +491,9 @@ api.food = {
textThe: t('foodCakeSkeletonThe'),
target: 'Skeleton',
canBuy () {
- return canBuyCakeFood;
+ return FOOD_SEASON === 'Cake';
},
- canDrop: canDropCakeFood,
+ canDrop: FOOD_SEASON === 'Cake',
},
Cake_Base: {
text: t('foodCakeBase'),
@@ -505,9 +501,9 @@ api.food = {
textThe: t('foodCakeBaseThe'),
target: 'Base',
canBuy () {
- return canBuyCakeFood;
+ return FOOD_SEASON === 'Cake';
},
- canDrop: canDropCakeFood,
+ canDrop: FOOD_SEASON === 'Cake',
},
Cake_CottonCandyBlue: {
text: t('foodCakeCottonCandyBlue'),
@@ -515,9 +511,9 @@ api.food = {
textThe: t('foodCakeCottonCandyBlueThe'),
target: 'CottonCandyBlue',
canBuy () {
- return canBuyCakeFood;
+ return FOOD_SEASON === 'Cake';
},
- canDrop: canDropCakeFood,
+ canDrop: FOOD_SEASON === 'Cake',
},
Cake_CottonCandyPink: {
text: t('foodCakeCottonCandyPink'),
@@ -525,9 +521,9 @@ api.food = {
textThe: t('foodCakeCottonCandyPinkThe'),
target: 'CottonCandyPink',
canBuy () {
- return canBuyCakeFood;
+ return FOOD_SEASON === 'Cake';
},
- canDrop: canDropCakeFood,
+ canDrop: FOOD_SEASON === 'Cake',
},
Cake_Shade: {
text: t('foodCakeShade'),
@@ -535,9 +531,9 @@ api.food = {
textThe: t('foodCakeShadeThe'),
target: 'Shade',
canBuy () {
- return canBuyCakeFood;
+ return FOOD_SEASON === 'Cake';
},
- canDrop: canDropCakeFood,
+ canDrop: FOOD_SEASON === 'Cake',
},
Cake_White: {
text: t('foodCakeWhite'),
@@ -545,9 +541,9 @@ api.food = {
textThe: t('foodCakeWhiteThe'),
target: 'White',
canBuy () {
- return canBuyCakeFood;
+ return FOOD_SEASON === 'Cake';
},
- canDrop: canDropCakeFood,
+ canDrop: FOOD_SEASON === 'Cake',
},
Cake_Golden: {
text: t('foodCakeGolden'),
@@ -555,9 +551,9 @@ api.food = {
textThe: t('foodCakeGoldenThe'),
target: 'Golden',
canBuy () {
- return canBuyCakeFood;
+ return FOOD_SEASON === 'Cake';
},
- canDrop: canDropCakeFood,
+ canDrop: FOOD_SEASON === 'Cake',
},
Cake_Zombie: {
text: t('foodCakeZombie'),
@@ -565,9 +561,9 @@ api.food = {
textThe: t('foodCakeZombieThe'),
target: 'Zombie',
canBuy () {
- return canBuyCakeFood;
+ return FOOD_SEASON === 'Cake';
},
- canDrop: canDropCakeFood,
+ canDrop: FOOD_SEASON === 'Cake',
},
Cake_Desert: {
text: t('foodCakeDesert'),
@@ -575,9 +571,9 @@ api.food = {
textThe: t('foodCakeDesertThe'),
target: 'Desert',
canBuy () {
- return canBuyCakeFood;
+ return FOOD_SEASON === 'Cake';
},
- canDrop: canDropCakeFood,
+ canDrop: FOOD_SEASON === 'Cake',
},
Cake_Red: {
text: t('foodCakeRed'),
@@ -585,9 +581,9 @@ api.food = {
textThe: t('foodCakeRedThe'),
target: 'Red',
canBuy () {
- return canBuyCakeFood;
+ return FOOD_SEASON === 'Cake';
},
- canDrop: canDropCakeFood,
+ canDrop: FOOD_SEASON === 'Cake',
},
Candy_Skeleton: {
text: t('foodCandySkeleton'),
@@ -595,9 +591,9 @@ api.food = {
textThe: t('foodCandySkeletonThe'),
target: 'Skeleton',
canBuy () {
- return canBuyCandyFood;
+ return FOOD_SEASON === 'Candy';
},
- canDrop: canDropCandyFood,
+ canDrop: FOOD_SEASON === 'Candy',
},
Candy_Base: {
text: t('foodCandyBase'),
@@ -605,9 +601,9 @@ api.food = {
textThe: t('foodCandyBaseThe'),
target: 'Base',
canBuy () {
- return canBuyCandyFood;
+ return FOOD_SEASON === 'Candy';
},
- canDrop: canDropCandyFood,
+ canDrop: FOOD_SEASON === 'Candy',
},
Candy_CottonCandyBlue: {
text: t('foodCandyCottonCandyBlue'),
@@ -615,9 +611,9 @@ api.food = {
textThe: t('foodCandyCottonCandyBlueThe'),
target: 'CottonCandyBlue',
canBuy () {
- return canBuyCandyFood;
+ return FOOD_SEASON === 'Candy';
},
- canDrop: canDropCandyFood,
+ canDrop: FOOD_SEASON === 'Candy',
},
Candy_CottonCandyPink: {
text: t('foodCandyCottonCandyPink'),
@@ -625,9 +621,9 @@ api.food = {
textThe: t('foodCandyCottonCandyPinkThe'),
target: 'CottonCandyPink',
canBuy () {
- return canBuyCandyFood;
+ return FOOD_SEASON === 'Candy';
},
- canDrop: canDropCandyFood,
+ canDrop: FOOD_SEASON === 'Candy',
},
Candy_Shade: {
text: t('foodCandyShade'),
@@ -635,9 +631,9 @@ api.food = {
textThe: t('foodCandyShadeThe'),
target: 'Shade',
canBuy () {
- return canBuyCandyFood;
+ return FOOD_SEASON === 'Candy';
},
- canDrop: canDropCandyFood,
+ canDrop: FOOD_SEASON === 'Candy',
},
Candy_White: {
text: t('foodCandyWhite'),
@@ -645,9 +641,9 @@ api.food = {
textThe: t('foodCandyWhiteThe'),
target: 'White',
canBuy () {
- return canBuyCandyFood;
+ return FOOD_SEASON === 'Candy';
},
- canDrop: canDropCandyFood,
+ canDrop: FOOD_SEASON === 'Candy',
},
Candy_Golden: {
text: t('foodCandyGolden'),
@@ -655,9 +651,9 @@ api.food = {
textThe: t('foodCandyGoldenThe'),
target: 'Golden',
canBuy () {
- return canBuyCandyFood;
+ return FOOD_SEASON === 'Candy';
},
- canDrop: canDropCandyFood,
+ canDrop: FOOD_SEASON === 'Candy',
},
Candy_Zombie: {
text: t('foodCandyZombie'),
@@ -665,9 +661,9 @@ api.food = {
textThe: t('foodCandyZombieThe'),
target: 'Zombie',
canBuy () {
- return canBuyCandyFood;
+ return FOOD_SEASON === 'Candy';
},
- canDrop: canDropCandyFood,
+ canDrop: FOOD_SEASON === 'Candy',
},
Candy_Desert: {
text: t('foodCandyDesert'),
@@ -675,9 +671,9 @@ api.food = {
textThe: t('foodCandyDesertThe'),
target: 'Desert',
canBuy () {
- return canBuyCandyFood;
+ return FOOD_SEASON === 'Candy';
},
- canDrop: canDropCandyFood,
+ canDrop: FOOD_SEASON === 'Candy',
},
Candy_Red: {
text: t('foodCandyRed'),
@@ -685,9 +681,109 @@ api.food = {
textThe: t('foodCandyRedThe'),
target: 'Red',
canBuy () {
- return canBuyCandyFood;
+ return FOOD_SEASON === 'Candy';
},
- canDrop: canDropCandyFood,
+ canDrop: FOOD_SEASON === 'Candy',
+ },
+ Pie_Skeleton: {
+ text: t('foodPieSkeleton'),
+ textA: t('foodPieSkeletonA'),
+ textThe: t('foodPieSkeletonThe'),
+ target: 'Skeleton',
+ canBuy () {
+ return FOOD_SEASON === 'Pie';
+ },
+ canDrop: FOOD_SEASON === 'Pie',
+ },
+ Pie_Base: {
+ text: t('foodPieBase'),
+ textA: t('foodPieBaseA'),
+ textThe: t('foodPieBaseThe'),
+ target: 'Base',
+ canBuy () {
+ return FOOD_SEASON === 'Pie';
+ },
+ canDrop: FOOD_SEASON === 'Pie',
+ },
+ Pie_CottonCandyBlue: {
+ text: t('foodPieCottonCandyBlue'),
+ textA: t('foodPieCottonCandyBlueA'),
+ textThe: t('foodPieCottonCandyBlueThe'),
+ target: 'CottonCandyBlue',
+ canBuy () {
+ return FOOD_SEASON === 'Pie';
+ },
+ canDrop: FOOD_SEASON === 'Pie',
+ },
+ Pie_CottonCandyPink: {
+ text: t('foodPieCottonCandyPink'),
+ textA: t('foodPieCottonCandyPinkA'),
+ textThe: t('foodPieCottonCandyPinkThe'),
+ target: 'CottonCandyPink',
+ canBuy () {
+ return FOOD_SEASON === 'Pie';
+ },
+ canDrop: FOOD_SEASON === 'Pie',
+ },
+ Pie_Shade: {
+ text: t('foodPieShade'),
+ textA: t('foodPieShadeA'),
+ textThe: t('foodPieShadeThe'),
+ target: 'Shade',
+ canBuy () {
+ return FOOD_SEASON === 'Pie';
+ },
+ canDrop: FOOD_SEASON === 'Pie',
+ },
+ Pie_White: {
+ text: t('foodPieWhite'),
+ textA: t('foodPieWhiteA'),
+ textThe: t('foodPieWhiteThe'),
+ target: 'White',
+ canBuy () {
+ return FOOD_SEASON === 'Pie';
+ },
+ canDrop: FOOD_SEASON === 'Pie',
+ },
+ Pie_Golden: {
+ text: t('foodPieGolden'),
+ textA: t('foodPieGoldenA'),
+ textThe: t('foodPieGoldenThe'),
+ target: 'Golden',
+ canBuy () {
+ return FOOD_SEASON === 'Pie';
+ },
+ canDrop: FOOD_SEASON === 'Pie',
+ },
+ Pie_Zombie: {
+ text: t('foodPieZombie'),
+ textA: t('foodPieZombieA'),
+ textThe: t('foodPieZombieThe'),
+ target: 'Zombie',
+ canBuy () {
+ return FOOD_SEASON === 'Pie';
+ },
+ canDrop: FOOD_SEASON === 'Pie',
+ },
+ Pie_Desert: {
+ text: t('foodPieDesert'),
+ textA: t('foodPieDesertA'),
+ textThe: t('foodPieDesertThe'),
+ target: 'Desert',
+ canBuy () {
+ return FOOD_SEASON === 'Pie';
+ },
+ canDrop: FOOD_SEASON === 'Pie',
+ },
+ Pie_Red: {
+ text: t('foodPieRed'),
+ textA: t('foodPieRedA'),
+ textThe: t('foodPieRedThe'),
+ target: 'Red',
+ canBuy () {
+ return FOOD_SEASON === 'Pie';
+ },
+ canDrop: FOOD_SEASON === 'Pie',
},
/* eslint-enable camelcase */
};
diff --git a/website/common/script/content/mystery-sets.js b/website/common/script/content/mystery-sets.js
index c1d898873d..980e880e16 100644
--- a/website/common/script/content/mystery-sets.js
+++ b/website/common/script/content/mystery-sets.js
@@ -246,6 +246,10 @@ let mysterySets = {
start: '2019-02-25',
end: '2019-03-02',
},
+ 201903: {
+ start: '2019-03-26',
+ end: '2019-04-02',
+ },
301404: {
start: '3014-03-24',
end: '3014-04-02',
diff --git a/website/common/script/content/quests.js b/website/common/script/content/quests.js
index 8c903681fa..80e8f14f2a 100644
--- a/website/common/script/content/quests.js
+++ b/website/common/script/content/quests.js
@@ -511,7 +511,7 @@ let quests = {
value: 1,
category: 'pet',
canBuy () {
- return false;
+ return true;
},
collect: {
plainEgg: {
diff --git a/website/common/script/content/shop-featuredItems.js b/website/common/script/content/shop-featuredItems.js
index 6a5fdc9af2..09c8c6ffa7 100644
--- a/website/common/script/content/shop-featuredItems.js
+++ b/website/common/script/content/shop-featuredItems.js
@@ -8,12 +8,12 @@ const featuredItems = {
path: 'armoire',
},
{
- type: 'premiumHatchingPotion',
- path: 'premiumHatchingPotions.Cupid',
+ type: 'hatchingPotions',
+ path: 'hatchingPotions.Golden',
},
{
- type: 'premiumHatchingPotion',
- path: 'premiumHatchingPotions.RoseQuartz',
+ type: 'eggs',
+ path: 'eggs.PandaCub',
},
{
type: 'card',
@@ -27,14 +27,14 @@ const featuredItems = {
},
{
type: 'quests',
- path: 'quests.sabretooth',
+ path: 'quests.egg',
},
{
type: 'quests',
path: 'quests.rock',
},
],
- seasonal: '',
+ seasonal: 'spring2018Healer',
timeTravelers: [
// TODO
],
diff --git a/website/common/script/libs/shops-seasonal.config.js b/website/common/script/libs/shops-seasonal.config.js
index 4f38ef5b53..af78c532d3 100644
--- a/website/common/script/libs/shops-seasonal.config.js
+++ b/website/common/script/libs/shops-seasonal.config.js
@@ -1,23 +1,29 @@
-// import { SEASONAL_SETS } from '../content/constants';
+import { SEASONAL_SETS } from '../content/constants';
module.exports = {
- opened: false,
+ opened: true,
- currentSeason: 'Closed',
+ currentSeason: 'Spring',
- dateRange: { start: '2018-09-20', end: '2018-10-31' },
+ dateRange: { start: '2019-03-19', end: '2019-04-30' },
availableSets: [
+ ...SEASONAL_SETS.spring,
],
pinnedSets: {
+ wizard: 'spring2019AmberMageSet',
+ warrior: 'spring2019OrchidWarriorSet',
+ rogue: 'spring2019CloudRogueSet',
+ healer: 'spring2019RobinHealerSet',
},
availableSpells: [
],
availableQuests: [
+ 'egg',
],
- featuredSet: 'mummyMedicSet',
+ featuredSet: 'spring2018DucklingRogueSet',
};
diff --git a/website/raw_sprites/spritesmith/backgrounds/background_duck_pond.png b/website/raw_sprites/spritesmith/backgrounds/background_duck_pond.png
new file mode 100644
index 0000000000..7abeced836
Binary files /dev/null and b/website/raw_sprites/spritesmith/backgrounds/background_duck_pond.png differ
diff --git a/website/raw_sprites/spritesmith/backgrounds/background_field_with_colored_eggs.png b/website/raw_sprites/spritesmith/backgrounds/background_field_with_colored_eggs.png
new file mode 100644
index 0000000000..eae2f9a773
Binary files /dev/null and b/website/raw_sprites/spritesmith/backgrounds/background_field_with_colored_eggs.png differ
diff --git a/website/raw_sprites/spritesmith/backgrounds/background_flower_market.png b/website/raw_sprites/spritesmith/backgrounds/background_flower_market.png
new file mode 100644
index 0000000000..6223a8c384
Binary files /dev/null and b/website/raw_sprites/spritesmith/backgrounds/background_flower_market.png differ
diff --git a/website/raw_sprites/spritesmith/backgrounds/icons/icon_background_duck_pond.png b/website/raw_sprites/spritesmith/backgrounds/icons/icon_background_duck_pond.png
new file mode 100644
index 0000000000..5f8849b073
Binary files /dev/null and b/website/raw_sprites/spritesmith/backgrounds/icons/icon_background_duck_pond.png differ
diff --git a/website/raw_sprites/spritesmith/backgrounds/icons/icon_background_field_with_colored_eggs.png b/website/raw_sprites/spritesmith/backgrounds/icons/icon_background_field_with_colored_eggs.png
new file mode 100644
index 0000000000..aab2b1d67b
Binary files /dev/null and b/website/raw_sprites/spritesmith/backgrounds/icons/icon_background_field_with_colored_eggs.png differ
diff --git a/website/raw_sprites/spritesmith/backgrounds/icons/icon_background_flower_market.png b/website/raw_sprites/spritesmith/backgrounds/icons/icon_background_flower_market.png
new file mode 100644
index 0000000000..bbc5ec0833
Binary files /dev/null and b/website/raw_sprites/spritesmith/backgrounds/icons/icon_background_flower_market.png differ
diff --git a/website/raw_sprites/spritesmith/gear/armoire/broad_armor_armoire_vernalVestment.png b/website/raw_sprites/spritesmith/gear/armoire/broad_armor_armoire_vernalVestment.png
new file mode 100644
index 0000000000..cf506fb429
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/armoire/broad_armor_armoire_vernalVestment.png differ
diff --git a/website/raw_sprites/spritesmith/gear/armoire/head_armoire_vernalHennin.png b/website/raw_sprites/spritesmith/gear/armoire/head_armoire_vernalHennin.png
new file mode 100644
index 0000000000..ba1176e5f0
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/armoire/head_armoire_vernalHennin.png differ
diff --git a/website/raw_sprites/spritesmith/gear/armoire/shop/shop_armor_armoire_vernalVestment.png b/website/raw_sprites/spritesmith/gear/armoire/shop/shop_armor_armoire_vernalVestment.png
new file mode 100644
index 0000000000..dd0b1f512d
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/armoire/shop/shop_armor_armoire_vernalVestment.png differ
diff --git a/website/raw_sprites/spritesmith/gear/armoire/shop/shop_head_armoire_vernalHennin.png b/website/raw_sprites/spritesmith/gear/armoire/shop/shop_head_armoire_vernalHennin.png
new file mode 100644
index 0000000000..6766f60640
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/armoire/shop/shop_head_armoire_vernalHennin.png differ
diff --git a/website/raw_sprites/spritesmith/gear/armoire/shop/shop_weapon_armoire_vernalTaper.png b/website/raw_sprites/spritesmith/gear/armoire/shop/shop_weapon_armoire_vernalTaper.png
new file mode 100644
index 0000000000..eac12eeadd
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/armoire/shop/shop_weapon_armoire_vernalTaper.png differ
diff --git a/website/raw_sprites/spritesmith/gear/armoire/slim_armor_armoire_vernalVestment.png b/website/raw_sprites/spritesmith/gear/armoire/slim_armor_armoire_vernalVestment.png
new file mode 100644
index 0000000000..2b4e13f842
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/armoire/slim_armor_armoire_vernalVestment.png differ
diff --git a/website/raw_sprites/spritesmith/gear/armoire/weapon_armoire_vernalTaper.png b/website/raw_sprites/spritesmith/gear/armoire/weapon_armoire_vernalTaper.png
new file mode 100644
index 0000000000..8a3ce57baa
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/armoire/weapon_armoire_vernalTaper.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/mystery_201903/broad_armor_mystery_201903.png b/website/raw_sprites/spritesmith/gear/events/mystery_201903/broad_armor_mystery_201903.png
new file mode 100644
index 0000000000..a6ae8c3ed1
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/mystery_201903/broad_armor_mystery_201903.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/mystery_201903/head_mystery_201903.png b/website/raw_sprites/spritesmith/gear/events/mystery_201903/head_mystery_201903.png
new file mode 100644
index 0000000000..4e57ef17ea
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/mystery_201903/head_mystery_201903.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/mystery_201903/shop_armor_mystery_201903.png b/website/raw_sprites/spritesmith/gear/events/mystery_201903/shop_armor_mystery_201903.png
new file mode 100644
index 0000000000..552d6df5fb
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/mystery_201903/shop_armor_mystery_201903.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/mystery_201903/shop_head_mystery_201903.png b/website/raw_sprites/spritesmith/gear/events/mystery_201903/shop_head_mystery_201903.png
new file mode 100644
index 0000000000..044a5738ef
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/mystery_201903/shop_head_mystery_201903.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/mystery_201903/shop_set_mystery_201903.png b/website/raw_sprites/spritesmith/gear/events/mystery_201903/shop_set_mystery_201903.png
new file mode 100644
index 0000000000..ac66d1ee0f
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/mystery_201903/shop_set_mystery_201903.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/mystery_201903/slim_armor_mystery_201903.png b/website/raw_sprites/spritesmith/gear/events/mystery_201903/slim_armor_mystery_201903.png
new file mode 100644
index 0000000000..fb20e01e47
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/mystery_201903/slim_armor_mystery_201903.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/piDay/head_special_piDay.png b/website/raw_sprites/spritesmith/gear/events/piDay/head_special_piDay.png
new file mode 100644
index 0000000000..042a1c45f3
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/piDay/head_special_piDay.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/piDay/shield_special_piDay.png b/website/raw_sprites/spritesmith/gear/events/piDay/shield_special_piDay.png
new file mode 100644
index 0000000000..c8cd7113a8
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/piDay/shield_special_piDay.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/piDay/shop_head_special_piDay.png b/website/raw_sprites/spritesmith/gear/events/piDay/shop_head_special_piDay.png
new file mode 100644
index 0000000000..ef534c7212
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/piDay/shop_head_special_piDay.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/piDay/shop_shield_special_piDay.png b/website/raw_sprites/spritesmith/gear/events/piDay/shop_shield_special_piDay.png
new file mode 100644
index 0000000000..0289cc564f
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/piDay/shop_shield_special_piDay.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/broad_armor_special_spring2019Healer.png b/website/raw_sprites/spritesmith/gear/events/spring/broad_armor_special_spring2019Healer.png
new file mode 100644
index 0000000000..a19d6396ea
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/broad_armor_special_spring2019Healer.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/broad_armor_special_spring2019Mage.png b/website/raw_sprites/spritesmith/gear/events/spring/broad_armor_special_spring2019Mage.png
new file mode 100644
index 0000000000..e145b42699
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/broad_armor_special_spring2019Mage.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/broad_armor_special_spring2019Rogue.png b/website/raw_sprites/spritesmith/gear/events/spring/broad_armor_special_spring2019Rogue.png
new file mode 100644
index 0000000000..00a068193a
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/broad_armor_special_spring2019Rogue.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/broad_armor_special_spring2019Warrior.png b/website/raw_sprites/spritesmith/gear/events/spring/broad_armor_special_spring2019Warrior.png
new file mode 100644
index 0000000000..849c7ac7dc
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/broad_armor_special_spring2019Warrior.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/head_special_spring2019Healer.png b/website/raw_sprites/spritesmith/gear/events/spring/head_special_spring2019Healer.png
new file mode 100644
index 0000000000..887af6f537
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/head_special_spring2019Healer.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/head_special_spring2019Mage.png b/website/raw_sprites/spritesmith/gear/events/spring/head_special_spring2019Mage.png
new file mode 100644
index 0000000000..ee662594d2
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/head_special_spring2019Mage.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/head_special_spring2019Rogue.png b/website/raw_sprites/spritesmith/gear/events/spring/head_special_spring2019Rogue.png
new file mode 100644
index 0000000000..10bbb5299a
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/head_special_spring2019Rogue.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/head_special_spring2019Warrior.png b/website/raw_sprites/spritesmith/gear/events/spring/head_special_spring2019Warrior.png
new file mode 100644
index 0000000000..c4a2d71bc2
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/head_special_spring2019Warrior.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shield_special_spring2019Healer.png b/website/raw_sprites/spritesmith/gear/events/spring/shield_special_spring2019Healer.png
new file mode 100644
index 0000000000..3c324c48fd
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shield_special_spring2019Healer.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shield_special_spring2019Rogue.png b/website/raw_sprites/spritesmith/gear/events/spring/shield_special_spring2019Rogue.png
new file mode 100644
index 0000000000..25d30193c5
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shield_special_spring2019Rogue.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shield_special_spring2019Warrior.png b/website/raw_sprites/spritesmith/gear/events/spring/shield_special_spring2019Warrior.png
new file mode 100644
index 0000000000..4d88c4d0ab
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shield_special_spring2019Warrior.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_armor_special_spring2019Healer.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_armor_special_spring2019Healer.png
new file mode 100644
index 0000000000..a9b554dc1c
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_armor_special_spring2019Healer.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_armor_special_spring2019Mage.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_armor_special_spring2019Mage.png
new file mode 100644
index 0000000000..e64ed698e5
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_armor_special_spring2019Mage.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_armor_special_spring2019Rogue.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_armor_special_spring2019Rogue.png
new file mode 100644
index 0000000000..beda9a960c
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_armor_special_spring2019Rogue.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_armor_special_spring2019Warrior.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_armor_special_spring2019Warrior.png
new file mode 100644
index 0000000000..a6ffe1da77
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_armor_special_spring2019Warrior.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_head_special_spring2019Healer.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_head_special_spring2019Healer.png
new file mode 100644
index 0000000000..b2c6bf8058
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_head_special_spring2019Healer.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_head_special_spring2019Mage.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_head_special_spring2019Mage.png
new file mode 100644
index 0000000000..3289b6a0ce
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_head_special_spring2019Mage.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_head_special_spring2019Rogue.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_head_special_spring2019Rogue.png
new file mode 100644
index 0000000000..3268fa2a51
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_head_special_spring2019Rogue.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_head_special_spring2019Warrior.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_head_special_spring2019Warrior.png
new file mode 100644
index 0000000000..4b6e745cfd
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_head_special_spring2019Warrior.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_shield_special_spring2019Healer.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_shield_special_spring2019Healer.png
new file mode 100644
index 0000000000..1faf7c7dc8
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_shield_special_spring2019Healer.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_shield_special_spring2019Rogue.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_shield_special_spring2019Rogue.png
new file mode 100644
index 0000000000..2d0fd4a352
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_shield_special_spring2019Rogue.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_shield_special_spring2019Warrior.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_shield_special_spring2019Warrior.png
new file mode 100644
index 0000000000..b7d6160c08
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_shield_special_spring2019Warrior.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_weapon_special_spring2019Healer.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_weapon_special_spring2019Healer.png
new file mode 100644
index 0000000000..cc3de8d3ac
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_weapon_special_spring2019Healer.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_weapon_special_spring2019Mage.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_weapon_special_spring2019Mage.png
new file mode 100644
index 0000000000..3ca13615a6
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_weapon_special_spring2019Mage.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_weapon_special_spring2019Rogue.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_weapon_special_spring2019Rogue.png
new file mode 100644
index 0000000000..83345ebe7e
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_weapon_special_spring2019Rogue.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_weapon_special_spring2019Warrior.png b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_weapon_special_spring2019Warrior.png
new file mode 100644
index 0000000000..2268e4d2db
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/shop/shop_weapon_special_spring2019Warrior.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/slim_armor_special_spring2019Healer.png b/website/raw_sprites/spritesmith/gear/events/spring/slim_armor_special_spring2019Healer.png
new file mode 100644
index 0000000000..2f8f986964
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/slim_armor_special_spring2019Healer.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/slim_armor_special_spring2019Mage.png b/website/raw_sprites/spritesmith/gear/events/spring/slim_armor_special_spring2019Mage.png
new file mode 100644
index 0000000000..6d9ed6e73c
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/slim_armor_special_spring2019Mage.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/slim_armor_special_spring2019Rogue.png b/website/raw_sprites/spritesmith/gear/events/spring/slim_armor_special_spring2019Rogue.png
new file mode 100644
index 0000000000..9b3cf9f4b0
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/slim_armor_special_spring2019Rogue.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/slim_armor_special_spring2019Warrior.png b/website/raw_sprites/spritesmith/gear/events/spring/slim_armor_special_spring2019Warrior.png
new file mode 100644
index 0000000000..7493cbe2bd
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/slim_armor_special_spring2019Warrior.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/weapon_special_spring2019Healer.png b/website/raw_sprites/spritesmith/gear/events/spring/weapon_special_spring2019Healer.png
new file mode 100644
index 0000000000..a6052ef5f7
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/weapon_special_spring2019Healer.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/weapon_special_spring2019Mage.png b/website/raw_sprites/spritesmith/gear/events/spring/weapon_special_spring2019Mage.png
new file mode 100644
index 0000000000..8f5f05d399
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/weapon_special_spring2019Mage.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/weapon_special_spring2019Rogue.png b/website/raw_sprites/spritesmith/gear/events/spring/weapon_special_spring2019Rogue.png
new file mode 100644
index 0000000000..703b9e733b
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/weapon_special_spring2019Rogue.png differ
diff --git a/website/raw_sprites/spritesmith/gear/events/spring/weapon_special_spring2019Warrior.png b/website/raw_sprites/spritesmith/gear/events/spring/weapon_special_spring2019Warrior.png
new file mode 100644
index 0000000000..2930378bb2
Binary files /dev/null and b/website/raw_sprites/spritesmith/gear/events/spring/weapon_special_spring2019Warrior.png differ
diff --git a/website/raw_sprites/spritesmith/npcs/npc_bailey.png b/website/raw_sprites/spritesmith/npcs/npc_bailey.png
index d5940b986b..0fe6763311 100644
Binary files a/website/raw_sprites/spritesmith/npcs/npc_bailey.png and b/website/raw_sprites/spritesmith/npcs/npc_bailey.png differ
diff --git a/website/raw_sprites/spritesmith/npcs/npc_justin.png b/website/raw_sprites/spritesmith/npcs/npc_justin.png
index 08ba7025c2..9365bc12c9 100644
Binary files a/website/raw_sprites/spritesmith/npcs/npc_justin.png and b/website/raw_sprites/spritesmith/npcs/npc_justin.png differ
diff --git a/website/raw_sprites/spritesmith/npcs/npc_matt.png b/website/raw_sprites/spritesmith/npcs/npc_matt.png
index 2531f1084b..e129d6e43e 100644
Binary files a/website/raw_sprites/spritesmith/npcs/npc_matt.png and b/website/raw_sprites/spritesmith/npcs/npc_matt.png differ
diff --git a/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Base.png b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Base.png
new file mode 100644
index 0000000000..0f06841633
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Base.png differ
diff --git a/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_CottonCandyBlue.png b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_CottonCandyBlue.png
new file mode 100644
index 0000000000..6dd3df0dcf
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_CottonCandyBlue.png differ
diff --git a/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_CottonCandyPink.png b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_CottonCandyPink.png
new file mode 100644
index 0000000000..210ac974fc
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_CottonCandyPink.png differ
diff --git a/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Desert.png b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Desert.png
new file mode 100644
index 0000000000..dd2ab6bae1
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Desert.png differ
diff --git a/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Golden.png b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Golden.png
new file mode 100644
index 0000000000..bff877d248
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Golden.png differ
diff --git a/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Red.png b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Red.png
new file mode 100644
index 0000000000..bfc2148a4c
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Red.png differ
diff --git a/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Shade.png b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Shade.png
new file mode 100644
index 0000000000..f32497452e
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Shade.png differ
diff --git a/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Skeleton.png b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Skeleton.png
new file mode 100644
index 0000000000..c2fcc6a3f0
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Skeleton.png differ
diff --git a/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_White.png b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_White.png
new file mode 100644
index 0000000000..c789272368
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_White.png differ
diff --git a/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Zombie.png b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Zombie.png
new file mode 100644
index 0000000000..454cdb0987
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/food/Pet_Food_Pie_Zombie.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_BearCub-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_BearCub-Celestial.png
new file mode 100644
index 0000000000..47c3664013
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_BearCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_Cactus-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_Cactus-Celestial.png
new file mode 100644
index 0000000000..fcb2637cbc
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_Cactus-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_Dragon-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_Dragon-Celestial.png
new file mode 100644
index 0000000000..84552d746f
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_Dragon-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_FlyingPig-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_FlyingPig-Celestial.png
new file mode 100644
index 0000000000..58be9597e5
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_FlyingPig-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_Fox-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_Fox-Celestial.png
new file mode 100644
index 0000000000..094046af0b
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_Fox-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_LionCub-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_LionCub-Celestial.png
new file mode 100644
index 0000000000..214cbc66bc
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_LionCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_PandaCub-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_PandaCub-Celestial.png
new file mode 100644
index 0000000000..b53aa246bb
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_PandaCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_TigerCub-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_TigerCub-Celestial.png
new file mode 100644
index 0000000000..4dd4abb5d7
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_TigerCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_Wolf-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_Wolf-Celestial.png
new file mode 100644
index 0000000000..2a291a184f
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/body/Mount_Body_Wolf-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_BearCub-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_BearCub-Celestial.png
new file mode 100644
index 0000000000..1d7b43129f
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_BearCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_Cactus-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_Cactus-Celestial.png
new file mode 100644
index 0000000000..a59b6d027d
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_Cactus-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_Dragon-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_Dragon-Celestial.png
new file mode 100644
index 0000000000..3ab1659fa6
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_Dragon-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_FlyingPig-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_FlyingPig-Celestial.png
new file mode 100644
index 0000000000..3e83af730d
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_FlyingPig-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_Fox-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_Fox-Celestial.png
new file mode 100644
index 0000000000..973cf0d683
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_Fox-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_LionCub-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_LionCub-Celestial.png
new file mode 100644
index 0000000000..70045dc34a
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_LionCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_PandaCub-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_PandaCub-Celestial.png
new file mode 100644
index 0000000000..9b0e651f4a
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_PandaCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_TigerCub-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_TigerCub-Celestial.png
new file mode 100644
index 0000000000..e0b1231f0f
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_TigerCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_Wolf-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_Wolf-Celestial.png
new file mode 100644
index 0000000000..9fe1384430
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/head/Mount_Head_Wolf-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_BearCub-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_BearCub-Celestial.png
new file mode 100644
index 0000000000..6f4f01b571
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_BearCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_Cactus-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_Cactus-Celestial.png
new file mode 100644
index 0000000000..0b4e41cf7c
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_Cactus-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_Dragon-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_Dragon-Celestial.png
new file mode 100644
index 0000000000..26c7b10b86
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_Dragon-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_FlyingPig-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_FlyingPig-Celestial.png
new file mode 100644
index 0000000000..f1b7f117db
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_FlyingPig-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_Fox-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_Fox-Celestial.png
new file mode 100644
index 0000000000..3933c00c7f
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_Fox-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_LionCub-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_LionCub-Celestial.png
new file mode 100644
index 0000000000..b299ba2f9c
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_LionCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_PandaCub-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_PandaCub-Celestial.png
new file mode 100644
index 0000000000..62ade18d5c
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_PandaCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_TigerCub-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_TigerCub-Celestial.png
new file mode 100644
index 0000000000..258f007841
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_TigerCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_Wolf-Celestial.png b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_Wolf-Celestial.png
new file mode 100644
index 0000000000..2d96310226
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/mounts/icon/Mount_Icon_Wolf-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/pets/Pet-BearCub-Celestial.png b/website/raw_sprites/spritesmith/stable/pets/Pet-BearCub-Celestial.png
new file mode 100644
index 0000000000..2f2c081bb5
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/pets/Pet-BearCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/pets/Pet-Cactus-Celestial.png b/website/raw_sprites/spritesmith/stable/pets/Pet-Cactus-Celestial.png
new file mode 100644
index 0000000000..5b87d14c30
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/pets/Pet-Cactus-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/pets/Pet-Dragon-Celestial.png b/website/raw_sprites/spritesmith/stable/pets/Pet-Dragon-Celestial.png
new file mode 100644
index 0000000000..0ad41e12c9
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/pets/Pet-Dragon-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/pets/Pet-FlyingPig-Celestial.png b/website/raw_sprites/spritesmith/stable/pets/Pet-FlyingPig-Celestial.png
new file mode 100644
index 0000000000..3036fa6a3a
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/pets/Pet-FlyingPig-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/pets/Pet-Fox-Celestial.png b/website/raw_sprites/spritesmith/stable/pets/Pet-Fox-Celestial.png
new file mode 100644
index 0000000000..1fdc2534de
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/pets/Pet-Fox-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/pets/Pet-LionCub-Celestial.png b/website/raw_sprites/spritesmith/stable/pets/Pet-LionCub-Celestial.png
new file mode 100644
index 0000000000..f628ccfeab
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/pets/Pet-LionCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/pets/Pet-PandaCub-Celestial.png b/website/raw_sprites/spritesmith/stable/pets/Pet-PandaCub-Celestial.png
new file mode 100644
index 0000000000..3c4a27f55c
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/pets/Pet-PandaCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/pets/Pet-TigerCub-Celestial.png b/website/raw_sprites/spritesmith/stable/pets/Pet-TigerCub-Celestial.png
new file mode 100644
index 0000000000..62c140325f
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/pets/Pet-TigerCub-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/pets/Pet-Wolf-Celestial.png b/website/raw_sprites/spritesmith/stable/pets/Pet-Wolf-Celestial.png
new file mode 100644
index 0000000000..820659c29a
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/pets/Pet-Wolf-Celestial.png differ
diff --git a/website/raw_sprites/spritesmith/stable/potions/Pet_HatchingPotion_Celestial.png b/website/raw_sprites/spritesmith/stable/potions/Pet_HatchingPotion_Celestial.png
new file mode 100644
index 0000000000..1de484d48d
Binary files /dev/null and b/website/raw_sprites/spritesmith/stable/potions/Pet_HatchingPotion_Celestial.png differ
diff --git a/website/raw_sprites/spritesmith_large/promo_armoire_backgrounds_201902.png b/website/raw_sprites/spritesmith_large/promo_armoire_backgrounds_201902.png
deleted file mode 100644
index 1ee1cbc343..0000000000
Binary files a/website/raw_sprites/spritesmith_large/promo_armoire_backgrounds_201902.png and /dev/null differ
diff --git a/website/raw_sprites/spritesmith_large/promo_armoire_backgrounds_201903.png b/website/raw_sprites/spritesmith_large/promo_armoire_backgrounds_201903.png
new file mode 100644
index 0000000000..e2971ff3dc
Binary files /dev/null and b/website/raw_sprites/spritesmith_large/promo_armoire_backgrounds_201903.png differ
diff --git a/website/raw_sprites/spritesmith_large/promo_beffymaroo_wondercon.png b/website/raw_sprites/spritesmith_large/promo_beffymaroo_wondercon.png
new file mode 100644
index 0000000000..a65532938a
Binary files /dev/null and b/website/raw_sprites/spritesmith_large/promo_beffymaroo_wondercon.png differ
diff --git a/website/raw_sprites/spritesmith_large/promo_celestial_rainbow_potions.png b/website/raw_sprites/spritesmith_large/promo_celestial_rainbow_potions.png
new file mode 100644
index 0000000000..913d48a27d
Binary files /dev/null and b/website/raw_sprites/spritesmith_large/promo_celestial_rainbow_potions.png differ
diff --git a/website/raw_sprites/spritesmith_large/promo_classes_spring2019.png b/website/raw_sprites/spritesmith_large/promo_classes_spring2019.png
new file mode 100644
index 0000000000..18678fb1e2
Binary files /dev/null and b/website/raw_sprites/spritesmith_large/promo_classes_spring2019.png differ
diff --git a/website/raw_sprites/spritesmith_large/promo_egg_hunt.png b/website/raw_sprites/spritesmith_large/promo_egg_hunt.png
new file mode 100644
index 0000000000..cafc89723b
Binary files /dev/null and b/website/raw_sprites/spritesmith_large/promo_egg_hunt.png differ
diff --git a/website/raw_sprites/spritesmith_large/promo_mystery_201902.png b/website/raw_sprites/spritesmith_large/promo_mystery_201902.png
deleted file mode 100644
index 9940133afa..0000000000
Binary files a/website/raw_sprites/spritesmith_large/promo_mystery_201902.png and /dev/null differ
diff --git a/website/raw_sprites/spritesmith_large/promo_mystery_201903.png b/website/raw_sprites/spritesmith_large/promo_mystery_201903.png
new file mode 100644
index 0000000000..f74175480e
Binary files /dev/null and b/website/raw_sprites/spritesmith_large/promo_mystery_201903.png differ
diff --git a/website/raw_sprites/spritesmith_large/promo_mythical_marvels_bundle.png b/website/raw_sprites/spritesmith_large/promo_mythical_marvels_bundle.png
deleted file mode 100644
index b49fc1fc41..0000000000
Binary files a/website/raw_sprites/spritesmith_large/promo_mythical_marvels_bundle.png and /dev/null differ
diff --git a/website/raw_sprites/spritesmith_large/promo_seasonalshop_spring.png b/website/raw_sprites/spritesmith_large/promo_seasonalshop_spring.png
new file mode 100644
index 0000000000..e1f811893e
Binary files /dev/null and b/website/raw_sprites/spritesmith_large/promo_seasonalshop_spring.png differ
diff --git a/website/raw_sprites/spritesmith_large/scene_cooking.png b/website/raw_sprites/spritesmith_large/scene_cooking.png
deleted file mode 100644
index c4a67167cb..0000000000
Binary files a/website/raw_sprites/spritesmith_large/scene_cooking.png and /dev/null differ
diff --git a/website/raw_sprites/spritesmith_large/scene_dailies.png b/website/raw_sprites/spritesmith_large/scene_dailies.png
new file mode 100644
index 0000000000..6c6e0c9195
Binary files /dev/null and b/website/raw_sprites/spritesmith_large/scene_dailies.png differ
diff --git a/website/raw_sprites/spritesmith_large/scene_tavern.png b/website/raw_sprites/spritesmith_large/scene_tavern.png
new file mode 100644
index 0000000000..63078b9e28
Binary files /dev/null and b/website/raw_sprites/spritesmith_large/scene_tavern.png differ
diff --git a/website/raw_sprites/spritesmith_large/scene_todos.png b/website/raw_sprites/spritesmith_large/scene_todos.png
new file mode 100644
index 0000000000..b8c52d399d
Binary files /dev/null and b/website/raw_sprites/spritesmith_large/scene_todos.png differ
diff --git a/website/server/controllers/api-v3/auth.js b/website/server/controllers/api-v3/auth.js
index 37d7f7e451..84c5485fe6 100644
--- a/website/server/controllers/api-v3/auth.js
+++ b/website/server/controllers/api-v3/auth.js
@@ -98,6 +98,9 @@ api.loginLocal = {
// load the entire user because we may have to save it to convert the password to bcrypt
let user = await User.findOne(login).exec();
+ // if user is using social login, then user will not have a hashed_password stored
+ if (!user.auth.local.hashed_password) throw new NotAuthorized(res.t('invalidLoginCredentialsLong'));
+
let isValidPassword;
if (!user) {
diff --git a/website/server/controllers/api-v3/challenges.js b/website/server/controllers/api-v3/challenges.js
index 37f9cac929..82bb301169 100644
--- a/website/server/controllers/api-v3/challenges.js
+++ b/website/server/controllers/api-v3/challenges.js
@@ -49,7 +49,7 @@ let api = {};
* @apiSuccess {String} challenge.name Full name of challenge.
* @apiSuccess {String} challenge.shortName A shortened name for the challenge, to be used as a tag.
* @apiSuccess {Object} challenge.leader User details of challenge leader.
- * @apiSuccess {UUID} challenge.leader._id User id of challenge leader.
+ * @apiSuccess {UUID} challenge.leader._id User ID of challenge leader.
* @apiSuccess {Object} challenge.leader.profile Profile information of leader.
* @apiSuccess {Object} challenge.leader.profile.name Display Name of leader.
* @apiSuccess {String} challenge.updatedAt Timestamp of last update.
diff --git a/website/server/controllers/api-v3/chat.js b/website/server/controllers/api-v3/chat.js
index 3c7f5468b0..84703ac0da 100644
--- a/website/server/controllers/api-v3/chat.js
+++ b/website/server/controllers/api-v3/chat.js
@@ -30,12 +30,17 @@ const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map((email)
/**
* @apiDefine GroupIdRequired
- * @apiError (404) {badRequest} groupIdRequired A group ID is required
+ * @apiError (400) {badRequest} groupIdRequired A group ID is required
*/
/**
* @apiDefine ChatIdRequired
- * @apiError (404) {badRequest} chatIdRequired A chat ID is required
+ * @apiError (400) {badRequest} chatIdRequired A chat ID is required
+ */
+
+/**
+ * @apiDefine MessageIdRequired
+ * @apiError (400) {badRequest} messageIdRequired A message ID is required
*/
let api = {};
@@ -246,7 +251,7 @@ api.likeChat = {
let groupId = req.params.groupId;
req.checkParams('groupId', apiError('groupIdRequired')).notEmpty();
- req.checkParams('chatId', res.t('chatIdRequired')).notEmpty();
+ req.checkParams('chatId', apiError('chatIdRequired')).notEmpty();
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
@@ -285,7 +290,7 @@ api.likeChat = {
* @apiSuccess {Object} data.likes The likes of the message
* @apiSuccess {Object} data.flags The flags of the message
* @apiSuccess {Number} data.flagCount The number of flags the message has
- * @apiSuccess {UUID} data.uuid The user id of the author of the message
+ * @apiSuccess {UUID} data.uuid The User ID of the author of the message
* @apiSuccess {String} data.user The username of the author of the message
*
* @apiUse GroupNotFound
@@ -334,7 +339,7 @@ api.clearChatFlags = {
let chatId = req.params.chatId;
req.checkParams('groupId', apiError('groupIdRequired')).notEmpty();
- req.checkParams('chatId', res.t('chatIdRequired')).notEmpty();
+ req.checkParams('chatId', apiError('chatIdRequired')).notEmpty();
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
@@ -470,7 +475,7 @@ api.deleteChat = {
let chatId = req.params.chatId;
req.checkParams('groupId', apiError('groupIdRequired')).notEmpty();
- req.checkParams('chatId', res.t('chatIdRequired')).notEmpty();
+ req.checkParams('chatId', apiError('chatIdRequired')).notEmpty();
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
diff --git a/website/server/controllers/api-v3/groups.js b/website/server/controllers/api-v3/groups.js
index 166f82e430..9eef5108e9 100644
--- a/website/server/controllers/api-v3/groups.js
+++ b/website/server/controllers/api-v3/groups.js
@@ -941,11 +941,11 @@ api.removeGroupMember = {
* {"name": "User2", "email": "user-2@example.com"}
* ]
* }
- * @apiParamExample {json} User Ids
+ * @apiParamExample {json} User IDs
* {
* "uuids": ["user-id-of-existing-user", "user-id-of-another-existing-user"]
* }
- * @apiParamExample {json} User Ids and Emails
+ * @apiParamExample {json} User IDs and Emails
* {
* "emails": [
* {"email": "user-1@example.com"},
@@ -955,7 +955,7 @@ api.removeGroupMember = {
* }
*
* @apiSuccess {Array} data The invites
- * @apiSuccess {Object} data[0] If the invitation was a user id, you'll receive back an object. You'll receive one Object for each succesful user id invite.
+ * @apiSuccess {Object} data[0] If the invitation was a User ID, you'll receive back an object. You'll receive one Object for each succesful User ID invite.
* @apiSuccess {String} data[1] If the invitation was an email, you'll receive back the email. You'll receive one String for each successful email invite.
*
* @apiSuccessExample {json} Successful Response with Emails
@@ -966,13 +966,13 @@ api.removeGroupMember = {
* ]
* }
*
- * @apiSuccessExample {json} Successful Response with User Id
+ * @apiSuccessExample {json} Successful Response with User ID
* {
* "data": [
* { id: 'the-id-of-the-invited-user', name: 'The group name', inviter: 'your-user-id' }
* ]
* }
- * @apiSuccessExample {json} Successful Response with User Ids and Emails
+ * @apiSuccessExample {json} Successful Response with User IDs and Emails
* {
* "data": [
* "user-1@example.com",
@@ -987,9 +987,9 @@ api.removeGroupMember = {
* param `Array`.
* @apiError (400) {BadRequest} UuidOrEmailOnly The `emails` and `uuids` params were both missing and/or a
* key other than `emails` or `uuids` was provided in the body param.
- * @apiError (400) {BadRequest} CannotInviteSelf User id or email of invitee matches that of the inviter.
+ * @apiError (400) {BadRequest} CannotInviteSelf User ID or email of invitee matches that of the inviter.
* @apiError (400) {BadRequest} MustBeArray The `uuids` or `emails` body param was not an array.
- * @apiError (400) {BadRequest} TooManyInvites A max of 100 invites (combined emails and user ids) can
+ * @apiError (400) {BadRequest} TooManyInvites A max of 100 invites (combined emails and User IDs) can
* be sent out at a time.
* @apiError (400) {BadRequest} ExceedsMembersLimit A max of 30 members can join a party.
*
diff --git a/website/server/controllers/api-v3/hall.js b/website/server/controllers/api-v3/hall.js
index e42adb80c5..2073962af0 100644
--- a/website/server/controllers/api-v3/hall.js
+++ b/website/server/controllers/api-v3/hall.js
@@ -175,7 +175,7 @@ api.getHero = {
if (validator.isUUID(heroId)) {
query = {_id: heroId};
} else {
- query = {'auth.local.username': heroId};
+ query = {'auth.local.lowerCaseUsername': heroId.toLowerCase()};
}
const hero = await User
@@ -197,7 +197,7 @@ const gemsPerTier = {1: 3, 2: 3, 3: 3, 4: 4, 5: 4, 6: 4, 7: 4, 8: 0, 9: 0};
/**
* @api {put} /api/v3/hall/heroes/:heroId Update any user ("hero")
- * @apiParam (Path) {UUID} heroId user ID
+ * @apiParam (Path) {UUID} heroId User ID
* @apiName UpdateHero
* @apiGroup Hall
* @apiPermission Admin
diff --git a/website/server/controllers/api-v3/news.js b/website/server/controllers/api-v3/news.js
index f5ce1102ff..fe9843d2ce 100644
--- a/website/server/controllers/api-v3/news.js
+++ b/website/server/controllers/api-v3/news.js
@@ -3,7 +3,7 @@ import { authWithHeaders } from '../../middlewares/auth';
let api = {};
// @TODO export this const, cannot export it from here because only routes are exported from controllers
-const LAST_ANNOUNCEMENT_TITLE = 'FEBRUARY SUBSCRIBER MYSTERY ITEMS! PLUS USE CASE SPOTLIGHT';
+const LAST_ANNOUNCEMENT_TITLE = 'LAST CHANCE FOR MARCH SUBSCRIBER GEAR! AND THE APRIL FOOL PROMISES HEʼLL BEHAVE THIS YEAR';
const worldDmg = { // @TODO
bailey: false,
};
@@ -30,20 +30,22 @@ api.getNews = {
${res.t('newStuff')}
- 2/25/2019 - ${LAST_ANNOUNCEMENT_TITLE}
+ 3/29/2019 - ${LAST_ANNOUNCEMENT_TITLE}
-
- February Subscriber Items Revealed!
- The February Subscriber Items have been revealed: the Cryptic Crush Item Set! You only have four days to receive the item set when you subscribe. If you're already an active subscriber, reload the site and then head to Inventory > Items to claim your gear!
- Subscribers also receive the ability to buy Gems with Gold -- the longer you subscribe, the more Gems you can buy per month! There are other perks as well, such as longer access to uncompressed data and a cute Jackalope pet. Best of all, subscriptions let us keep Habitica running. Thank you very much for your support -- it means a lot to us.
+
+ Last Chance for March Subscriber Gear
+ Reminder: this weekend is your final chance to subscribe and receive the Egg-squisite Armor Set! Subscribing also lets you buy Gems with Gold. The longer your subscription, the more Gems you get!
+ Thanks so much for your support! You help keep Habitica running.
by Beffymaroo
-
- Use Case Spotlight: Household Task Sharing
- We've posted a new Use Case Spotlight on the Habitica blog! It features a number of great suggestions for using Habitica's task system to manage sharing domestic chores. These suggestions were submitted by Habiticans in the Use Case Spotlights Guild.
- Plus, we're collecting user submissions for the next Use Case Spotlight! How do you use Habitica to set up routines? We’ll be featuring player-submitted examples in Use Case Spotlights on the Habitica Blog next month, so post your suggestions in the Use Case Spotlight Guild now. We look forward to learning more about how you use Habitica to improve your life and get things done!
- by shanaqui
+ The April Fool Stops By the Tavern...
+ As March in Habitica comes to a close, everyone is wondering what the ever-impish Master of Rogues, the April Fool, might have in store for his favorite holiday.
+ He's stopped by the Tavern today, ostensibly for lunch, but he seems keen to put everyone at ease about the possibility of shenanigans in the near future.
+ "I've re-committed myself to health!" he says, happily munching on a crisp, ripe pear. "I'm too busy brushing up on nutrition to possibly pull a prank! If anything I'd rather just help every Habitican get more healthy food into their routines."
+ Beffymaroo smiles and leans to whisper to Piyo and SabreCat, on the next bench.
+ "Given his track record over the years, I'd say the chance he's going to behave himself this year is about as good as the chance of artichokes falling from the sky."
+ Perhaps you should check back when April 1st rolls around to see what's in store…
`,
});
diff --git a/website/server/controllers/api-v4/members.js b/website/server/controllers/api-v4/members.js
new file mode 100644
index 0000000000..57a1b39ddd
--- /dev/null
+++ b/website/server/controllers/api-v4/members.js
@@ -0,0 +1,43 @@
+import { authWithHeaders } from '../../middlewares/auth';
+import { chatReporterFactory } from '../../libs/chatReporting/chatReporterFactory';
+
+let api = {};
+
+/**
+ * @api {post} /api/v4/members/flag-private-message/:messageId Flag a private message
+ * @apiDescription An email and slack message are sent to the moderators about every flagged message.
+ * @apiName FlagPrivateMessage
+ * @apiGroup Member
+ *
+ * @apiParam (Path) {UUID} messageId The private message id
+ *
+ * @apiSuccess {Object} data The flagged private message
+ * @apiSuccess {UUID} data.id The id of the message
+ * @apiSuccess {String} data.text The text of the message
+ * @apiSuccess {Number} data.timestamp The timestamp of the message in milliseconds
+ * @apiSuccess {Object} data.likes The likes of the message (always an empty object)
+ * @apiSuccess {Object} data.flags The flags of the message
+ * @apiSuccess {Number} data.flagCount The number of flags the message has
+ * @apiSuccess {UUID} data.uuid The User ID of the author of the message, or of the recipient if `sent` is true
+ * @apiSuccess {String} data.user The Display Name of the author of the message, or of the recipient if `sent` is true
+ * @apiSuccess {String} data.username The Username of the author of the message, or of the recipient if `sent` is true
+ *
+ * @apiUse MessageNotFound
+ * @apiUse MessageIdRequired
+ * @apiError (400) {BadRequest} messageGroupChatFlagAlreadyReported You have already reported this message
+ */
+api.flagPrivateMessage = {
+ method: 'POST',
+ url: '/members/flag-private-message/:messageId',
+ middlewares: [authWithHeaders()],
+ async handler (req, res) {
+ const chatReporter = chatReporterFactory('Inbox', req, res);
+ const message = await chatReporter.flag();
+ res.respond(200, {
+ ok: true,
+ message,
+ });
+ },
+};
+
+module.exports = api;
diff --git a/website/server/libs/analyticsService.js b/website/server/libs/analyticsService.js
index f96e7ca228..38da9c6a9c 100644
--- a/website/server/libs/analyticsService.js
+++ b/website/server/libs/analyticsService.js
@@ -10,11 +10,11 @@ import {
} from 'lodash';
import { content as Content } from '../../common';
-const AMPLIUDE_TOKEN = nconf.get('AMPLITUDE_KEY');
+const AMPLITUDE_TOKEN = nconf.get('AMPLITUDE_KEY');
const GA_TOKEN = nconf.get('GA_ID');
const GA_POSSIBLE_LABELS = ['gaLabel', 'itemKey'];
const GA_POSSIBLE_VALUES = ['gaValue', 'gemCost', 'goldCost'];
-const AMPLITUDE_PROPERTIES_TO_SCRUB = ['uuid', 'user', 'purchaseValue', 'gaLabel', 'gaValue', 'headers'];
+const AMPLITUDE_PROPERTIES_TO_SCRUB = ['uuid', 'user', 'purchaseValue', 'gaLabel', 'gaValue', 'headers', 'registeredThrough'];
const PLATFORM_MAP = Object.freeze({
'habitica-web': 'Web',
@@ -23,7 +23,7 @@ const PLATFORM_MAP = Object.freeze({
});
let amplitude;
-if (AMPLIUDE_TOKEN) amplitude = new Amplitude(AMPLIUDE_TOKEN);
+if (AMPLITUDE_TOKEN) amplitude = new Amplitude(AMPLITUDE_TOKEN);
let ga = googleAnalytics(GA_TOKEN);
@@ -97,10 +97,6 @@ let _formatUserData = (user) => {
properties.ABtests = toArray(user._ABtests);
}
- if (user.registeredThrough) {
- properties.registeredPlatform = user.registeredThrough;
- }
-
if (user.loginIncentives) {
properties.loginIncentives = user.loginIncentives;
}
@@ -271,11 +267,24 @@ let _sendPurchaseDataToGoogle = (data) => {
});
};
+let _setOnce = (data) => {
+ return amplitude.identify({
+ user_properties: {
+ $setOnce: data,
+ },
+ });
+};
+
function track (eventType, data) {
- return Promise.all([
+ let promises = [
_sendDataToAmplitude(eventType, data),
_sendDataToGoogle(eventType, data),
- ]);
+ ];
+ if (data.user && data.user.registeredThrough) {
+ promises.push(_setOnce({registeredPlatform: data.user.registeredThrough}));
+ }
+
+ return Promise.all(promises);
}
function trackPurchase (data) {
diff --git a/website/server/libs/auth/social.js b/website/server/libs/auth/social.js
index 882554818f..8ac649ab71 100644
--- a/website/server/libs/auth/social.js
+++ b/website/server/libs/auth/social.js
@@ -88,7 +88,13 @@ async function loginSocial (req, res) {
.remove({email: savedUser.auth[network].emails[0].value.toLowerCase()})
.exec()
.then(() => {
- if (!existingUser) sendTxnEmail(savedUser, 'welcome');
+ if (!existingUser) {
+ if (savedUser._ABtests && savedUser._ABtests.welcomeEmailSplit) {
+ sendTxnEmail(savedUser, savedUser._ABtests.welcomeEmailSplit);
+ } else {
+ sendTxnEmail(savedUser, 'welcome');
+ }
+ }
}); // eslint-disable-line max-nested-callbacks
}
diff --git a/website/server/libs/chatReporting/chatReporter.js b/website/server/libs/chatReporting/chatReporter.js
index 148dbd4696..d55a95c353 100644
--- a/website/server/libs/chatReporting/chatReporter.js
+++ b/website/server/libs/chatReporting/chatReporter.js
@@ -1,6 +1,4 @@
-import {
-} from '../errors';
-import { getUserInfo } from '../email';
+import { getGroupUrl, getUserInfo } from '../email';
import { getAuthorEmailFromMessage } from '../chat';
export default class ChatReporter {
@@ -11,25 +9,51 @@ export default class ChatReporter {
async validate () {}
- async notify (group, message) {
- const reporterEmailContent = getUserInfo(this.user, ['email']).email;
- this.authorEmail = await getAuthorEmailFromMessage(message);
- this.emailVariables = [
+ async getMessageVariables (group, message) {
+ const reporterEmail = getUserInfo(this.user, ['email']).email;
+
+ const authorVariables = await this.getAuthorVariables(message);
+ const groupUrl = getGroupUrl(group);
+
+ return [
{name: 'MESSAGE_TIME', content: (new Date(message.timestamp)).toString()},
{name: 'MESSAGE_TEXT', content: message.text},
- {name: 'REPORTER_USERNAME', content: this.user.profile.name},
+ {name: 'REPORTER_DISPLAY_NAME', content: this.user.profile.name},
+ {name: 'REPORTER_USERNAME', content: this.user.auth.local.username},
{name: 'REPORTER_UUID', content: this.user._id},
- {name: 'REPORTER_EMAIL', content: reporterEmailContent},
+ {name: 'REPORTER_EMAIL', content: reporterEmail},
{name: 'REPORTER_MODAL_URL', content: `/static/front/#?memberId=${this.user._id}`},
- {name: 'AUTHOR_USERNAME', content: message.user},
- {name: 'AUTHOR_UUID', content: message.uuid},
- {name: 'AUTHOR_EMAIL', content: this.authorEmail},
- {name: 'AUTHOR_MODAL_URL', content: `/static/front/#?memberId=${message.uuid}`},
+ ...authorVariables,
+
+ {name: 'GROUP_NAME', content: group.name},
+ {name: 'GROUP_TYPE', content: group.type},
+ {name: 'GROUP_ID', content: group._id},
+ {name: 'GROUP_URL', content: groupUrl || 'N/A'},
];
}
+ createGenericAuthorVariables (prefix, {user, username, uuid, email}) {
+ return [
+ {name: `${prefix}_DISPLAY_NAME`, content: user},
+ {name: `${prefix}_USERNAME`, content: username},
+ {name: `${prefix}_UUID`, content: uuid},
+ {name: `${prefix}_EMAIL`, content: email},
+ {name: `${prefix}_MODAL_URL`, content: `/static/front/#?memberId=${uuid}`},
+ ];
+ }
+
+ async getAuthorVariables (message) {
+ this.authorEmail = await getAuthorEmailFromMessage(message);
+ return this.createGenericAuthorVariables('AUTHOR', {
+ user: message.user,
+ username: message.username,
+ uuid: message.uuid,
+ email: this.authorEmail,
+ });
+ }
+
async flag () {
throw new Error('Flag must be implemented');
}
diff --git a/website/server/libs/chatReporting/chatReporterFactory.js b/website/server/libs/chatReporting/chatReporterFactory.js
index cd97da6793..50ac7e9e89 100644
--- a/website/server/libs/chatReporting/chatReporterFactory.js
+++ b/website/server/libs/chatReporting/chatReporterFactory.js
@@ -1,11 +1,10 @@
import GroupChatReporter from './groupChatReporter';
-// import InboxChatReporter from './inboxChatReporter';
+import InboxChatReporter from './inboxChatReporter';
export function chatReporterFactory (type, req, res) {
if (type === 'Group') {
return new GroupChatReporter(req, res);
+ } else if (type === 'Inbox') {
+ return new InboxChatReporter(req, res);
}
- // else if (type === 'Inbox') {
- // return new InboxChatReporter(req, res);
- // }
}
diff --git a/website/server/libs/chatReporting/groupChatReporter.js b/website/server/libs/chatReporting/groupChatReporter.js
index ff625bfb8a..b41f142579 100644
--- a/website/server/libs/chatReporting/groupChatReporter.js
+++ b/website/server/libs/chatReporting/groupChatReporter.js
@@ -6,7 +6,7 @@ import {
BadRequest,
NotFound,
} from '../errors';
-import { getGroupUrl, sendTxn } from '../email';
+import { sendTxn } from '../email';
import slack from '../slack';
import { model as Group } from '../../models/group';
import { chatModel as Chat } from '../../models/message';
@@ -28,7 +28,7 @@ export default class GroupChatReporter extends ChatReporter {
async validate () {
this.req.checkParams('groupId', apiError('groupIdRequired')).notEmpty();
- this.req.checkParams('chatId', this.res.t('chatIdRequired')).notEmpty();
+ this.req.checkParams('chatId', apiError('chatIdRequired')).notEmpty();
let validationErrors = this.req.validationErrors();
if (validationErrors) throw validationErrors;
@@ -50,16 +50,12 @@ export default class GroupChatReporter extends ChatReporter {
}
async notify (group, message, userComment, automatedComment = '') {
- await super.notify(group, message);
-
- const groupUrl = getGroupUrl(group);
- sendTxn(FLAG_REPORT_EMAILS, 'flag-report-to-mods-with-comments', this.emailVariables.concat([
- {name: 'GROUP_NAME', content: group.name},
- {name: 'GROUP_TYPE', content: group.type},
- {name: 'GROUP_ID', content: group._id},
- {name: 'GROUP_URL', content: groupUrl},
+ let emailVariables = await this.getMessageVariables(group, message);
+ emailVariables = emailVariables.concat([
{name: 'REPORTER_COMMENT', content: userComment || ''},
- ]));
+ ]);
+
+ sendTxn(FLAG_REPORT_EMAILS, 'flag-report-to-mods-with-comments', emailVariables);
slack.sendFlagNotification({
authorEmail: this.authorEmail,
diff --git a/website/server/libs/chatReporting/inboxChatReporter.js b/website/server/libs/chatReporting/inboxChatReporter.js
new file mode 100644
index 0000000000..207b570fd3
--- /dev/null
+++ b/website/server/libs/chatReporting/inboxChatReporter.js
@@ -0,0 +1,129 @@
+import nconf from 'nconf';
+import { model as User } from '../../models/user';
+
+import ChatReporter from './chatReporter';
+import {
+ BadRequest,
+} from '../errors';
+import { getUserInfo, sendTxn} from '../email';
+import slack from '../slack';
+import apiError from '../apiError';
+
+import * as inboxLib from '../inbox';
+import {getAuthorEmailFromMessage} from '../chat';
+
+const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map((email) => {
+ return { email, canSend: true };
+});
+
+export default class InboxChatReporter extends ChatReporter {
+ constructor (req, res) {
+ super(req, res);
+
+ this.user = res.locals.user;
+ this.inboxUser = res.locals.user;
+ }
+
+ async validate () {
+ this.req.checkParams('messageId', apiError('messageIdRequired')).notEmpty();
+
+ let validationErrors = this.req.validationErrors();
+ if (validationErrors) throw validationErrors;
+
+ if (this.user.contributor.admin && this.req.query.userId) {
+ this.inboxUser = await User.findOne({_id: this.req.query.userId});
+ }
+
+ const message = await inboxLib.getUserInboxMessage(this.inboxUser, this.req.params.messageId);
+ if (!message) throw new BadRequest(this.res.t('messageGroupChatNotFound'));
+
+ const userComment = this.req.body.comment;
+
+ return {message, userComment};
+ }
+
+ async notify (message, userComment) {
+ const group = {
+ type: 'private messages',
+ name: 'N/A',
+ _id: 'N/A',
+ };
+
+ let emailVariables = await this.getMessageVariables(group, message);
+ emailVariables = emailVariables.concat([
+ {name: 'REPORTER_COMMENT', content: userComment || ''},
+ ]);
+
+ sendTxn(FLAG_REPORT_EMAILS, 'flag-report-to-mods-with-comments', emailVariables);
+
+ slack.sendInboxFlagNotification({
+ authorEmail: this.authorEmail,
+ flagger: this.user,
+ message,
+ userComment,
+ });
+ }
+
+ async getAuthorVariables (message) {
+ const messageUser = {
+ user: message.user,
+ username: message.username,
+ uuid: message.uuid,
+ email: await getAuthorEmailFromMessage(message),
+ };
+
+ const reporter = {
+ user: this.user.profile.name,
+ username: this.user.auth.local.username,
+ uuid: this.user._id,
+ email: getUserInfo(this.user, ['email']).email,
+ };
+
+ // if message.sent, the reporter is the author of this message
+ const sendingUser = message.sent ? reporter : messageUser;
+ const recipient = message.sent ? messageUser : reporter;
+
+ this.authorEmail = sendingUser.email;
+
+ return [
+ ...this.createGenericAuthorVariables('AUTHOR', sendingUser),
+ ...this.createGenericAuthorVariables('RECIPIENT', recipient),
+ ];
+ }
+
+ updateMessageAndSave (message, ...changedFields) {
+ for (const changedField of changedFields) {
+ message.markModified(changedField);
+ }
+
+ return message.save();
+ }
+
+ flagInboxMessage (message) {
+ // Log user ids that have flagged the message
+ if (!message.flags) message.flags = {};
+ // TODO fix error type
+ if (message.flags[this.user._id] && !this.user.contributor.admin) {
+ throw new BadRequest(this.res.t('messageGroupChatFlagAlreadyReported'));
+ }
+
+ message.flags[this.user._id] = true;
+ message.flagCount = 1;
+
+ return this.updateMessageAndSave(message, 'flags', 'flagCount');
+ }
+
+ async markMessageAsReported (message) {
+ message.reported = true;
+
+ return this.updateMessageAndSave(message, 'reported');
+ }
+
+ async flag () {
+ let {message, userComment} = await this.validate();
+ await this.flagInboxMessage(message);
+ await this.notify(message, userComment);
+ await this.markMessageAsReported(message);
+ return message;
+ }
+}
diff --git a/website/server/libs/inbox/index.js b/website/server/libs/inbox/index.js
index cbdda9d8e2..26524ecac0 100644
--- a/website/server/libs/inbox/index.js
+++ b/website/server/libs/inbox/index.js
@@ -16,6 +16,10 @@ export async function getUserInbox (user, asArray = true) {
}
}
+export async function getUserInboxMessage (user, messageId) {
+ return Inbox.findOne({ownerId: user._id, _id: messageId}).exec();
+}
+
export async function deleteMessage (user, messageId) {
const message = await Inbox.findOne({_id: messageId, ownerId: user._id }).exec();
if (!message) return false;
diff --git a/website/server/libs/setupPassport.js b/website/server/libs/setupPassport.js
index 9f17200fd1..215c91daa5 100644
--- a/website/server/libs/setupPassport.js
+++ b/website/server/libs/setupPassport.js
@@ -6,7 +6,7 @@ import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
-// this will be as simple as storing the user ID when serializing, and finding
+// this will be as simple as storing the User ID when serializing, and finding
// the user by ID when deserializing. However, since this example does not
// have a database of user records, the complete Facebook profile is serialized
// and deserialized.
diff --git a/website/server/libs/slack.js b/website/server/libs/slack.js
index b0b5bb8898..0adaf24894 100644
--- a/website/server/libs/slack.js
+++ b/website/server/libs/slack.js
@@ -9,6 +9,10 @@ const SLACK_FLAGGING_URL = nconf.get('SLACK_FLAGGING_URL');
const SLACK_FLAGGING_FOOTER_LINK = nconf.get('SLACK_FLAGGING_FOOTER_LINK');
const SLACK_SUBSCRIPTIONS_URL = nconf.get('SLACK_SUBSCRIPTIONS_URL');
const BASE_URL = nconf.get('BASE_URL');
+const IS_PRODUCTION = nconf.get('IS_PROD');
+
+const SKIP_FLAG_METHODS = IS_PRODUCTION && !SLACK_FLAGGING_URL;
+const SKIP_SUB_METHOD = IS_PRODUCTION && !SLACK_SUBSCRIPTIONS_URL;
let flagSlack;
let subscriptionSlack;
@@ -18,6 +22,26 @@ try {
subscriptionSlack = new IncomingWebhook(SLACK_SUBSCRIPTIONS_URL);
} catch (err) {
logger.error(err);
+
+ if (!IS_PRODUCTION) {
+ flagSlack = subscriptionSlack = {
+ send (data) {
+ logger.info('Data sent to slack', data);
+ },
+ };
+ }
+}
+
+/**
+ *
+ * @param formatObj.name userName
+ * @param formatObj.displayName displayName
+ * @param formatObj.email email
+ * @param formatObj.uuid uuid
+ * @returns {string}
+ */
+function formatUser (formatObj) {
+ return `@${formatObj.name} ${formatObj.displayName} (${formatObj.email}; ${formatObj.uuid})`;
}
function sendFlagNotification ({
@@ -28,13 +52,13 @@ function sendFlagNotification ({
userComment,
automatedComment,
}) {
- if (!SLACK_FLAGGING_URL) {
+ if (SKIP_FLAG_METHODS) {
return;
}
let titleLink;
let authorName;
let title = `Flag in ${group.name}`;
- let text = `${flagger.profile.name} (${flagger.id}; language: ${flagger.preferences.language}) flagged a message`;
+ let text = `${flagger.profile.name} (${flagger.id}; language: ${flagger.preferences.language}) flagged a group message`;
let footer = `<${SLACK_FLAGGING_FOOTER_LINK}?groupId=${group.id}&chatId=${message.id}|Flag this message.>`;
if (userComment) {
@@ -55,7 +79,12 @@ function sendFlagNotification ({
if (!message.user && message.uuid === 'system') {
authorName = 'System Message';
} else {
- authorName = `${message.user} - ${authorEmail} - ${message.uuid}`;
+ authorName = formatUser({
+ name: message.username,
+ displayName: message.user,
+ email: authorEmail,
+ uuid: message.uuid,
+ });
}
const timestamp = `${moment(message.timestamp).utc().format('YYYY-MM-DD HH:mm')} UTC`;
@@ -77,6 +106,69 @@ function sendFlagNotification ({
});
}
+function sendInboxFlagNotification ({
+ authorEmail,
+ flagger,
+ message,
+ userComment,
+}) {
+ if (SKIP_FLAG_METHODS) {
+ return;
+ }
+ let titleLink = '';
+ let authorName;
+ let title = `Flag in ${flagger.profile.name}'s Inbox`;
+ let text = `${flagger.profile.name} (${flagger.id}; language: ${flagger.preferences.language}) flagged a PM`;
+ let footer = '';
+
+ if (userComment) {
+ text += ` and commented: ${userComment}`;
+ }
+
+ let messageText = message.text;
+ let sender = '';
+ let recipient = '';
+
+ const flaggerFormat = formatUser({
+ displayName: flagger.profile.name,
+ name: flagger.auth.local.username,
+ email: flagger.auth.local.email,
+ uuid: flagger._id,
+ });
+ const messageUserFormat = formatUser({
+ displayName: message.user,
+ name: message.username,
+ email: authorEmail,
+ uuid: message.uuid,
+ });
+
+ if (message.sent) {
+ sender = flaggerFormat;
+ recipient = messageUserFormat;
+ } else {
+ sender = messageUserFormat;
+ recipient = flaggerFormat;
+ }
+
+ authorName = `${sender} wrote this message to ${recipient}.`;
+
+ flagSlack.send({
+ text,
+ attachments: [{
+ fallback: 'Flag Message',
+ color: 'danger',
+ author_name: authorName,
+ title,
+ title_link: titleLink,
+ text: messageText,
+ footer,
+ mrkdwn_in: [
+ 'text',
+ ],
+ }],
+ });
+}
+
function sendSubscriptionNotification ({
buyer,
recipient,
@@ -84,7 +176,7 @@ function sendSubscriptionNotification ({
months,
groupId,
}) {
- if (!SLACK_SUBSCRIPTIONS_URL) {
+ if (SKIP_SUB_METHOD) {
return;
}
let text;
@@ -108,7 +200,7 @@ function sendSlurNotification ({
group,
message,
}) {
- if (!SLACK_FLAGGING_URL) {
+ if (SKIP_FLAG_METHODS) {
return;
}
let titleLink;
@@ -124,7 +216,12 @@ function sendSlurNotification ({
title += ` - (${group.privacy} ${group.type})`;
}
- authorName = `${author.profile.name} - ${authorEmail} - ${author.id}`;
+ authorName = formatUser({
+ name: author.auth.local.username,
+ displayName: author.profile.name,
+ email: authorEmail,
+ uuid: author.id,
+ });
flagSlack.send({
text,
@@ -143,5 +240,9 @@ function sendSlurNotification ({
}
module.exports = {
- sendFlagNotification, sendSubscriptionNotification, sendSlurNotification,
+ sendFlagNotification,
+ sendInboxFlagNotification,
+ sendSubscriptionNotification,
+ sendSlurNotification,
+ formatUser,
};
diff --git a/website/server/models/group.js b/website/server/models/group.js
index cea35d21ad..e8bf77e39a 100644
--- a/website/server/models/group.js
+++ b/website/server/models/group.js
@@ -408,7 +408,7 @@ function getInviteCount (uuids, emails) {
/**
* Checks invitation uuids and emails for possible errors.
*
- * @param uuids An array of user ids
+ * @param uuids An array of User IDs
* @param emails An array of emails
* @param res Express res object for use with translations
* @throws BadRequest An error describing the issue with the invitations
diff --git a/website/server/models/user/hooks.js b/website/server/models/user/hooks.js
index 90616326b2..75bee4d71d 100644
--- a/website/server/models/user/hooks.js
+++ b/website/server/models/user/hooks.js
@@ -129,14 +129,11 @@ function _setUpNewUser (user) {
user.preferences.background = 'violet';
const testGroup = Math.random();
- if (testGroup < 0.25) {
- user._ABtests.welcomeEmailSplit = 'welcome-v2';
- } else if (testGroup < 0.5) {
+
+ if (testGroup < 0.5) {
user._ABtests.welcomeEmailSplit = 'welcome-v2b';
- } else if (testGroup < 0.75) {
- user._ABtests.welcomeEmailSplit = 'welcome-v2c';
} else {
- user._ABtests.welcomeEmailSplit = 'welcome-v2d';
+ user._ABtests.welcomeEmailSplit = 'welcome';
}
if (user.registeredThrough === 'habitica-web') {