Oct 19 fixes (#9234)

* Add more checks for user achievements

* Began to add ajax request for bailey

* Prevented purchase of locked item

* Refactored notifications to have unique id and use actions

* Added feature banner when gear is bought

* Removed debug code

* Mark group notifications as read

* Fixed lint issues

* Added gem icon to purchase all
This commit is contained in:
Keith Holliday 2017-10-20 15:22:13 +02:00 committed by GitHub
parent 6838b7d0a6
commit 506275c30e
15 changed files with 161 additions and 85 deletions

View file

@ -172,7 +172,7 @@ export default {
return Promise.resolve(error); return Promise.resolve(error);
} }
this.$store.state.notificationStore.push({ this.$store.dispatch('snackbars:add', {
title: 'Habitica', title: 'Habitica',
text: error.response.data.message, text: error.response.data.message,
type: 'error', type: 'error',

View file

@ -22,6 +22,7 @@
</style> </style>
<script> <script>
// import axios from 'axios';
import bModal from 'bootstrap-vue/lib/components/modal'; import bModal from 'bootstrap-vue/lib/components/modal';
import { mapState } from 'client/libs/store'; import { mapState } from 'client/libs/store';
import markdown from 'client/directives/markdown'; import markdown from 'client/directives/markdown';
@ -38,6 +39,16 @@
directives: { directives: {
markdown, markdown,
}, },
mounted () {
this.$root.$on('show::modal', async (modalId) => {
if (modalId !== 'new-stuff') return;
// Request the lastest news, but not locally incase they don't refresh
// let response = await axios.get('/static/new-stuff');
});
},
destroyed () {
this.$root.$off('show::modal');
},
methods: { methods: {
close () { close () {
this.$root.$emit('hide::modal', 'new-stuff'); this.$root.$emit('hide::modal', 'new-stuff');

View file

@ -58,8 +58,11 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.gem-lock(v-if='item.locked') .gem-lock(v-if='item.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(v-if='!userOwnsSet("shirt", specialShirtKeys)')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("shirt", specialShirtKeys)', @click='unlock(`shirt.${specialShirtKeys.join(",shirt.")}`)') {{ $t('purchaseAll') }} .gem-lock
.svg-icon.gem(v-html='icons.gem')
span 5
button.btn.btn-secondary.purchase-all(@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"}')
@ -77,8 +80,11 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.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(v-if='!hideSet(set) && !userOwnsSet("skin", set.keys)')
button.btn.btn-secondary.purchase-all(v-if='!hideSet(set) && !userOwnsSet("skin", set.keys)', @click='unlock(`skin.${set.keys.join(",skin.")}`)') {{ $t('purchaseAll') }} .gem-lock
.svg-icon.gem(v-html='icons.gem')
span 5
button.btn.btn-secondary.purchase-all(@click='unlock(`skin.${set.keys.join(",skin.")}`)') {{ $t('purchaseAll') }}
#hair.section.customize-section(v-if='activeTopPage === "hair"') #hair.section.customize-section(v-if='activeTopPage === "hair"')
.row.sub-menu.text-center .row.sub-menu.text-center
.col-3.offset-1.text-center.sub-menu-item(@click='changeSubPage("color")', :class='{active: activeSubPage === "color"}') .col-3.offset-1.text-center.sub-menu-item(@click='changeSubPage("color")', :class='{active: activeSubPage === "color"}')
@ -104,8 +110,11 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.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(v-if='!hideSet(set) && !userOwnsSet("hair", set.keys, "color")')
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') }} .gem-lock
.svg-icon.gem(v-html='icons.gem')
span 5
button.btn.btn-secondary.purchase-all(@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')
.head_0.option(@click='set({"preferences.hair.base": 0})', :class="[{ active: user.preferences.hair.base === 0 }, 'hair_base_0_' + user.preferences.hair.color]") .head_0.option(@click='set({"preferences.hair.base": 0})', :class="[{ active: user.preferences.hair.base === 0 }, 'hair_base_0_' + user.preferences.hair.color]")
@ -115,8 +124,11 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.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(v-if='!userOwnsSet("hair", baseHair3Keys, "base")')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair3Keys, "base")', @click='unlock(`hair.base.${baseHair3Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }} .gem-lock
.svg-icon.gem(v-html='icons.gem')
span 5
button.btn.btn-secondary.purchase-all(@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}')
@ -124,8 +136,11 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.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(v-if='!userOwnsSet("hair", baseHair4Keys, "base")')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair4Keys, "base")', @click='unlock(`hair.base.${baseHair4Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }} .gem-lock
.svg-icon.gem(v-html='icons.gem')
span 5
button.btn.btn-secondary.purchase-all(@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})',
@ -146,8 +161,11 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.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(v-if='!userOwnsSet("hair", baseHair2Keys, "base")')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair2Keys, "base")', @click='unlock(`hair.base.${baseHair2Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }} .gem-lock
.svg-icon.gem(v-html='icons.gem')
span 5
button.btn.btn-secondary.purchase-all(@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')
.head_0.option(@click='set({"preferences.hair.beard": 0})', :class="[{ active: user.preferences.hair.beard === 0 }, 'hair_base_0_' + user.preferences.hair.color]") .head_0.option(@click='set({"preferences.hair.beard": 0})', :class="[{ active: user.preferences.hair.beard === 0 }, 'hair_base_0_' + user.preferences.hair.color]")
@ -157,8 +175,11 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.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(v-if='!userOwnsSet("hair", baseHair5Keys, "beard")')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair5Keys, "beard")', @click='unlock(`hair.beard.${baseHair5Keys.join(",hair.beard.")}`)') {{ $t('purchaseAll') }} .gem-lock
.svg-icon.gem(v-html='icons.gem')
span 5
button.btn.btn-secondary.purchase-all(@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}')
@ -166,8 +187,11 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.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(v-if='!userOwnsSet("hair", baseHair6Keys, "mustache")')
button.btn.btn-secondary.purchase-all(v-if='!userOwnsSet("hair", baseHair6Keys, "mustache")', @click='unlock(`hair.mustache.${baseHair6Keys.join(",hair.mustache.")}`)') {{ $t('purchaseAll') }} .gem-lock
.svg-icon.gem(v-html='icons.gem')
span 5
button.btn.btn-secondary.purchase-all(@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 .row.sub-menu
.col-3.offset-1.text-center.sub-menu-item(@click='changeSubPage("glasses")', :class='{active: activeSubPage === "glasses"}') .col-3.offset-1.text-center.sub-menu-item(@click='changeSubPage("glasses")', :class='{active: activeSubPage === "glasses"}')
@ -191,8 +215,11 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.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(v-if='!animalEarsOwned')
button.btn.btn-secondary.purchase-all(v-if='!animalEarsOwned', @click='unlock(animalEarsUnlockString)') {{ $t('purchaseAll') }} .gem-lock
.svg-icon.gem(v-html='icons.gem')
span 5
button.btn.btn-secondary.purchase-all(@click='unlock(animalEarsUnlockString)') {{ $t('purchaseAll') }}
#wheelchairs.row(v-if='activeSubPage === "wheelchair"') #wheelchairs.row(v-if='activeSubPage === "wheelchair"')
.col-12.customize-options .col-12.customize-options
.option(@click='set({"preferences.chair": "none"})', :class='{active: user.preferences.chair === "none"}') .option(@click='set({"preferences.chair": "none"})', :class='{active: user.preferences.chair === "none"}')
@ -258,7 +285,7 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.col-12.text-center(v-if='!ownsSet("background", set.items) && set.identifier !== "incentiveBackgrounds"') .col-12.text-center(v-if='!ownsSet("background", set.items) && set.identifier !== "incentiveBackgrounds"')
.gem-amount .gem-amount
.svg-icon.gem(v-html='icons.gem') .svg-icon.gem(v-html='icons.gem')
span 15 span 5
button.btn.btn-secondary(@click='unlock(setKeys("background", set.items))') Purchase Set button.btn.btn-secondary(@click='unlock(setKeys("background", set.items))') Purchase Set
.container.interests-section(v-if='modalPage === 3 && !editing') .container.interests-section(v-if='modalPage === 3 && !editing')
@ -531,28 +558,35 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
margin-bottom: .5em; margin-bottom: .5em;
margin-right: .5em; margin-right: .5em;
.gem-lock {
.svg-icon {
width: 16px;
}
span {
color: #24cc8f;
font-weight: bold;
margin-left: .5em;
}
.svg-icon, span {
display: inline-block;
vertical-align: bottom;
}
}
.sprite.customize-option { .sprite.customize-option {
margin: 0 auto; margin: 0 auto;
} }
} }
.text-center .gem-lock {
display: inline-block;
margin-right: 1em;
margin-bottom: 1.6em;
vertical-align: bottom;
}
.gem-lock {
.svg-icon {
width: 16px;
}
span {
color: #24cc8f;
font-weight: bold;
margin-left: .5em;
}
.svg-icon, span {
display: inline-block;
vertical-align: bottom;
}
}
.option.active { .option.active {
border: 4px solid $purple-200; border: 4px solid $purple-200;
border-radius: 4px; border-radius: 4px;

View file

@ -42,16 +42,11 @@ div.item-with-icon.item-notifications.dropdown
span {{message.name}} span {{message.name}}
span.clear-button(@click='clearMessages(message.key)', :popover="$t('clear')", span.clear-button(@click='clearMessages(message.key)', :popover="$t('clear')",
popover-placement='right', popover-trigger='mouseenter', popover-append-to-body='true') Clear popover-placement='right', popover-trigger='mouseenter', popover-append-to-body='true') Clear
a.dropdown-item(v-for='(notification, index) in user.groupNotifications', @click='viewGroupApprovalNotification(notification, index, true)') a.dropdown-item(v-for='notification in groupNotifications', :key='notification.id')
span(:class="groupApprovalNotificationIcon(notification)") span(:class="groupApprovalNotificationIcon(notification)")
span span {{notification.data.message}}
| {{notification.data.message}} span.clear-button(@click='viewGroupApprovalNotification(notification)', :popover="$t('clear')",
a.dropdown-item(@click='viewGroupApprovalNotification(notification, $index)', popover-placement='right', popover-trigger='mouseenter', popover-append-to-body='true') Clear
:popover="$t('clear')",
popover-placement='right',
popover-trigger='mouseenter',
popover-append-to-body='true')
span.glyphicon.glyphicon-remove-circle
</template> </template>
<style lang='scss' scoped> <style lang='scss' scoped>
@ -159,6 +154,7 @@ div.item-with-icon.item-notifications.dropdown
</style> </style>
<script> <script>
import axios from 'axios';
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
import map from 'lodash/map'; import map from 'lodash/map';
@ -194,6 +190,9 @@ export default {
} }
return userNewMessages; return userNewMessages;
}, },
groupNotifications () {
return this.$store.state.groupNotifications;
},
notificationsCount () { notificationsCount () {
let count = 0; let count = 0;
@ -217,6 +216,8 @@ export default {
count += Object.keys(this.userNewMessages).length; count += Object.keys(this.userNewMessages).length;
} }
count += this.groupNotifications.length;
return count; return count;
}, },
}, },
@ -236,8 +237,8 @@ export default {
return unallocatedValue; return unallocatedValue;
} else if (!isEmpty(user.newMessages)) { } else if (!isEmpty(user.newMessages)) {
return messageValue; return messageValue;
} else if (!isEmpty(user.groupNotifications)) { } else if (!isEmpty(this.groupNotifications)) {
let groupNotificationTypes = map(user.groupNotifications, 'type'); let groupNotificationTypes = map(this.groupNotifications, 'type');
if (groupNotificationTypes.indexOf('GROUP_TASK_APPROVAL') !== -1) { if (groupNotificationTypes.indexOf('GROUP_TASK_APPROVAL') !== -1) {
return groupApprovalRequested; return groupApprovalRequested;
} else if (groupNotificationTypes.indexOf('GROUP_TASK_APPROVED') !== -1) { } else if (groupNotificationTypes.indexOf('GROUP_TASK_APPROVED') !== -1) {
@ -303,11 +304,14 @@ export default {
hasNoNotifications () { hasNoNotifications () {
return this.selectNotificationValue(false, false, false, false, false, true, false, false); return this.selectNotificationValue(false, false, false, false, false, true, false, false);
}, },
viewGroupApprovalNotification (notification, index, navigate) { viewGroupApprovalNotification (notification) {
// @TODO: USe notifications: User.readNotification(notification.id); this.$store.state.groupNotifications = this.groupNotifications.filter(groupNotif => {
this.user.groupNotifications.splice(index, 1); return groupNotif.id !== notification.id;
return navigate; // @TODO: remove });
// @TODO: this.$router.go if (navigate) go('options.social.guilds.detail', {gid: notification.data.groupId});
axios.post('/api/v3/notifications/read', {
notificationIds: [notification.id],
});
}, },
groupApprovalNotificationIcon (notification) { groupApprovalNotificationIcon (notification) {
if (notification.type === 'GROUP_TASK_APPROVAL') { if (notification.type === 'GROUP_TASK_APPROVAL') {

View file

@ -276,8 +276,8 @@ export default {
if (after === before || after === false) return; if (after === before || after === false) return;
this.$root.$emit('show::modal', 'armoire-empty'); this.$root.$emit('show::modal', 'armoire-empty');
}, },
questCompleted (after) { questCompleted () {
if (!after) return; if (!this.questCompleted) return;
this.$root.$emit('show::modal', 'quest-completed'); this.$root.$emit('show::modal', 'quest-completed');
}, },
invitedToQuest (after) { invitedToQuest (after) {
@ -291,6 +291,26 @@ export default {
this.$store.dispatch('user:fetch'), this.$store.dispatch('user:fetch'),
this.$store.dispatch('tasks:fetchUserTasks'), this.$store.dispatch('tasks:fetchUserTasks'),
]).then(() => { ]).then(() => {
this.checkUserAchievements();
// @TODO: This is a timeout to ensure dom is loaded
window.setTimeout(() => {
this.initTour();
if (this.user.flags.tour.intro === this.TOUR_END || !this.user.flags.welcomed) return;
this.goto('intro', 0);
}, 2000);
this.runYesterDailies();
// Do not remove the event listener as it's live for the entire app lifetime
document.addEventListener('mousemove', () => this.checkNextCron());
document.addEventListener('touchstart', () => this.checkNextCron());
document.addEventListener('mousedown', () => this.checkNextCron());
document.addEventListener('keydown', () => this.checkNextCron());
});
},
methods: {
checkUserAchievements () {
// List of prompts for user on changes. Sounds like we may need a refactor here, but it is clean for now // List of prompts for user on changes. Sounds like we may need a refactor here, but it is clean for now
if (this.user.flags.newStuff) { if (this.user.flags.newStuff) {
this.$root.$emit('show::modal', 'new-stuff'); this.$root.$emit('show::modal', 'new-stuff');
@ -313,24 +333,7 @@ export default {
if (this.userClassSelect) { if (this.userClassSelect) {
this.$root.$emit('show::modal', 'choose-class'); this.$root.$emit('show::modal', 'choose-class');
} }
},
// @TODO: This is a timeout to ensure dom is loaded
window.setTimeout(() => {
this.initTour();
if (this.user.flags.tour.intro === this.TOUR_END || !this.user.flags.welcomed) return;
this.goto('intro', 0);
}, 2000);
this.runYesterDailies();
// Do not remove the event listener as it's live for the entire app lifetime
document.addEventListener('mousemove', () => this.checkNextCron());
document.addEventListener('touchstart', () => this.checkNextCron());
document.addEventListener('mousedown', () => this.checkNextCron());
document.addEventListener('keydown', () => this.checkNextCron());
});
},
methods: {
showLevelUpNotifications (newlevel) { showLevelUpNotifications (newlevel) {
this.lvl(); this.lvl();
this.playSound('Level_Up'); this.playSound('Level_Up');
@ -417,8 +420,7 @@ export default {
this.scheduleNextCron(); this.scheduleNextCron();
}, },
transferGroupNotification (notification) { transferGroupNotification (notification) {
if (!this.user.groupNotifications) this.user.groupNotifications = []; this.$store.state.groupNotifications.push(notification);
this.user.groupNotifications.push(notification);
}, },
async handleUserNotifications (after) { async handleUserNotifications (after) {
if (!after || after.length === 0 || !Array.isArray(after)) return; if (!after || after.length === 0 || !Array.isArray(after)) return;
@ -426,7 +428,7 @@ export default {
let notificationsToRead = []; let notificationsToRead = [];
let scoreTaskNotification = []; let scoreTaskNotification = [];
this.user.groupNotifications = []; // Flush group notifictions this.$store.state.groupNotifications = []; // Flush group notifictions
after.forEach((notification) => { after.forEach((notification) => {
if (this.lastShownNotifications.indexOf(notification.id) !== -1) { if (this.lastShownNotifications.indexOf(notification.id) !== -1) {
@ -578,6 +580,8 @@ export default {
} }
this.user.notifications = []; // reset the notifications this.user.notifications = []; // reset the notifications
this.checkUserAchievements();
}, },
}, },
}; };

View file

@ -2,8 +2,6 @@
b-modal#amazon-payment(title="Amazon", :hide-footer="true", size='md') b-modal#amazon-payment(title="Amazon", :hide-footer="true", size='md')
h2.text-center Continue with Amazon h2.text-center Continue with Amazon
#AmazonPayButton #AmazonPayButton
| {{amazonPayments}}
| {{amazonLoggedIn}}
#AmazonPayWallet(v-if="amazonLoggedIn", style="width: 400px; height: 228px;") #AmazonPayWallet(v-if="amazonLoggedIn", style="width: 400px; height: 228px;")
#AmazonPayRecurring(v-if="amazonLoggedIn && amazonPayments.type === 'subscription'", #AmazonPayRecurring(v-if="amazonLoggedIn && amazonPayments.type === 'subscription'",
style="width: 400px; height: 140px;") style="width: 400px; height: 140px;")

View file

@ -40,6 +40,10 @@
span.rectangle span.rectangle
span.text(v-once) {{ $t('featuredset', { name: seasonal.featured.text }) }} span.text(v-once) {{ $t('featuredset', { name: seasonal.featured.text }) }}
span.rectangle span.rectangle
div.featured-label.with-border(v-else)
span.rectangle
span.text(v-once) {{ $t('featuredItems') }}
span.rectangle
div.items.margin-center div.items.margin-center
shopItem( shopItem(
@ -264,6 +268,11 @@
} }
</style> </style>
<style scoped>
.margin-center {
margin: 0 auto;
}
</style>
<script> <script>
import {mapState} from 'client/libs/store'; import {mapState} from 'client/libs/store';

View file

@ -135,9 +135,7 @@ export default {
}, },
watch: { watch: {
show () { show () {
setTimeout(() => { this.$store.dispatch('snackbars:remove', this.notification);
this.$store.state.notificationStore.splice(0, 1);
}, 1000);
}, },
}, },
computed: { computed: {

View file

@ -1,6 +1,6 @@
<template lang="pug"> <template lang="pug">
.notifications .notifications
div(v-for='notification in notifications') div(v-for='notification in notifications', :key='notification.uuid')
notification(:notification='notification') notification(:notification='notification')
</template> </template>

View file

@ -11,7 +11,7 @@
) {{ $t(filter.label) }} ) {{ $t(filter.label) }}
.tasks-list(ref="tasksWrapper") .tasks-list(ref="tasksWrapper")
input.quick-add( input.quick-add(
v-if="isUser", :placeholder="quickAddPlaceholder", v-if="isUser", :placeholder="quickAddPlaceholder",
v-model="quickAddText", @keyup.enter="quickAdd", v-model="quickAddText", @keyup.enter="quickAdd",
ref="quickAdd", ref="quickAdd",
) )
@ -455,6 +455,8 @@ export default {
} }
}, },
openBuyDialog (rewardItem) { openBuyDialog (rewardItem) {
if (rewardItem.locked) return;
// Buy armoire and health potions immediately // Buy armoire and health potions immediately
let itemsToPurchaseImmediately = ['potion', 'armoire']; let itemsToPurchaseImmediately = ['potion', 'armoire'];
if (itemsToPurchaseImmediately.indexOf(rewardItem.key) !== -1) { if (itemsToPurchaseImmediately.indexOf(rewardItem.key) !== -1) {

View file

@ -65,7 +65,7 @@ export default {
return round(number, nDigits); return round(number, nDigits);
}, },
notify (html, type, icon, sign) { notify (html, type, icon, sign) {
this.notifications.push({ this.$store.dispatch('snackbars:add', {
title: '', title: '',
text: html, text: html,
type, type,

View file

@ -14,6 +14,7 @@ import * as notifications from './notifications';
import * as tags from './tags'; import * as tags from './tags';
import * as hall from './hall'; import * as hall from './hall';
import * as shops from './shops'; import * as shops from './shops';
import * as snackbars from './snackbars';
// Actions should be named as 'actionName' and can be accessed as 'namespace:actionName' // Actions should be named as 'actionName' and can be accessed as 'namespace:actionName'
// Example: fetch in user.js -> 'user:fetch' // Example: fetch in user.js -> 'user:fetch'
@ -33,6 +34,7 @@ const actions = flattenAndNamespace({
tags, tags,
hall, hall,
shops, shops,
snackbars,
}); });
export default actions; export default actions;

View file

@ -92,7 +92,7 @@ export async function genericPurchase (store, params) {
} }
// @TODO: We might need to abstract notifications to library rather than mixin // @TODO: We might need to abstract notifications to library rather than mixin
store.state.notificationStore.push({ store.dispatch('snackbars:add', {
title: '', title: '',
text: isExperience ? item.value : item.dropText, text: isExperience ? item.value : item.dropText,
type: isExperience ? 'xp' : 'drop', type: isExperience ? 'xp' : 'drop',

View file

@ -0,0 +1,13 @@
import uuid from 'uuid';
export function add (store, payload) {
let notification = Object.assign({}, payload);
notification.uuid = uuid();
store.state.notificationStore.push(notification);
}
export function remove (store, payload) {
store.state.notificationStore = store.state.notificationStore.filter(notification => {
return notification.uuid !== payload.uuid;
});
}

View file

@ -136,6 +136,7 @@ export default function () {
equipmentDrawerOpen: true, equipmentDrawerOpen: true,
recentlyPurchased: {}, recentlyPurchased: {},
groupPlans: [], groupPlans: [],
groupNotifications: [],
}, },
}); });