Sept 18 fixes (#9051)

* Added hover state to buy buttons

* Translated profile

* Fixed sending private message from member modal

* Added payment functions

* Added translation to home page

* Fixed translation

* Some front page styles

* Fixed inbox sorting and searching

* Added seasonals

* Fixed buy gem modal conflict

* Fixed paypal link

* Fixed footer style crossover

* Fixed quest update

* Fixed sanity
This commit is contained in:
Keith Holliday
2017-09-19 15:35:32 -05:00
committed by GitHub
parent 0a69c7a08d
commit b1652ddd97
15 changed files with 382 additions and 187 deletions

View File

@@ -59,7 +59,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.svg-icon.gem(v-html='icons.gem')
span 2
.col-12.text-center
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("shirt", specialShirtKeys)', @click='unlock(`shirt.${specialShirtKeys.join(",shirt.")}`)') $t('purchaseAll')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("shirt", specialShirtKeys)', @click='unlock(`shirt.${specialShirtKeys.join(",shirt.")}`)') {{ $t('purchaseAll') }}
#skin.section.customize-section(v-if='activeTopPage === "skin"')
.row.sub-menu.col-6.offset-3.text-center
.col-6.offset-3.text-center.sub-menu-item(:class='{active: activeSubPage === "color"}')
@@ -69,26 +69,17 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.option(v-for='option in ["ddc994", "f5a76e", "ea8349", "c06534", "98461a", "915533", "c3e1dc", "6bd049"]',
:class='{active: user.preferences.skin === option}')
.skin.sprite.customize-option(:class="`skin_${option}`", @click='set({"preferences.skin": option})')
.row(v-if='editing')
.row(v-if='editing && set.key !== "undefined"', v-for='set in seasonalSkins')
.col-12.customize-options
.option(v-for='option in rainbowSkins',
:class='{active: option.active, locked: option.locked}')
//h3(v-if='!hideSet(set)') {{$t(set.key)}}
.option(v-for='option in set.options',
:class='{active: option.active, locked: option.locked, hide: option.hide}')
.skin.sprite.customize-option(:class="`skin_${option.key}`", @click='option.click')
.gem-lock(v-if='option.locked')
.svg-icon.gem(v-html='icons.gem')
span 2
.col-12.text-center
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("skin", rainbowSkinKeys)', @click='unlock(`skin.${rainbowSkinKeys.join(",skin.")}`)') $t('purchaseAll')
.row(v-if='editing')
.col-12.customize-options
.option(v-for='option in animalSkins',
:class='{active: option.active, locked: option.locked}')
.skin.sprite.customize-option(:class="`skin_${option.key}`", @click='option.click')
.gem-lock(v-if='option.locked')
.svg-icon.gem(v-html='icons.gem')
span 2
.col-12.text-center
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("skin", animalSkinKeys)', @click='unlock(`skin.${animalSkinKeys.join(",skin.")}`)') $t('purchaseAll')
button.btn.btn-secondary.purchase-all(v-if='!hideSet(set) && !userOwnsSet("skin", set.keys)', @click='unlock(`skin.${set.keys.join(",skin.")}`)') {{ $t('purchaseAll') }}
#hair.section.customize-section(v-if='activeTopPage === "hair"')
.row.sub-menu.col-6.offset-3.text-center
.col-2.offset-1.text-center.sub-menu-item(@click='changeSubPage("color")', :class='{active: activeSubPage === "color"}')
@@ -101,11 +92,13 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
strong(v-once) {{$t('ponytail')}}
.col-2.text-center.sub-menu-item(@click='changeSubPage("facialhair")', :class='{active: activeSubPage === "facialhair"}', v-if='editing')
strong(v-once) {{$t('facialhair')}}
#hair-color.row(v-if='activeSubPage === "color"')
#hair-color.section.customize-section(v-if='activeSubPage === "color"')
.row
.col-12.customize-options
.option(v-for='option in ["white", "brown", "blond", "red", "black"]',
:class='{active: user.preferences.hair.color === option}')
.color-bangs.sprite.customize-option(:class="`hair_bangs_1_${option}`", @click='set({"preferences.hair.color": option})')
//.row(v-if='editing')
.col-12.customize-options(v-if='editing')
.option(v-for='option in premiumHairColors',
:class='{active: option.active === option, locked: option.locked}')
@@ -114,7 +107,18 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.svg-icon.gem(v-html='icons.gem')
span 2
.col-12.text-center
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", premiumHairColorKeys, "color")', @click='unlock(`hair.color.${premiumHairColorKeys.join(",hair.color.")}`)') $t('purchaseAll')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", premiumHairColorKeys, "color")', @click='unlock(`hair.color.${premiumHairColorKeys.join(",hair.color.")}`)') {{ $t('purchaseAll') }}
.row(v-if='editing && set.key !== "undefined"', v-for='set in seasonalHairColors')
.col-12.customize-options
//h3(v-if='!hideSet(set)') {{set.text}}
.option(v-for='option in set.options',
:class='{active: option.active, locked: option.locked, hide: option.hide}')
.skin.sprite.customize-option(:class="`hair_bangs_1_${option.key}`", @click='option.click')
.gem-lock(v-if='option.locked')
.svg-icon.gem(v-html='icons.gem')
span 2
.col-12.text-center
button.btn.btn-secondary.purchase-all(v-if='!hideSet(set) && !userOwnsSet("hair", set.keys, "color")', @click='unlock(`hair.color.${set.keys.join(",hair.color.")}`)') {{ $t('purchaseAll') }}
#style.row(v-if='activeSubPage === "style"')
.col-12.customize-options(v-if='editing')
.option(v-for='option in baseHair3',
@@ -124,7 +128,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.svg-icon.gem(v-html='icons.gem')
span 2
.col-12.text-center
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair3Keys, "base")', @click='unlock(`hair.base.${baseHair3Keys.join(",hair.base.")}`)') $t('purchaseAll')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair3Keys, "base")', @click='unlock(`hair.base.${baseHair3Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }}
.col-12.customize-options(v-if='editing')
.option(v-for='option in baseHair4',
:class='{active: option.active, locked: option.locked}')
@@ -133,7 +137,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.svg-icon.gem(v-html='icons.gem')
span 2
.col-12.text-center
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair4Keys, "base")', @click='unlock(`hair.base.${baseHair4Keys.join(",hair.base.")}`)') $t('purchaseAll')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair4Keys, "base")', @click='unlock(`hair.base.${baseHair4Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }}
#bangs.row(v-if='activeSubPage === "bangs"')
.col-12.customize-options
.head_0.option(@click='set({"preferences.hair.bangs": 0})',
@@ -155,7 +159,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.svg-icon.gem(v-html='icons.gem')
span 2
.col-12.text-center
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair2Keys, "base")', @click='unlock(`hair.base.${baseHair2Keys.join(",hair.base.")}`)') $t('purchaseAll')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair2Keys, "base")', @click='unlock(`hair.base.${baseHair2Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }}
#facialhair.row(v-if='activeSubPage === "facialhair"')
.col-12.customize-options(v-if='editing')
.option(v-for='option in baseHair5',
@@ -165,7 +169,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.svg-icon.gem(v-html='icons.gem')
span 2
.col-12.text-center
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair5Keys, "beard")', @click='unlock(`hair.beard.${baseHair5Keys.join(",hair.beard.")}`)') $t('purchaseAll')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair5Keys, "beard")', @click='unlock(`hair.beard.${baseHair5Keys.join(",hair.beard.")}`)') {{ $t('purchaseAll') }}
.col-12.customize-options(v-if='editing')
.option(v-for='option in baseHair6',
:class='{active: option.active, locked: option.locked}')
@@ -174,7 +178,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.svg-icon.gem(v-html='icons.gem')
span 2
.col-12.text-center
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair6Keys, "mustache")', @click='unlock(`hair.mustache.${baseHair6Keys.join(",hair.mustache.")}`)') $t('purchaseAll')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair6Keys, "mustache")', @click='unlock(`hair.mustache.${baseHair6Keys.join(",hair.mustache.")}`)') {{ $t('purchaseAll') }}
#extra.section.container.customize-section(v-if='activeTopPage === "extra"')
.row.sub-menu.col-6.offset-3.text-center
.col-4.text-center.sub-menu-item(@click='changeSubPage("glasses")', :class='{active: activeSubPage === "glasses"}')
@@ -200,7 +204,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.svg-icon.gem(v-html='icons.gem')
span 2
.col-12.text-center
button.btn.btn-secondary.purchase-all(v-if='!animalEarsOwned', @click='unlock(animalEarsUnlockString)') $t('purchaseAll')
button.btn.btn-secondary.purchase-all(v-if='!animalEarsOwned', @click='unlock(animalEarsUnlockString)') {{ $t('purchaseAll') }}
#wheelchairs.row(v-if='activeSubPage === "wheelchair"')
.col-12.customize-options.weelchairs
.option(@click='set({"preferences.chair": "none"})', :class='{active: user.preferences.chair === "none"}')
@@ -508,6 +512,10 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
}
.option.hide {
display: none !important;
}
.customize-options .option {
display: inline-block;
vertical-align: bottom;
@@ -756,14 +764,18 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
<script>
import axios from 'axios';
import moment from 'moment';
import map from 'lodash/map';
import get from 'lodash/get';
import groupBy from 'lodash/groupBy';
import { mapState } from 'client/libs/store';
import avatar from './avatar';
import { getBackgroundShopSets } from '../../common/script/libs/shops';
import unlock from '../../common/script/ops/unlock';
import guide from 'client/mixins/guide';
import notifications from 'client/mixins/notifications';
import appearance from 'common/script/content/appearance';
import appearanceSets from 'common/script/content/appearance/sets';
import bModal from 'bootstrap-vue/lib/components/modal';
@@ -777,6 +789,9 @@ import gem from 'assets/svg/gem.svg';
import pin from 'assets/svg/pin.svg';
import isPinned from 'common/script/libs/isPinned';
const skinsBySet = groupBy(appearance.skin, 'set.key');
const hairColorBySet = groupBy(appearance.hair.color, 'set.key');
let tasksByCategory = {
work: [
{
@@ -1029,6 +1044,70 @@ export default {
});
return options;
},
seasonalSkins () {
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
let seasonalSkins = [];
for (let key in skinsBySet) {
let set = skinsBySet[key];
let keys = set.map(item => {
return item.key;
});
let options = keys.map(optionKey => {
return this.mapKeysToOption(optionKey, 'skin', '', key);
});
let text = this.$t(key);
if (appearanceSets[key] && appearanceSets[key].text) {
text = appearanceSets[key].text();
}
let compiledSet = {
key,
options,
keys,
text,
};
seasonalSkins.push(compiledSet);
}
return seasonalSkins;
},
seasonalHairColors () {
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
let seasonalHairColors = [];
for (let key in hairColorBySet) {
let set = hairColorBySet[key];
let keys = set.map(item => {
return item.key;
});
let options = keys.map(optionKey => {
return this.mapKeysToOption(optionKey, 'hair', 'color', key);
});
let text = this.$t(key);
if (appearanceSets[key] && appearanceSets[key].text) {
text = appearanceSets[key].text();
}
let compiledSet = {
key,
options,
keys,
text,
};
seasonalHairColors.push(compiledSet);
}
return seasonalHairColors;
},
premiumHairColors () {
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
@@ -1117,6 +1196,9 @@ export default {
},
},
methods: {
hideSet (set) {
return moment(appearanceSets[set.key].availableUntil).isBefore(moment());
},
purchase (type, key) {
this.$store.dispatch('shops:purchase', {
type,
@@ -1124,16 +1206,22 @@ export default {
});
this.backgroundUpdate = new Date();
},
mapKeysToOption (key, type, subType) {
mapKeysToOption (key, type, subType, set) {
let userPreference = subType ? this.user.preferences[type][subType] : this.user.preferences[type];
let userPurchased = subType ? this.user.purchased[type][subType] : this.user.purchased[type];
let locked = !userPurchased || !userPurchased[key];
let pathKey = subType ? `${type}.${subType}` : `${type}`;
let hide = false;
if (set && appearanceSets[set]) {
if (locked) hide = moment(appearanceSets[set].availableUntil).isBefore(moment());
}
let option = {};
option.key = key;
option.active = userPreference === key;
option.locked = locked;
option.hide = hide;
option.click = () => {
return locked ? this.unlock(`${pathKey}.${key}`) : this.set({[`preferences.${pathKey}`]: key});
};

View File

@@ -234,7 +234,7 @@ export default {
},
methods: {
sendMessage () {
this.userIdToMessage = this.user._id;
this.$store.state.userIdToMessage = this.user._id;
this.$root.$emit('show::modal', 'private-message');
},
async getMembers () {

View File

@@ -170,7 +170,7 @@ export default {
const response = await this.$store.dispatch('guilds:inviteToQuest', {groupId: this.group._id, key});
const quest = response.data.data;
this.$store.party.data.quest = quest;
this.$store.state.party.data.quest = quest;
this.$root.$emit('hide::modal', 'start-quest-modal');
},
},

View File

@@ -30,39 +30,39 @@
.col
+featureBullet("{{ $t('gemBenefit3') }}")
+featureBullet("{{ $t('gemBenefit4') }}")
.card-deck
.card.text-center
.card-deck.gem-deck
//.card.text-center(:class="{active: gemAmount === 4}")
.card-img-top
.mx-auto(v-html='icons.fourGems', style='"height: 53px; width: 49.5px; margin-top: 2em;"')
.card-body
.gem-count 4
.gem-text {{ $t('gems') }}
.divider
button.btn.btn-primary $.99
.card.text-center
button.btn.btn-primary(@click='gemAmount = 4') {{gemAmount === 4 ? $t('selected') : '$1.00'}}
.card.text-center.col-3(:class="{active: gemAmount === 21}")
.card-img-top
.mx-auto(v-html='icons.twentyOneGems', style='"height: 55px; width: 47.5px; margin-top: 1.85em;"')
.card-body
.gem-count 21
.gem-text {{ $t('gems') }}
.divider
button.btn.btn-primary $4.99
.card.text-center
button.btn.btn-primary(@click='gemAmount = 21') {{gemAmount === 21 ? $t('selected') : '$5.00'}}
//.card.text-center(:class="{active: gemAmount === 42}")
.card-img-top
.mx-auto(v-html='icons.fortyTwoGems', style='"height: 49.5px; width: 51px; margin-top: 1.9em;"')
.card-body
.gem-count 42
.gem-text {{ $t('gems') }}
.divider
button.btn.btn-primary $9.99
.card.text-center
button.btn.btn-primary(@click='gemAmount = 42') {{gemAmount === 42 ? $t('selected') : '$10.00'}}
//.card.text-center(:class="{active: gemAmount === 84}")
.card-img-top
.mx-auto(v-html='icons.eightyFourGems', style='"height: 65px; width: 67px; margin-top: 1em;"')
.card-body
.gem-count 84
.gem-text {{ $t('gems') }}
.divider
button.btn.btn-primary $19.99
button.btn.btn-primary(@click='gemAmount = 84') {{gemAmount === 84 ? $t('selected') : '$20.00'}}
.row.text-center
h2.mx-auto.text-payment {{ $t('choosePaymentMethod') }}
.card-deck
@@ -100,63 +100,63 @@
+featureBullet("{{ $t('subscriptionBenefit5') }}")
+featureBullet("{{ $t('subscriptionBenefit6') }}")
.card-deck
.card.text-center
.card.text-center(:class='{active: subscriptionPlan === "basic_earned"}')
.card-body
.subscription-price
span.superscript $
span 4
span.superscript.muted .99
span 5
span.superscript.muted .00
.small {{ $t('everyMonth') }}
.divider
p.benefits(v-markdown='$t("earnGemsMonthly", {cap:25})')
.spacer
button.btn.btn-primary {{ $t('select') }}
.card.text-center
button.btn.btn-primary(@click='subscriptionPlan = "basic_earned"') {{ subscriptionPlan === "basic_earned" ? $t('select') : $t('selected') }}
.card.text-center(:class='{active: subscriptionPlan === "basic_3mo"}')
.card-body
.subscription-price
span.superscript $
span 14
span.superscript.muted .99
span 15
span.superscript.muted .00
.small {{ $t('everyXMonths', {interval: 3}) }}
.divider
p.benefits(v-markdown='$t("earnGemsMonthly", {cap:30})')
p.benefits(v-markdown='$t("receiveMysticHourglass")')
button.btn.btn-primary {{ $t('select') }}
.card.text-center
button.btn.btn-primary(@click='subscriptionPlan = "basic_3mo"') {{ subscriptionPlan === "basic_3mo" ? $t('select') : $t('selected') }}
.card.text-center(:class='{active: subscriptionPlan === "basic_6mo"}')
.card-body
.subscription-price
span.superscript $
span 29
span.superscript.muted .99
span 30
span.superscript.muted .00
.small {{ $t('everyXMonths', {interval: 6}) }}
.divider
p.benefits(v-markdown='$t("earnGemsMonthly", {cap:35})')
p.benefits(v-markdown='$t("receiveMysticHourglasses", {amount:2})')
button.btn.btn-primary {{ $t('select') }}
.card.text-center
button.btn.btn-primary(@click='subscriptionPlan = "basic_6mo"') {{ subscriptionPlan === "basic_6mo" ? $t('select') : $t('selected') }}
.card.text-center(:class='{active: subscriptionPlan === "basic_12mo"}')
.card-body
.subscription-price
span.superscript $
span 47
span.superscript.muted .99
span 48
span.superscript.muted .00
.small {{ $t('everyYear') }}
.divider
p.benefits(v-markdown='$t("earnGemsMonthly", {cap:45})')
p.benefits(v-markdown='$t("receiveMysticHourglasses", {amount:4})')
button.btn.btn-primary {{ $t('select') }}
button.btn.btn-primary(@click='subscriptionPlan = "basic_12mo"') {{ subscriptionPlan === "basic_12mo" ? $t('select') : $t('selected') }}
.row.text-center
h2.mx-auto.text-payment {{ $t('choosePaymentMethod') }}
.row.text-center
a.mx-auto {{ $t('haveCouponCode') }}
.card-deck
.card.text-center.payment-method
.card-body(@click='showStripe({})')
.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(:href='paypalCheckoutLink', target='_blank')
a.card-body.paypal(:href='paypalSubscriptionLink', target='_blank')
img(src='~assets/images/paypal.png')
.card.text-center.payment-method
.card-body.amazon(@click="amazonPaymentsInit({type: 'single'})")
.card-body.amazon(@click="amazonPaymentsInit({type: 'subscription', subscription: subscriptionPlan})")
img(src='~assets/images/amazon-payments.png')
.row.text-center
.svg-icon.mx-auto(v-html='icons.heart', style='"height: 24px; width: 24px;"')
@@ -198,12 +198,24 @@
border: solid 2px #e1e0e3;
}
.gem-deck {
align-items: center;
justify-content: center;
}
.card {
margin: 1em;
border-radius: 2px;
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
}
.card.active {
border-color: #24cc8f;
button {
background-color: #24cc8f;
}
}
.divider {
width: 80%;
height: 1px;
@@ -261,6 +273,10 @@
background-color: #e1e0e3;
}
.payment-method:hover {
cursor: pointer;
}
.paypal {
padding-top: 1.3em;
}
@@ -369,6 +385,8 @@
fortyTwoGems,
eightyFourGems,
}),
gemAmount: 0,
subscriptionPlan: '',
selectedPage: 'subscribe',
amazonPayments: {},
planGemLimits,

View File

@@ -21,7 +21,6 @@ import notifications from 'client/mixins/notifications';
export default {
mixins: [notifications],
props: ['userIdToMessage'],
components: {
bModal,
},
@@ -29,8 +28,19 @@ export default {
return {
privateMessage: '',
loading: false,
userIdToMessage: '',
};
},
computed: {
userIdToMessageStore () {
return this.$store.state.userIdToMessage;
},
},
watch: {
userIdToMessageStore () {
this.userIdToMessage = this.userIdToMessageStore;
},
},
methods: {
async sendPrivateMessage () {
if (!this.privateMessage || !this.userIdToMessage) return;
@@ -45,6 +55,8 @@ export default {
this.loading = false;
this.text(this.$t('messageSentAlert'));
this.privateMessage = '';
},
},
};

View File

@@ -82,7 +82,7 @@
a.purchase(:href='paypalPurchaseLink', :disabled='!subscription.key', target='_blank')
img(src='https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png', :alt="$t('paypal')")
.col-md-4
a.purchase(@click="amazonPaymentsInit({type: 'subscription', subscription:subscription.key, coupon:subscription.coupon})")
a.btn.btn-secondary.purchase(@click="amazonPaymentsInit({type: 'subscription', subscription:subscription.key, coupon:subscription.coupon})")
img(src='https://payments.amazon.com/gp/cba/button', :alt="$t('amazonPayments')")
.row
@@ -153,12 +153,6 @@ export default {
},
computed: {
...mapState({user: 'user.data'}),
paypalPurchaseLink () {
let couponString = '';
if (this.subscription.coupon) couponString = `&coupon=${this.subscription.coupon}`;
return `/paypal/subscribe?_id=${this.user._id}&apiToken=${this.user.apiToken}&sub=${this.subscription.key}${couponString}`;
// @TODO don't put API Token in URL parameters
},
subscriptionBlocksOrdered () {
let subscriptions = filter(subscriptionBlocks, (o) => {
return o.discount !== true;

View File

@@ -9,10 +9,10 @@
.row
.col-6
img(src='~assets/images/home/home-main@3x.png', width='357px')
h1 Motivate yourself to achieve your goals.
p.section-main It's time to have fun when you get things done! Join over 2 million Habiticans and improve your life one task at a time.
h1 {{$t('motivateYourself')}}
p.section-main {{$t('timeToGetThingsDone')}}
.col-6
h3.text-center Sign Up For Free
h3.text-center {{$t('singUpForFree')}}
div.text-center
button.social-button(@click='socialAuth("facebook")')
.svg-icon.social-icon(v-html="icons.facebookIcon")
@@ -21,14 +21,14 @@
.svg-icon.social-icon(v-html="icons.googleIcon")
span {{$t('signUpWithSocial', {social: 'Google'})}}
.strike
span OR
span {{$t('or')}}
.form
input.form-control(type='text', placeholder='Login Name', v-model='username', :class='{"input-valid": username.length > 0}')
input.form-control(type='email', placeholder='Email', v-model='email', :class='{"input-invalid": emailInvalid, "input-valid": emailValid}')
input.form-control(type='password', placeholder='Password', v-model='password', :class='{"input-valid": password.length > 0}')
input.form-control(type='password', placeholder='Confirm Password', v-model='passwordConfirm', :class='{"input-invalid": passwordConfirmInvalid, "input-valid": passwordConfirmValid}')
p.form-text(v-once, v-html="$t('termsAndAgreement')")
button.sign-up(@click='register()') Sign Up
button.sign-up(@click='register()') {{$t('signup')}}
.col-12
.spacer.svg-icon(v-html='icons.spacer')
@@ -38,21 +38,21 @@
.container
.row
.col-6.offset-3.text-center
h2 Gamify Your Life
p.section-main Habitica is a free habit-building and productivity app that treats your real life like a game. With in-game rewards and punishments to motivate you and a strong social network to inspire you, Habitica can help you achieve your goals to become healthy, hard-working, and happy.
h2 {{$t('gamifyYourLife')}}
p.section-main {{$t('aboutHabitica')}}
.row
.col-4
img.track-habits(src='~assets/images/home/track-habits@3x.png', width='354px', height='228px')
strong Track Your Habits and Goals
p Stay accountable by tracking and managing your Habits, Daily goals, and To-Do list with Habiticas easy-to-use mobile apps and web interface.
strong {{$t('trackYourGoals')}}
p {{$t('trackYourGoalsDesc')}}
.col-4
img(src='~assets/images/home/earn-rewards@3x.png', width='316px', height='244px')
strong Earn Rewards for Your Goals
p Check off tasks to level up your Avatar and unlock in-game features such as battle armor, mysterious pets, magic skills, and even quests!
strong {{$t('earnRewards')}}
p {{$t('earnRewardsDesc')}}
.col-4
img(src='~assets/images/home/battle-monsters@3x.png', width='303px', height='244px')
strong Battle Monsters with Friends
p Fight monsters with other Habiticans! Use the Gold that you earn to buy in-game or custom rewards, like watching an episode of your favorite TV show.
strong {{$t('battleMonsters')}}
p {{$t('battleMonstersDesc')}}
.col-12
.spacer.svg-icon(v-html='icons.spacer')
@@ -60,20 +60,20 @@
.container.text-center
.row
.col-12
h2 Players Use Habitica to Improve
h2 {{$t('playersUseToImprove')}}
.row
.col-4
img(src='~assets/images/home/health-fitness@3x.png', width='300px', height='300px')
strong Health and Fitness
p Never motivated to floss? Can't seem to get to the gym? Habitica finally makes it fun to get healthy.
strong {{$t('healthAndFitness')}}
p {{$t('healthAndFitnessDesc')}}
.col-4
img(src='~assets/images/home/school-work@3x.png', width='300px', height='300px')
strong School and Work
p Whether you're preparing a report for your teacher or your boss, it's easy to keep track of your progress as you tackle your toughest tasks.
strong {{$t('schoolAndWork')}}
p {{$t('schoolAndWorkDesc')}}
.col-4
img(src='~assets/images/home/much-more@3x.png', width='300px', height='300px')
strong And much, much more!
p Our fully customizable task list means that you can shape Habitica to fit your personal goals. Work on creative projects, emphasize self-care, or pursue a different dream -- it's all up to you.
strong {{$t('muchmuchMore')}}
p {{$t('muchmuchMoreDesc')}}
.col-12
.spacer.svg-icon(v-html='icons.spacer')
.container-fluid
@@ -85,8 +85,8 @@
.col-6
.iphones.svg-icon(v-html='icons.iphones')
.col-6.text-column
h2 Level Up Anywhere
p Our mobile apps make it simple to keep track of your tasks on-the-go. Accomplish your goals with a single tap, no matter where you are.
h2 {{ $t('levelUpAnywhere') }}
p {{ $t('levelUpAnywhereDesc') }}
a.app.svg-icon(v-html='icons.googlePlay', href='https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica', target='_blank')
a.app.svg-icon(v-html='icons.iosAppStore', href='https://itunes.apple.com/us/app/habitica-gamified-task-manager/id994882113?mt=8', target='_blank')
.container-fluid
@@ -95,13 +95,13 @@
#call-to-action.purple-4
.container.featured
.row.text-center
h3.col-12 Join over 2,000,000 people having fun while accomplishing their goals!
h3.col-12 {{ $t('joinMany') }}
.row
.col-12.text-center
button.btn.btn-primary.join-button(@click='playButtonClick()') Join Habitica Today
button.btn.btn-primary.join-button(@click='playButtonClick()') {{ $t('joinToday') }}
.row.featured
.col-12.text-center
strong Featured in
strong {{ $t('featuredIn') }}
.container-fluid.featured
.row
.col-12
@@ -115,11 +115,6 @@
img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/discover-logo.png', alt="$t(altAttrDiscover)")
.container-fluid
.seamless_stars_varied_opacity_repeat
#bottom-wrap.purple-4
#bottom-background
.seamless_mountains_demo_repeat
.midground_foreground_extended2
</template>
<style lang="scss">
@@ -240,13 +235,19 @@
.social-button {
border-radius: 2px;
border: solid 2px #bda8ff;
width: 206px;
height: 40px;
width: 48%;
min-height: 40px;
padding: .5em;
background: transparent;
margin-right: .5em;
color: #fff;
}
.social-button:hover {
cursor: pointer;
border-color: #fff;
}
.social-icon {
margin-right: 1em;
width: 18px;
@@ -290,17 +291,28 @@
margin-left: 15px;
}
.form {
padding-top: 1em;
padding-bottom: 1em;
}
input {
margin-bottom: 1em;
border-radius: 2px;
background-color: #432874;
border-color: #432874;
color: $purple-400;
border: solid 2px transparent;
}
input:focus {
border: solid 2px #9a62ff;
color: #fff;
background-color: #36205d;
}
input:hover {
background-color: #36205d;
}
button.sign-up {
@@ -469,33 +481,6 @@
width: 100%;
}
}
#bottom-wrap {
padding-top: 10em;
}
#bottom-background {
position: relative;
.seamless_mountains_demo_repeat {
background-image: url('~assets/images/auth/seamless_mountains_demo.png');
background-repeat: repeat-x;
width: 100%;
height: 500px;
position: absolute;
z-index: 0;
bottom: 0;
}
.midground_foreground_extended2 {
background-image: url('~assets/images/auth/midground_foreground_extended2.png');
position: relative;
width: 1500px;
max-width: 100%;
height: 150px;
margin: 0 auto;
}
}
</style>
<script>

View File

@@ -7,11 +7,20 @@
#purple-footer
app-footer
#bottom-wrap.purple-4
#bottom-background
.seamless_mountains_demo_repeat
.midground_foreground_extended2
</template>
<style lang='scss'>
@import '~client/assets/scss/colors.scss';
.purple-4 {
background-color: #271b3d;
}
#purple-footer {
background-color: #271b3d;
@@ -34,6 +43,33 @@
}
}
#bottom-wrap {
padding-top: 10em;
}
#bottom-background {
position: relative;
.seamless_mountains_demo_repeat {
background-image: url('~assets/images/auth/seamless_mountains_demo.png');
background-repeat: repeat-x;
width: 100%;
height: 500px;
position: absolute;
z-index: 0;
bottom: 0;
}
.midground_foreground_extended2 {
background-image: url('~assets/images/auth/midground_foreground_extended2.png');
position: relative;
width: 1500px;
max-width: 100%;
height: 150px;
margin: 0 auto;
}
}
.static-wrapper {
.container-fluid {
margin: 5em 2em 2em 2em;

View File

@@ -23,7 +23,7 @@
h4(v-once) {{$t('emptyMessagesLine1')}}
p(v-once) {{$t('emptyMessagesLine2')}}
.conversations(v-if='filtersConversations.length > 0')
.conversation(v-for='conversation in conversations', @click='selectConversation(conversation.key)',
.conversation(v-for='conversation in filtersConversations', @click='selectConversation(conversation.key)',
:class="{active: selectedConversation === conversation.key}")
div
span(:class="userLevelStyle(conversation)") {{conversation.name}}
@@ -213,6 +213,11 @@ export default {
conversations[userId].date = message.timestamp;
}
conversations = sortBy(conversations, [(o) => {
return moment(o.date).toDate();
}]);
conversations = conversations.reverse();
return conversations;
},
currentMessages () {
@@ -220,7 +225,7 @@ export default {
return this.conversations[this.selectedConversation].messages;
},
filtersConversations () {
if (!this.search) return Object.values(this.conversations);
if (!this.search) return this.conversations;
return filter(this.conversations, (conversation) => {
return conversation.name.toLowerCase().indexOf(this.search.toLowerCase()) !== -1;
});

View File

@@ -20,46 +20,46 @@ div
member-details(:member="user")
.row
.col-6.offset-3.text-center.nav
.nav-item(@click='selectedPage = "profile"', :class="{active: selectedPage === 'profile'}") Profile
.nav-item(@click='selectedPage = "stats"', :class="{active: selectedPage === 'stats'}") Stats
.nav-item(@click='selectedPage = "achievements"', :class="{active: selectedPage === 'achievements'}") Achievements
.nav-item(@click='selectedPage = "profile"', :class="{active: selectedPage === 'profile'}") {{ $t('profile') }}
.nav-item(@click='selectedPage = "stats"', :class="{active: selectedPage === 'stats'}") {{ $t('stats') }}
.nav-item(@click='selectedPage = "achievements"', :class="{active: selectedPage === 'achievements'}") {{ $t('achievements') }}
#userProfile.standard-page(v-show='selectedPage === "profile"', v-if='user.profile')
.row
.col-8
.header
h1 {{user.profile.name}}
h4
strong User Id:
strong {{ $t('userId') }}:
| {{user._id}}
.col-4
button.btn.btn-secondary(v-if='user._id === userLoggedIn._id', @click='editing = !editing') Edit
button.btn.btn-secondary(v-if='user._id === userLoggedIn._id', @click='editing = !editing') {{ $t('edit') }}
.row(v-if='!editing')
.col-8
.about
h2 About
h2 {{ $t('about') }}
p(v-markdown='user.profile.blurb')
.photo
h2 Photo
h2 {{ $t('photo') }}
img.img-rendering-auto(v-if='user.profile.imageUrl', :src='user.profile.imageUrl')
.col-4
.info
h2 info
h2 {{ $t('info') }}
div
strong Joined:
strong {{ $t('joined') }}:
| {{user.auth.timestamps.created}}
div
strong Total Log Ins:
strong {{ $t('totalLogins') }}:
span {{ $t('totalCheckins', {count: user.loginIncentives}) }}
div
| {{getProgressDisplay()}}
.progress
.progress-bar(role='progressbar', :aria-valuenow='incentivesProgress', aria-valuemin='0', aria-valuemax='100', :style='{width: incentivesProgress + "%"}')
span.sr-only {{ incentivesProgress }}% Complete
span.sr-only {{ incentivesProgress }}% {{$t('complete')}}
// @TODO: Implement in V2 .social
.row(v-if='editing')
h1 Edit Profile
h1 {{$t('editProfile')}}
.col-12
.alert.alert-info.alert-sm(v-html='$t("communityGuidelinesWarning", managerEmail)')
@@ -87,7 +87,6 @@ div
.col-12.text-center
button.btn.btn-primary(@click='save()') {{ $t("save") }}
button.btn.btn-warning(@click='editing = false') {{ $t("cancel") }}
#achievements.standard-page.container(v-show='selectedPage === "achievements"', v-if='user.achievements')
.row(v-for='(category, key) in achievements')
h2.col-12.text-center {{ $t(key+'Achievs') }}
@@ -107,94 +106,93 @@ div
.row
.col-6(v-if='user.achievements.challenges')
.achievement-icon.achievement-alien
h2.text-center Challeges Won
h2.text-center {{$t('challengesWon')}}
div(v-for='chal in user.achievements.challenges')
span {{chal}}
hr
.col-6(v-if='user.achievements.quests')
.achievement-icon.achievement-karaoke
h2.text-center Quests Completed
h2.text-center {{$t('questsCompleted')}}
div(v-for='(value, key) in user.achievements.quests')
span {{ content.quests[key].text() }}
span {{ value }}
#stats.standard-page(v-show='selectedPage === "stats"', v-if='user.preferences')
.row
.col-6
h2.text-center Equipment
.col-6 {{$t('equipment')}}
h2.text-center
.well
.col-4.item-wrapper
.box(:class='{white: equippedItems.eyewear}')
div(:class="`shop_${equippedItems.eyewear}`")
h3 Eyewear
h3 {{$t('eyewear')}}
.col-4.item-wrapper
.box(:class='{white: equippedItems.head}')
div(:class="`shop_${equippedItems.head}`")
h3 Head Gear
h3 {{$t('headGear')}}
.col-4.item-wrapper
.box(:class='{white: equippedItems.headAccessory}')
div(:class="`shop_${equippedItems.headAccessory}`")
h3 Head Access.
h3 {{$t('headAccess')}}
.col-4.item-wrapper
.box(:class='{white: equippedItems.backAccessory}')
div(:class="`shop_${equippedItems.backAccessory}`")
h3 Back Access.
h3 {{$t('backAccess')}}
.col-4.item-wrapper
.box(:class='{white: equippedItems.armor}')
div(:class="`shop_${equippedItems.armor}`")
h3 Armor
h3 {{$t('armor')}}
.col-4.item-wrapper
.box(:class='{white: equippedItems.bodyAccessory}')
div(:class="`shop_${equippedItems.bodyAccessory}`")
h3 Body Access.
h3 {{$t('bodyAccess')}}
.col-4.item-wrapper
.box(:class='{white: equippedItems.weapon}')
div(:class="`shop_${equippedItems.weapon}`")
h3 Main-Hand
h3 {{$t('mainHand')}}
.col-4.item-wrapper
.col-4.item-wrapper
.box(:class='{white: equippedItems.shield}')
div(:class="`shop_${equippedItems.shield}`")
h3 Off-Hand
h3 {{$t('offHand')}}
.col-6
h2.text-center Costume
h2.text-center {{$t('costume')}}
.well
.col-4.item-wrapper
.box(:class='{white: costumeItems.eyewear}')
div(:class="`shop_${costumeItems.eyewear}`")
h3 Eyewear
h3 {{$t('eyewear')}}
.col-4.item-wrapper
.box(:class='{white: costumeItems.head}')
div(:class="`shop_${costumeItems.head}`")
h3 Head Gear
h3 {{$t('headGear')}}
.col-4.item-wrapper
.box(:class='{white: costumeItems.headAccessory}')
div(:class="`shop_${costumeItems.headAccessory}`")
h3 Head Access.
h3 {{$t('headAccess')}}
.col-4.item-wrapper
.box(:class='{white: costumeItems.backAccessory}')
div(:class="`shop_${costumeItems.backAccessory}`")
h3 Back Access.
h3 {{$t('backAccess')}}
.col-4.item-wrapper
.box(:class='{white: costumeItems.armor}')
div(:class="`shop_${costumeItems.armor}`")
h3 Armor
h3 {{$t('armor')}}
.col-4.item-wrapper
.box(:class='{white: costumeItems.bodyAccessory}')
div(:class="`shop_${costumeItems.bodyAccessory}`")
h3 Body Access.
h3 {{$t('bodyAccess')}}
.col-4.item-wrapper
.box(:class='{white: costumeItems.weapon}')
div(:class="`shop_${costumeItems.weapon}`")
h3 Main-Hand
h3 {{$t('mainHand')}}
.col-4.item-wrapper
.box(:class='{white: user.preferences.background}')
div(:class="user.preferences.background")
h3 Background
h3 {{$t('background')}}
.col-4.item-wrapper
.box(:class='{white: costumeItems.shield}')
div(:class="`shop_${costumeItems.shield}`")
h3 Off-Hand
h3 {{$t('offHand')}}
.row.pet-mount-row
.col-6
h2.text-center(v-once) {{ $t('pets') }}
@@ -230,7 +228,7 @@ div
span {{ mountMasterProgress(user.items.mounts) }}
#attributes.row
hr.col-12
h2.col-12 Attributes
h2.col-12 {{$t('attributes')}}
.col-6(v-for="(statInfo, stat) in stats")
.row.col-12.stats-column
.col-4.attribute-label
@@ -241,31 +239,33 @@ div
.col-6
ul.bonus-stats
li
strong Level:
strong {{$t('level')}}:
| {{statsComputed.levelBonus[stat]}}
li
strong Equipment:
strong {{$t('equipment')}}:
| {{statsComputed.gearBonus[stat]}}
li
strong Class:
strong {{$t('class')}}:
| {{statsComputed.classBonus[stat]}}
li
strong Allocated:
strong {{$t('allocated')}}:
| {{user.stats[stat]}}
li
strong Buffs:
strong {{$t('buffs')}}:
| {{user.stats.buffs[stat]}}
#allocation(v-if='user._id === userLoggedIn._id')
.row.title-row
.col-6
h3(v-if='userLevel100Plus', v-once, v-html="$t('noMoreAllocate')")
h3(v-if='user.stats.points || userLevel100Plus')
| Points Available
| {{$t('pointsAvailable')}}
.counter.badge(v-if='user.stats.points || userLevel100Plus')
| {{user.stats.points}}&nbsp;
.col-6
.float-right
toggle-switch(:label="$t('autoAllocation')", v-model='user.preferences.automaticAllocation', @change='userset({"preferences.automaticAllocation": Boolean(user.preferences.automaticAllocation), "preferences.allocationMode": "taskbased"})')
toggle-switch(:label="$t('autoAllocation')",
v-model='user.preferences.automaticAllocation',
@change='userset({"preferences.automaticAllocation": Boolean(user.preferences.automaticAllocation), "preferences.allocationMode": "taskbased"})')
.row
.col-3(v-for='(statInfo, stat) in allocateStatsList')
@@ -273,10 +273,10 @@ div
.col-12
div(:class='stat') {{ $t(stats[stat].title) }}
.number {{ user.stats[stat] }}
.points pts
.points {{$t('pts')}}
.col-4
.up(v-if='user.stats.points', @click='allocate(stat)')
private-message-modal(:userIdToMessage='userIdToMessage')
private-message-modal
send-gems-modal(:userReceivingGems='userReceivingGems')
</template>
@@ -686,7 +686,7 @@ export default {
},
methods: {
sendMessage () {
this.userIdToMessage = this.user._id;
this.$store.state.userIdToMessage = this.user._id;
this.$root.$emit('show::modal', 'private-message');
},
getProgressDisplay () {

View File

@@ -2,8 +2,10 @@ import axios from 'axios';
let AUTH_SETTINGS = localStorage.getItem('habit-mobile-settings');
let API_TOKEN = '';
let API_ID = '';
if (AUTH_SETTINGS) {
AUTH_SETTINGS = JSON.parse(AUTH_SETTINGS);
API_ID = AUTH_SETTINGS.auth.apiId;
API_TOKEN = AUTH_SETTINGS.auth.apiToken;
}
@@ -15,7 +17,16 @@ let StripeCheckout = window.StripeCheckout;
export default {
computed: {
paypalCheckoutLink () {
return `/paypal/checkout?_id=${this.user._id}&apiToken=${API_TOKEN}`;
return `/paypal/checkout?_id=${API_ID}&apiToken=${API_TOKEN}`;
},
paypalSubscriptionLink () {
return `/paypal/subscribe?_id=${API_ID}&apiToken=${API_TOKEN}&sub=${this.subscriptionPlan}`;
},
paypalPurchaseLink () {
if (!this.subscription) return '';
let couponString = '';
if (this.subscription.coupon) couponString = `&coupon=${this.subscription.coupon}`;
return `/paypal/subscribe?_id=${API_ID}&apiToken=${API_TOKEN}&sub=${this.subscription.key}${couponString}`;
},
},
methods: {

View File

@@ -122,6 +122,7 @@ export default function () {
notificationStore: [],
modalStack: [],
afterLoginRedirect: '',
userIdToMessage: '',
},
});

View File

@@ -199,5 +199,25 @@
"invalidAttribute": "\"<%= attr %>\" is not a valid attribute.",
"notEnoughAttrPoints": "You don't have enough attribute points.",
"style": "Style",
"facialhair": "Facial"
"facialhair": "Facial",
"photo": "Photo",
"info": "Info",
"joined": "Joined",
"totalLogins": "Total Log Ins",
"editProfile": "Edit Profile",
"challengesWon": "Challeges Won",
"questsCompleted": "Quests Completed",
"equipment": "Equipment",
"costume": "Costume",
"headGear": "Head Gear",
"headAccess": "Head Access.",
"backAccess": "Back Access.",
"bodyAccess": "Body Access.",
"mainHand": "Main-Hand",
"offHand": "Off-Hand",
"level": "Level",
"allocated": "Allocated",
"buffs": "Buffs",
"pointsAvailable": "Points Available",
"pts": "pts"
}

View File

@@ -282,5 +282,30 @@
"confirmPasswordPlaceholder": "Make sure it's the same password!",
"joinHabitica": "Join Habitica",
"alreadyHaveAccountLogin": "Already have a Habitica account? <strong>Log in.</strong>",
"dontHaveAccountSignup": "Dont have a Habitica account? <strong>Sign up.</strong>"
"dontHaveAccountSignup": "Dont have a Habitica account? <strong>Sign up.</strong>",
"motivateYourself": "Motivate yourself to achieve your goals.",
"timeToGetThingsDone": "It's time to have fun when you get things done! Join over 2 million Habiticans and improve your life one task at a time.",
"singUpForFree": "Sign Up For Free",
"or": "OR",
"gamifyYourLife": "Gamify Your Life",
"aboutHabitica": "Habitica is a free habit-building and productivity app that treats your real life like a game. With in-game rewards and punishments to motivate you and a strong social network to inspire you, Habitica can help you achieve your goals to become healthy, hard-working, and happy.",
"trackYourGoals": "Track Your Habits and Goals",
"trackYourGoalsDesc": "Stay accountable by tracking and managing your Habits, Daily goals, and To-Do list with Habiticas easy-to-use mobile apps and web interface.",
"earnRewards": "Earn Rewards for Your Goals",
"earnRewardsDesc": "Check off tasks to level up your Avatar and unlock in-game features such as battle armor, mysterious pets, magic skills, and even quests!",
"battleMonsters": "Battle Monsters with Friends",
"battleMonstersDesc": "Fight monsters with other Habiticans! Use the Gold that you earn to buy in-game or custom rewards, like watching an episode of your favorite TV show.",
"playersUseToImprove": "Players Use Habitica to Improve",
"healthAndFitness": "Health and Fitness",
"healthAndFitnessDesc": "Never motivated to floss? Can't seem to get to the gym? Habitica finally makes it fun to get healthy.",
"schoolAndWork": "School and Work",
"schoolAndWorkDesc": "Whether you're preparing a report for your teacher or your boss, it's easy to keep track of your progress as you tackle your toughest tasks.",
"muchmuchMore": "And much, much more!",
"muchmuchMoreDesc": "Our fully customizable task list means that you can shape Habitica to fit your personal goals. Work on creative projects, emphasize self-care, or pursue a different dream -- it's all up to you.",
"levelUpAnywhere": "Level Up Anywhere",
"levelUpAnywhereDesc": "Our mobile apps make it simple to keep track of your tasks on-the-go. Accomplish your goals with a single tap, no matter where you are.",
"joinMany": "Join over 2,000,000 people having fun while accomplishing their goals!",
"joinToday": "Join Habitica Today",
"featuredIn": "Featured in",
"signup": "Sign Up"
}

View File

@@ -68,7 +68,6 @@
"subscriberItemText": "Each month, subscribers will receive a mystery item. This is usually released about one week before the end of the month. See the wiki's 'Mystery Item' page for more information.",
"all": "All",
"none": "None",
"or": "Or",
"and": "and",
"loginSuccess": "Login successful!",
"youSure": "Are you sure?",
@@ -279,5 +278,6 @@
"messages": "Messages",
"emptyMessagesLine1": "You don't have any messages",
"emptyMessagesLine2": "Send a message to start a conversation!",
"letsgo": "Let's Go!"
"letsgo": "Let's Go!",
"selected": "Selected"
}