FIX: notifications position with 2nd banner - WIP (#13556)

* FIX: notifications position with 2nd banner

* emit event when worldState is loaded

* fix heights / padding of notifications
This commit is contained in:
negue
2021-10-21 22:17:09 +02:00
committed by GitHub
parent 7646f7da9f
commit 7044c497a2
10 changed files with 138 additions and 30 deletions

View File

@@ -50,6 +50,10 @@ body.modal-open .habitica-top-banner {
<script>
import closeIcon from '@/assets/svg/close.svg';
import {
clearBannerSetting, hideBanner, isBannerHidden, updateBannerHeight,
} from '@/libs/banner.func';
import { EVENTS } from '@/libs/events';
export default {
props: {
@@ -93,29 +97,30 @@ export default {
canShow: {
handler (newVal) {
const valToSet = newVal === true ? this.height : '0px';
document.documentElement.style
.setProperty(`--banner-${this.bannerId}-height`, valToSet);
updateBannerHeight(this.bannerId, valToSet);
this.$root.$emit(EVENTS.BANNER_HEIGHT_UPDATED);
},
immediate: true,
},
show (newVal) {
// When the show condition is set to false externally, remove the session storage setting
if (newVal === false) {
window.sessionStorage.removeItem(`hide-banner-${this.bannerId}`);
clearBannerSetting(this.bannerId);
this.hidden = false;
}
},
},
mounted () {
const hideStatus = window.sessionStorage.getItem(`hide-banner-${this.bannerId}`);
if (hideStatus === 'true') {
if (isBannerHidden(this.bannerId)) {
this.hidden = true;
}
},
methods: {
close () {
window.sessionStorage.setItem(`hide-banner-${this.bannerId}`, 'true');
hideBanner(this.bannerId);
this.hidden = true;
this.$root.$emit(EVENTS.BANNER_HIDDEN, this.bannerId);
},
},
};

View File

@@ -175,8 +175,12 @@ import BaseNotification from './base';
import health from '@/assets/svg/health.svg';
import sword from '@/assets/svg/sword.svg';
import { worldStateMixin } from '@/mixins/worldState';
export default {
mixins: [
worldStateMixin,
],
components: {
BaseNotification,
},
@@ -203,8 +207,8 @@ export default {
return this.questData.boss.hp.toLocaleString();
},
},
async mounted () {
await this.$store.dispatch('worldState:getWorldState');
mounted () {
this.triggerGetWorldState();
},
methods: {
action () {

View File

@@ -377,6 +377,7 @@ import svgClose from '@/assets/svg/close.svg';
import gifts from '@/assets/svg/gifts.svg';
import paymentsButtons from '@/components/payments/buttons/list';
import { worldStateMixin } from '@/mixins/worldState';
export default {
components: {
@@ -385,7 +386,7 @@ export default {
directives: {
markdown,
},
mixins: [paymentsMixin],
mixins: [paymentsMixin, worldStateMixin],
data () {
return {
icons: Object.freeze({
@@ -446,13 +447,13 @@ export default {
},
},
async mounted () {
await this.$store.dispatch('worldState:getWorldState');
await this.triggerGetWorldState();
this.$root.$on('bv::show::modal', modalId => {
if (modalId === 'buy-gems') {
// We force reloading the world state every time the modal is reopened
// To make sure the promo status is always up to date
this.$store.dispatch('worldState:getWorldState', { forceLoad: true });
this.triggerGetWorldState(true);
}
});
},

View File

@@ -172,6 +172,7 @@ import buyMixin from '@/mixins/buy';
import currencyMixin from '../_currencyMixin';
import inventoryUtils from '@/mixins/inventoryUtils';
import pinUtils from '@/mixins/pinUtils';
import { worldStateMixin } from '@/mixins/worldState';
const sortItems = ['AZ', 'sortByNumber'].map(g => ({ id: g }));
@@ -192,7 +193,14 @@ export default {
CategoryRow,
MarketFilter,
},
mixins: [notifications, buyMixin, currencyMixin, inventoryUtils, pinUtils],
mixins: [
notifications,
buyMixin,
currencyMixin,
inventoryUtils,
pinUtils,
worldStateMixin,
],
data () {
return {
viewOptions: {
@@ -301,12 +309,13 @@ export default {
this.searchTextThrottled = this.searchText.toLowerCase();
}, 250),
},
async mounted () {
mounted () {
this.$store.dispatch('common:setTitle', {
subSection: this.$t('market'),
section: this.$t('shops'),
});
await this.$store.dispatch('worldState:getWorldState');
this.triggerGetWorldState();
},
methods: {
sellItem (itemScope) {

View File

@@ -424,6 +424,7 @@ import FilterSidebar from '@/components/ui/filterSidebar';
import FilterGroup from '@/components/ui/filterGroup';
import SelectTranslatedArray from '@/components/tasks/modal-controls/selectTranslatedArray';
import QuestPopover from './questPopover';
import { worldStateMixin } from '@/mixins/worldState';
export default {
components: {
@@ -441,7 +442,7 @@ export default {
PinBadge,
QuestInfo,
},
mixins: [buyMixin, currencyMixin, pinUtils],
mixins: [buyMixin, currencyMixin, pinUtils, worldStateMixin],
data () {
return {
viewOptions: {},
@@ -510,7 +511,7 @@ export default {
subSection: this.$t('quests'),
section: this.$t('shops'),
});
await this.$store.dispatch('worldState:getWorldState');
await this.triggerGetWorldState();
this.$root.$on('bv::modal::hidden', event => {
if (event.componentId === 'buy-quest-modal') {

View File

@@ -371,6 +371,7 @@ import SelectTranslatedArray from '@/components/tasks/modal-controls/selectTrans
import FilterSidebar from '@/components/ui/filterSidebar';
import FilterGroup from '@/components/ui/filterGroup';
import { getClassName } from '../../../../../common/script/libs/getClassName';
import { worldStateMixin } from '@/mixins/worldState';
export default {
components: {
@@ -382,7 +383,7 @@ export default {
ShopItem,
toggleSwitch,
},
mixins: [buyMixin, currencyMixin, pinUtils],
mixins: [buyMixin, currencyMixin, pinUtils, worldStateMixin],
data () {
return {
viewOptions: {},
@@ -506,7 +507,7 @@ export default {
this.searchTextThrottled = this.searchText.toLowerCase();
}, 250),
},
async mounted () {
mounted () {
this.$store.dispatch('common:setTitle', {
subSection: this.$t('seasonalShop'),
section: this.$t('shops'),
@@ -516,7 +517,7 @@ export default {
this.backgroundUpdate = new Date();
});
await this.$store.dispatch('worldState:getWorldState');
this.triggerGetWorldState();
},
beforeDestroy () {
this.$root.$off('buyModal::boughtItem');

View File

@@ -65,9 +65,14 @@
</style>
<script>
import debounce from 'lodash/debounce';
import { mapState } from '@/libs/store';
import notification from './notification';
import { sleepAsync } from '../../../../common/script/libs/sleepAsync';
import { getBannerHeight } from '@/libs/banner.func';
import { EVENTS } from '@/libs/events';
import { worldStateMixin } from '@/mixins/worldState';
const NOTIFICATIONS_VISIBLE_AT_ONCE = 4;
const REMOVAL_INTERVAL = 2500;
@@ -75,6 +80,9 @@ const DELAY_DELETE_AND_NEW = 60;
const DELAY_FILLING_ENTRIES = 240;
export default {
mixins: [
worldStateMixin,
],
components: {
notification,
},
@@ -95,12 +103,15 @@ export default {
removalIntervalId: null,
notificationTopY: '0px',
preventMultipleWatchExecution: false,
gemsPromoBannerHeight: null,
sleepingBannerHeight: null,
};
},
computed: {
...mapState({
notificationStore: 'notificationStore',
userSleeping: 'user.data.preferences.sleep',
currentEvent: 'worldState.data.currentEvent',
}),
notificationsTopPosClass () {
const base = 'notifications-top-pos-';
@@ -115,12 +126,18 @@ export default {
return `${base}${modifier} scroll-${this.scrollY}`;
},
notificationBannerHeight () {
let scrollPosToCheck = 0;
if (this.userSleeping) {
scrollPosToCheck = 98;
} else {
scrollPosToCheck = 56;
let scrollPosToCheck = 56;
if (this.sleepingBannerHeight) {
scrollPosToCheck += this.sleepingBannerHeight;
}
if (this.currentEvent
&& this.currentEvent.event
) {
scrollPosToCheck += this.gemsPromoBannerHeight ?? 0;
}
return scrollPosToCheck;
},
visibleNotificationsWithoutErrors () {
@@ -151,14 +168,32 @@ export default {
this.preventMultipleWatchExecution = false;
},
currentEvent: function currentEventChanged () {
this.gemsPromoBannerHeight = getBannerHeight('gems-promo');
},
},
mounted () {
window.addEventListener('scroll', this.updateScrollY, { passive: true });
this.updateScrollY();
},
async mounted () {
window.addEventListener('scroll', this.updateScrollY, {
passive: true,
});
this.$root.$on(EVENTS.BANNER_HEIGHT_UPDATED, () => {
this.updateBannerHeightAndScrollY();
});
this.$root.$on(EVENTS.WORLD_STATE_LOADED, () => {
this.updateBannerHeightAndScrollY();
});
await this.triggerGetWorldState();
this.updateBannerHeightAndScrollY();
},
destroyed () {
window.removeEventListener('scroll', this.updateScrollY, { passive: true });
window.removeEventListener('scroll', this.updateScrollY, {
passive: true,
});
this.$root.$off(EVENTS.BANNER_HEIGHT_UPDATED);
this.$root.$off(EVENTS.WORLD_STATE_LOADED);
},
methods: {
debug (...args) {
@@ -313,10 +348,16 @@ export default {
* This updates the position of all notifications so that its always at the top,
* unless the header is visible then its under the header
*/
updateScrollY () {
updateScrollY: debounce(function updateScrollY () {
const topY = Math.min(window.scrollY, this.notificationBannerHeight) - 10;
this.notificationTopY = `${this.notificationBannerHeight - topY}px`;
}, 16),
updateBannerHeightAndScrollY () {
this.gemsPromoBannerHeight = getBannerHeight('gems-promo');
this.sleepingBannerHeight = getBannerHeight('damage-paused');
this.updateScrollY();
},
},
};

View File

@@ -0,0 +1,28 @@
export function isBannerHidden (bannerId) {
return window.sessionStorage.getItem(`hide-banner-${bannerId}`) === 'true';
}
export function hideBanner (bannerId) {
window.sessionStorage.setItem(`hide-banner-${bannerId}`, 'true');
}
export function clearBannerSetting (bannerId) {
window.sessionStorage.removeItem(`hide-banner-${bannerId}`);
}
export function updateBannerHeight (bannerId, bannerHeight) {
document.documentElement.style
.setProperty(`--banner-${bannerId}-height`, bannerHeight);
}
export function getBannerHeight (bannerId) {
const heightProp = document.documentElement.style
.getPropertyValue(`--banner-${bannerId}-height`);
if (heightProp?.includes('rem')) {
return Number(heightProp.replace('rem', '') * 16); // FontSize
}
// || if the left side is an empty string
return Number(heightProp?.replace('px', '') || 0);
}

View File

@@ -3,4 +3,7 @@ export const EVENTS = {
RESYNC_COMPLETED: 'habitica::resync-completed',
PM_REFRESH: 'pm::refresh',
BANNER_HEIGHT_UPDATED: 'banner:height_updated',
WORLD_STATE_LOADED: 'habitica:world-state-loaded',
};

View File

@@ -0,0 +1,15 @@
import { EVENTS } from '@/libs/events';
export const worldStateMixin = { // eslint-disable-line import/prefer-default-export
methods: {
async triggerGetWorldState (forceLoad = false) {
if (forceLoad) {
await this.$store.dispatch('worldState:getWorldState', { forceLoad: true });
} else {
await this.$store.dispatch('worldState:getWorldState');
}
this.$root.$emit(EVENTS.WORLD_STATE_LOADED);
},
},
};