mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
Modal popup fixes (#10080)
* Added validation for modal stack * Lint fixes
This commit is contained in:
@@ -323,53 +323,7 @@ export default {
|
|||||||
this.hideLoadingScreen();
|
this.hideLoadingScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manage modals
|
this.initializeModalStack();
|
||||||
this.$root.$on('bv::show::modal', (modalId, data = {}) => {
|
|
||||||
if (data.fromRoot) return;
|
|
||||||
|
|
||||||
// Track opening of gems modal unless it's been already tracked
|
|
||||||
// For example the gems button in the menu already tracks the event by itself
|
|
||||||
if (modalId === 'buy-gems' && data.alreadyTracked !== true) {
|
|
||||||
Analytics.track({
|
|
||||||
hitType: 'event',
|
|
||||||
eventCategory: 'button',
|
|
||||||
eventAction: 'click',
|
|
||||||
eventLabel: 'Gems > Wallet',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get last modal on stack and hide
|
|
||||||
let modalStackLength = this.$store.state.modalStack.length;
|
|
||||||
let modalOnTop = this.$store.state.modalStack[modalStackLength - 1];
|
|
||||||
|
|
||||||
// Add new modal to the stack
|
|
||||||
this.$store.state.modalStack.push(modalId);
|
|
||||||
|
|
||||||
// Hide the previous top modal
|
|
||||||
if (modalOnTop) this.$root.$emit('bv::hide::modal', modalOnTop, {fromRoot: true});
|
|
||||||
});
|
|
||||||
|
|
||||||
// @TODO: This part is hacky and could be solved with two options:
|
|
||||||
// 1 - Find a way to pass fromRoot to hidden
|
|
||||||
// 2 - Enforce that all modals use the hide::modal event
|
|
||||||
this.$root.$on('bv::modal::hidden', (bvEvent) => {
|
|
||||||
const modalId = bvEvent.target.id;
|
|
||||||
|
|
||||||
let modalStackLength = this.$store.state.modalStack.length;
|
|
||||||
let modalSecondToTop = this.$store.state.modalStack[modalStackLength - 2];
|
|
||||||
// Don't remove modal if hid was called from main app
|
|
||||||
// @TODO: I'd reather use this, but I don't know how to pass data to hidden event
|
|
||||||
// if (data && data.fromRoot) return;
|
|
||||||
if (modalId === modalSecondToTop) return;
|
|
||||||
|
|
||||||
// Remove modal from stack
|
|
||||||
this.$store.state.modalStack.pop();
|
|
||||||
|
|
||||||
// Recalculate and show the last modal if there is one
|
|
||||||
modalStackLength = this.$store.state.modalStack.length;
|
|
||||||
let modalOnTop = this.$store.state.modalStack[modalStackLength - 1];
|
|
||||||
if (modalOnTop) this.$root.$emit('bv::show::modal', modalOnTop, {fromRoot: true});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
this.$root.$off('playSound');
|
this.$root.$off('playSound');
|
||||||
@@ -384,6 +338,80 @@ export default {
|
|||||||
if (loadingScreen) document.body.removeChild(loadingScreen);
|
if (loadingScreen) document.body.removeChild(loadingScreen);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
initializeModalStack () {
|
||||||
|
// Manage modals
|
||||||
|
this.$root.$on('bv::show::modal', (modalId, data = {}) => {
|
||||||
|
if (data.fromRoot) return;
|
||||||
|
const modalStack = this.$store.state.modalStack;
|
||||||
|
|
||||||
|
this.trackGemPurchase(modalId, data);
|
||||||
|
|
||||||
|
// Add new modal to the stack
|
||||||
|
const prev = modalStack[modalStack.length - 1];
|
||||||
|
const prevId = prev ? prev.modalId : undefined;
|
||||||
|
modalStack.push({modalId, prev: prevId});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$root.$on('bv::modal::hidden', (bvEvent) => {
|
||||||
|
const modalId = bvEvent.target && bvEvent.target.id;
|
||||||
|
if (!modalId) return;
|
||||||
|
|
||||||
|
const modalStack = this.$store.state.modalStack;
|
||||||
|
|
||||||
|
const modalOnTop = modalStack[modalStack.length - 1];
|
||||||
|
|
||||||
|
// Check for invalid modal. Event systems can send multiples
|
||||||
|
if (!this.validStack(modalStack)) return;
|
||||||
|
|
||||||
|
// If we are moving forward
|
||||||
|
if (modalOnTop && modalOnTop.prev === modalId) return;
|
||||||
|
|
||||||
|
// Remove modal from stack
|
||||||
|
this.$store.state.modalStack.pop();
|
||||||
|
|
||||||
|
// Get previous modal
|
||||||
|
const modalBefore = modalOnTop ? modalOnTop.prev : undefined;
|
||||||
|
if (modalBefore) this.$root.$emit('bv::show::modal', modalBefore, {fromRoot: true});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
validStack (modalStack) {
|
||||||
|
const modalsThatCanShowTwice = ['profile'];
|
||||||
|
const modalCount = {};
|
||||||
|
const prevAndCurrent = 2;
|
||||||
|
|
||||||
|
for (let index in modalStack) {
|
||||||
|
const current = modalStack[index];
|
||||||
|
|
||||||
|
if (!modalCount[current.modalId]) modalCount[current.modalId] = 0;
|
||||||
|
modalCount[current.modalId] += 1;
|
||||||
|
if (modalCount[current.modalId] > prevAndCurrent && modalsThatCanShowTwice.indexOf(current.modalId) === -1) {
|
||||||
|
this.$store.state.modalStack = [];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current.prev) continue; // eslint-disable-line
|
||||||
|
if (!modalCount[current.prev]) modalCount[current.prev] = 0;
|
||||||
|
modalCount[current.prev] += 1;
|
||||||
|
if (modalCount[current.prev] > prevAndCurrent && modalsThatCanShowTwice.indexOf(current.prev) === -1) {
|
||||||
|
this.$store.state.modalStack = [];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
trackGemPurchase (modalId, data) {
|
||||||
|
// Track opening of gems modal unless it's been already tracked
|
||||||
|
// For example the gems button in the menu already tracks the event by itself
|
||||||
|
if (modalId === 'buy-gems' && data.alreadyTracked !== true) {
|
||||||
|
Analytics.track({
|
||||||
|
hitType: 'event',
|
||||||
|
eventCategory: 'button',
|
||||||
|
eventAction: 'click',
|
||||||
|
eventLabel: 'Gems > Wallet',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
resetItemToBuy ($event) {
|
resetItemToBuy ($event) {
|
||||||
// @TODO: Do we need this? I think selecting a new item
|
// @TODO: Do we need this? I think selecting a new item
|
||||||
// overwrites. @negue might know
|
// overwrites. @negue might know
|
||||||
|
|||||||
@@ -204,6 +204,7 @@ export default {
|
|||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
userHp (after, before) {
|
userHp (after, before) {
|
||||||
|
if (this.user.needsCron) return;
|
||||||
if (after <= 0) {
|
if (after <= 0) {
|
||||||
this.playSound('Death');
|
this.playSound('Death');
|
||||||
this.$root.$emit('bv::show::modal', 'death');
|
this.$root.$emit('bv::show::modal', 'death');
|
||||||
@@ -252,6 +253,7 @@ export default {
|
|||||||
this.showLevelUpNotifications(after);
|
this.showLevelUpNotifications(after);
|
||||||
},
|
},
|
||||||
userClassSelect (after) {
|
userClassSelect (after) {
|
||||||
|
if (this.user.needsCron) return;
|
||||||
if (!after) return;
|
if (!after) return;
|
||||||
this.$root.$emit('bv::show::modal', 'choose-class');
|
this.$root.$emit('bv::show::modal', 'choose-class');
|
||||||
// @TODO: {controller:'UserCtrl', keyboard:false, backdrop:'static'}
|
// @TODO: {controller:'UserCtrl', keyboard:false, backdrop:'static'}
|
||||||
|
|||||||
Reference in New Issue
Block a user