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') .svg-icon.gem(v-html='icons.gem')
span 2 span 2
.col-12.text-center .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"') #skin.section.customize-section(v-if='activeTopPage === "skin"')
.row.sub-menu.col-6.offset-3.text-center .row.sub-menu.col-6.offset-3.text-center
.col-6.offset-3.text-center.sub-menu-item(:class='{active: activeSubPage === "color"}') .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"]', .option(v-for='option in ["ddc994", "f5a76e", "ea8349", "c06534", "98461a", "915533", "c3e1dc", "6bd049"]',
:class='{active: user.preferences.skin === option}') :class='{active: user.preferences.skin === option}')
.skin.sprite.customize-option(:class="`skin_${option}`", @click='set({"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 .col-12.customize-options
.option(v-for='option in rainbowSkins', //h3(v-if='!hideSet(set)') {{$t(set.key)}}
:class='{active: option.active, locked: option.locked}') .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') .skin.sprite.customize-option(:class="`skin_${option.key}`", @click='option.click')
.gem-lock(v-if='option.locked') .gem-lock(v-if='option.locked')
.svg-icon.gem(v-html='icons.gem') .svg-icon.gem(v-html='icons.gem')
span 2 span 2
.col-12.text-center .col-12.text-center
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("skin", rainbowSkinKeys)', @click='unlock(`skin.${rainbowSkinKeys.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') }}
.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')
#hair.section.customize-section(v-if='activeTopPage === "hair"') #hair.section.customize-section(v-if='activeTopPage === "hair"')
.row.sub-menu.col-6.offset-3.text-center .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"}') .col-2.offset-1.text-center.sub-menu-item(@click='changeSubPage("color")', :class='{active: activeSubPage === "color"}')
@@ -101,20 +92,33 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
strong(v-once) {{$t('ponytail')}} strong(v-once) {{$t('ponytail')}}
.col-2.text-center.sub-menu-item(@click='changeSubPage("facialhair")', :class='{active: activeSubPage === "facialhair"}', v-if='editing') .col-2.text-center.sub-menu-item(@click='changeSubPage("facialhair")', :class='{active: activeSubPage === "facialhair"}', v-if='editing')
strong(v-once) {{$t('facialhair')}} strong(v-once) {{$t('facialhair')}}
#hair-color.row(v-if='activeSubPage === "color"') #hair-color.section.customize-section(v-if='activeSubPage === "color"')
.col-12.customize-options .row
.option(v-for='option in ["white", "brown", "blond", "red", "black"]', .col-12.customize-options
:class='{active: user.preferences.hair.color === option}') .option(v-for='option in ["white", "brown", "blond", "red", "black"]',
.color-bangs.sprite.customize-option(:class="`hair_bangs_1_${option}`", @click='set({"preferences.hair.color": option})') :class='{active: user.preferences.hair.color === option}')
.col-12.customize-options(v-if='editing') .color-bangs.sprite.customize-option(:class="`hair_bangs_1_${option}`", @click='set({"preferences.hair.color": option})')
.option(v-for='option in premiumHairColors', //.row(v-if='editing')
:class='{active: option.active === option, locked: option.locked}') .col-12.customize-options(v-if='editing')
.color-bangs.sprite.customize-option(:class="`hair_bangs_1_${option.key}`", @click='option.click') .option(v-for='option in premiumHairColors',
.gem-lock(v-if='option.locked') :class='{active: option.active === option, locked: option.locked}')
.svg-icon.gem(v-html='icons.gem') .color-bangs.sprite.customize-option(:class="`hair_bangs_1_${option.key}`", @click='option.click')
span 2 .gem-lock(v-if='option.locked')
.col-12.text-center .svg-icon.gem(v-html='icons.gem')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", premiumHairColorKeys, "color")', @click='unlock(`hair.color.${premiumHairColorKeys.join(",hair.color.")}`)') $t('purchaseAll') 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') }}
.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"') #style.row(v-if='activeSubPage === "style"')
.col-12.customize-options(v-if='editing') .col-12.customize-options(v-if='editing')
.option(v-for='option in baseHair3', .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') .svg-icon.gem(v-html='icons.gem')
span 2 span 2
.col-12.text-center .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') .col-12.customize-options(v-if='editing')
.option(v-for='option in baseHair4', .option(v-for='option in baseHair4',
:class='{active: option.active, locked: option.locked}') :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') .svg-icon.gem(v-html='icons.gem')
span 2 span 2
.col-12.text-center .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"') #bangs.row(v-if='activeSubPage === "bangs"')
.col-12.customize-options .col-12.customize-options
.head_0.option(@click='set({"preferences.hair.bangs": 0})', .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') .svg-icon.gem(v-html='icons.gem')
span 2 span 2
.col-12.text-center .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"') #facialhair.row(v-if='activeSubPage === "facialhair"')
.col-12.customize-options(v-if='editing') .col-12.customize-options(v-if='editing')
.option(v-for='option in baseHair5', .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') .svg-icon.gem(v-html='icons.gem')
span 2 span 2
.col-12.text-center .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') .col-12.customize-options(v-if='editing')
.option(v-for='option in baseHair6', .option(v-for='option in baseHair6',
:class='{active: option.active, locked: option.locked}') :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') .svg-icon.gem(v-html='icons.gem')
span 2 span 2
.col-12.text-center .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"') #extra.section.container.customize-section(v-if='activeTopPage === "extra"')
.row.sub-menu.col-6.offset-3.text-center .row.sub-menu.col-6.offset-3.text-center
.col-4.text-center.sub-menu-item(@click='changeSubPage("glasses")', :class='{active: activeSubPage === "glasses"}') .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') .svg-icon.gem(v-html='icons.gem')
span 2 span 2
.col-12.text-center .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"') #wheelchairs.row(v-if='activeSubPage === "wheelchair"')
.col-12.customize-options.weelchairs .col-12.customize-options.weelchairs
.option(@click='set({"preferences.chair": "none"})', :class='{active: user.preferences.chair === "none"}') .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); 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 { .customize-options .option {
display: inline-block; display: inline-block;
vertical-align: bottom; vertical-align: bottom;
@@ -756,14 +764,18 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
<script> <script>
import axios from 'axios'; import axios from 'axios';
import moment from 'moment';
import map from 'lodash/map'; import map from 'lodash/map';
import get from 'lodash/get'; import get from 'lodash/get';
import groupBy from 'lodash/groupBy';
import { mapState } from 'client/libs/store'; import { mapState } from 'client/libs/store';
import avatar from './avatar'; import avatar from './avatar';
import { getBackgroundShopSets } from '../../common/script/libs/shops'; import { getBackgroundShopSets } from '../../common/script/libs/shops';
import unlock from '../../common/script/ops/unlock'; import unlock from '../../common/script/ops/unlock';
import guide from 'client/mixins/guide'; import guide from 'client/mixins/guide';
import notifications from 'client/mixins/notifications'; 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'; 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 pin from 'assets/svg/pin.svg';
import isPinned from 'common/script/libs/isPinned'; import isPinned from 'common/script/libs/isPinned';
const skinsBySet = groupBy(appearance.skin, 'set.key');
const hairColorBySet = groupBy(appearance.hair.color, 'set.key');
let tasksByCategory = { let tasksByCategory = {
work: [ work: [
{ {
@@ -1029,6 +1044,70 @@ export default {
}); });
return options; 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 () { premiumHairColors () {
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now // @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 backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
@@ -1117,6 +1196,9 @@ export default {
}, },
}, },
methods: { methods: {
hideSet (set) {
return moment(appearanceSets[set.key].availableUntil).isBefore(moment());
},
purchase (type, key) { purchase (type, key) {
this.$store.dispatch('shops:purchase', { this.$store.dispatch('shops:purchase', {
type, type,
@@ -1124,16 +1206,22 @@ export default {
}); });
this.backgroundUpdate = new Date(); 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 userPreference = subType ? this.user.preferences[type][subType] : this.user.preferences[type];
let userPurchased = subType ? this.user.purchased[type][subType] : this.user.purchased[type]; let userPurchased = subType ? this.user.purchased[type][subType] : this.user.purchased[type];
let locked = !userPurchased || !userPurchased[key]; let locked = !userPurchased || !userPurchased[key];
let pathKey = subType ? `${type}.${subType}` : `${type}`; let pathKey = subType ? `${type}.${subType}` : `${type}`;
let hide = false;
if (set && appearanceSets[set]) {
if (locked) hide = moment(appearanceSets[set].availableUntil).isBefore(moment());
}
let option = {}; let option = {};
option.key = key; option.key = key;
option.active = userPreference === key; option.active = userPreference === key;
option.locked = locked; option.locked = locked;
option.hide = hide;
option.click = () => { option.click = () => {
return locked ? this.unlock(`${pathKey}.${key}`) : this.set({[`preferences.${pathKey}`]: key}); return locked ? this.unlock(`${pathKey}.${key}`) : this.set({[`preferences.${pathKey}`]: key});
}; };

View File

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

View File

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

View File

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

View File

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

View File

@@ -82,7 +82,7 @@
a.purchase(:href='paypalPurchaseLink', :disabled='!subscription.key', target='_blank') 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')") img(src='https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png', :alt="$t('paypal')")
.col-md-4 .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')") img(src='https://payments.amazon.com/gp/cba/button', :alt="$t('amazonPayments')")
.row .row
@@ -153,12 +153,6 @@ export default {
}, },
computed: { computed: {
...mapState({user: 'user.data'}), ...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 () { subscriptionBlocksOrdered () {
let subscriptions = filter(subscriptionBlocks, (o) => { let subscriptions = filter(subscriptionBlocks, (o) => {
return o.discount !== true; return o.discount !== true;

View File

@@ -9,10 +9,10 @@
.row .row
.col-6 .col-6
img(src='~assets/images/home/home-main@3x.png', width='357px') img(src='~assets/images/home/home-main@3x.png', width='357px')
h1 Motivate yourself to achieve your goals. h1 {{$t('motivateYourself')}}
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. p.section-main {{$t('timeToGetThingsDone')}}
.col-6 .col-6
h3.text-center Sign Up For Free h3.text-center {{$t('singUpForFree')}}
div.text-center div.text-center
button.social-button(@click='socialAuth("facebook")') button.social-button(@click='socialAuth("facebook")')
.svg-icon.social-icon(v-html="icons.facebookIcon") .svg-icon.social-icon(v-html="icons.facebookIcon")
@@ -21,14 +21,14 @@
.svg-icon.social-icon(v-html="icons.googleIcon") .svg-icon.social-icon(v-html="icons.googleIcon")
span {{$t('signUpWithSocial', {social: 'Google'})}} span {{$t('signUpWithSocial', {social: 'Google'})}}
.strike .strike
span OR span {{$t('or')}}
.form .form
input.form-control(type='text', placeholder='Login Name', v-model='username', :class='{"input-valid": username.length > 0}') 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='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='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}') 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')") 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 .col-12
.spacer.svg-icon(v-html='icons.spacer') .spacer.svg-icon(v-html='icons.spacer')
@@ -38,21 +38,21 @@
.container .container
.row .row
.col-6.offset-3.text-center .col-6.offset-3.text-center
h2 Gamify Your Life h2 {{$t('gamifyYourLife')}}
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. p.section-main {{$t('aboutHabitica')}}
.row .row
.col-4 .col-4
img.track-habits(src='~assets/images/home/track-habits@3x.png', width='354px', height='228px') img.track-habits(src='~assets/images/home/track-habits@3x.png', width='354px', height='228px')
strong Track Your Habits and Goals strong {{$t('trackYourGoals')}}
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. p {{$t('trackYourGoalsDesc')}}
.col-4 .col-4
img(src='~assets/images/home/earn-rewards@3x.png', width='316px', height='244px') img(src='~assets/images/home/earn-rewards@3x.png', width='316px', height='244px')
strong Earn Rewards for Your Goals strong {{$t('earnRewards')}}
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! p {{$t('earnRewardsDesc')}}
.col-4 .col-4
img(src='~assets/images/home/battle-monsters@3x.png', width='303px', height='244px') img(src='~assets/images/home/battle-monsters@3x.png', width='303px', height='244px')
strong Battle Monsters with Friends strong {{$t('battleMonsters')}}
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. p {{$t('battleMonstersDesc')}}
.col-12 .col-12
.spacer.svg-icon(v-html='icons.spacer') .spacer.svg-icon(v-html='icons.spacer')
@@ -60,20 +60,20 @@
.container.text-center .container.text-center
.row .row
.col-12 .col-12
h2 Players Use Habitica to Improve h2 {{$t('playersUseToImprove')}}
.row .row
.col-4 .col-4
img(src='~assets/images/home/health-fitness@3x.png', width='300px', height='300px') img(src='~assets/images/home/health-fitness@3x.png', width='300px', height='300px')
strong Health and Fitness strong {{$t('healthAndFitness')}}
p Never motivated to floss? Can't seem to get to the gym? Habitica finally makes it fun to get healthy. p {{$t('healthAndFitnessDesc')}}
.col-4 .col-4
img(src='~assets/images/home/school-work@3x.png', width='300px', height='300px') img(src='~assets/images/home/school-work@3x.png', width='300px', height='300px')
strong School and Work strong {{$t('schoolAndWork')}}
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. p {{$t('schoolAndWorkDesc')}}
.col-4 .col-4
img(src='~assets/images/home/much-more@3x.png', width='300px', height='300px') img(src='~assets/images/home/much-more@3x.png', width='300px', height='300px')
strong And much, much more! strong {{$t('muchmuchMore')}}
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. p {{$t('muchmuchMoreDesc')}}
.col-12 .col-12
.spacer.svg-icon(v-html='icons.spacer') .spacer.svg-icon(v-html='icons.spacer')
.container-fluid .container-fluid
@@ -85,8 +85,8 @@
.col-6 .col-6
.iphones.svg-icon(v-html='icons.iphones') .iphones.svg-icon(v-html='icons.iphones')
.col-6.text-column .col-6.text-column
h2 Level Up Anywhere h2 {{ $t('levelUpAnywhere') }}
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. 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.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') 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 .container-fluid
@@ -95,13 +95,13 @@
#call-to-action.purple-4 #call-to-action.purple-4
.container.featured .container.featured
.row.text-center .row.text-center
h3.col-12 Join over 2,000,000 people having fun while accomplishing their goals! h3.col-12 {{ $t('joinMany') }}
.row .row
.col-12.text-center .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 .row.featured
.col-12.text-center .col-12.text-center
strong Featured in strong {{ $t('featuredIn') }}
.container-fluid.featured .container-fluid.featured
.row .row
.col-12 .col-12
@@ -115,11 +115,6 @@
img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/discover-logo.png', alt="$t(altAttrDiscover)") img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/discover-logo.png', alt="$t(altAttrDiscover)")
.container-fluid .container-fluid
.seamless_stars_varied_opacity_repeat .seamless_stars_varied_opacity_repeat
#bottom-wrap.purple-4
#bottom-background
.seamless_mountains_demo_repeat
.midground_foreground_extended2
</template> </template>
<style lang="scss"> <style lang="scss">
@@ -240,13 +235,19 @@
.social-button { .social-button {
border-radius: 2px; border-radius: 2px;
border: solid 2px #bda8ff; border: solid 2px #bda8ff;
width: 206px; width: 48%;
height: 40px; min-height: 40px;
padding: .5em;
background: transparent; background: transparent;
margin-right: .5em; margin-right: .5em;
color: #fff; color: #fff;
} }
.social-button:hover {
cursor: pointer;
border-color: #fff;
}
.social-icon { .social-icon {
margin-right: 1em; margin-right: 1em;
width: 18px; width: 18px;
@@ -290,17 +291,28 @@
margin-left: 15px; margin-left: 15px;
} }
.form {
padding-top: 1em;
padding-bottom: 1em;
}
input { input {
margin-bottom: 1em; margin-bottom: 1em;
border-radius: 2px; border-radius: 2px;
background-color: #432874; background-color: #432874;
border-color: #432874; border-color: #432874;
color: $purple-400; color: $purple-400;
border: solid 2px transparent;
} }
input:focus { input:focus {
border: solid 2px #9a62ff; border: solid 2px #9a62ff;
color: #fff; color: #fff;
background-color: #36205d;
}
input:hover {
background-color: #36205d;
} }
button.sign-up { button.sign-up {
@@ -469,33 +481,6 @@
width: 100%; 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> </style>
<script> <script>

View File

@@ -7,11 +7,20 @@
#purple-footer #purple-footer
app-footer app-footer
#bottom-wrap.purple-4
#bottom-background
.seamless_mountains_demo_repeat
.midground_foreground_extended2
</template> </template>
<style lang='scss'> <style lang='scss'>
@import '~client/assets/scss/colors.scss'; @import '~client/assets/scss/colors.scss';
.purple-4 {
background-color: #271b3d;
}
#purple-footer { #purple-footer {
background-color: #271b3d; 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 { .static-wrapper {
.container-fluid { .container-fluid {
margin: 5em 2em 2em 2em; margin: 5em 2em 2em 2em;

View File

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

View File

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

View File

@@ -2,8 +2,10 @@ import axios from 'axios';
let AUTH_SETTINGS = localStorage.getItem('habit-mobile-settings'); let AUTH_SETTINGS = localStorage.getItem('habit-mobile-settings');
let API_TOKEN = ''; let API_TOKEN = '';
let API_ID = '';
if (AUTH_SETTINGS) { if (AUTH_SETTINGS) {
AUTH_SETTINGS = JSON.parse(AUTH_SETTINGS); AUTH_SETTINGS = JSON.parse(AUTH_SETTINGS);
API_ID = AUTH_SETTINGS.auth.apiId;
API_TOKEN = AUTH_SETTINGS.auth.apiToken; API_TOKEN = AUTH_SETTINGS.auth.apiToken;
} }
@@ -15,7 +17,16 @@ let StripeCheckout = window.StripeCheckout;
export default { export default {
computed: { computed: {
paypalCheckoutLink () { 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: { methods: {

View File

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

View File

@@ -199,5 +199,25 @@
"invalidAttribute": "\"<%= attr %>\" is not a valid attribute.", "invalidAttribute": "\"<%= attr %>\" is not a valid attribute.",
"notEnoughAttrPoints": "You don't have enough attribute points.", "notEnoughAttrPoints": "You don't have enough attribute points.",
"style": "Style", "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!", "confirmPasswordPlaceholder": "Make sure it's the same password!",
"joinHabitica": "Join Habitica", "joinHabitica": "Join Habitica",
"alreadyHaveAccountLogin": "Already have a Habitica account? <strong>Log in.</strong>", "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.", "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", "all": "All",
"none": "None", "none": "None",
"or": "Or",
"and": "and", "and": "and",
"loginSuccess": "Login successful!", "loginSuccess": "Login successful!",
"youSure": "Are you sure?", "youSure": "Are you sure?",
@@ -279,5 +278,6 @@
"messages": "Messages", "messages": "Messages",
"emptyMessagesLine1": "You don't have any messages", "emptyMessagesLine1": "You don't have any messages",
"emptyMessagesLine2": "Send a message to start a conversation!", "emptyMessagesLine2": "Send a message to start a conversation!",
"letsgo": "Let's Go!" "letsgo": "Let's Go!",
"selected": "Selected"
} }