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> <script>
import closeIcon from '@/assets/svg/close.svg'; import closeIcon from '@/assets/svg/close.svg';
import {
clearBannerSetting, hideBanner, isBannerHidden, updateBannerHeight,
} from '@/libs/banner.func';
import { EVENTS } from '@/libs/events';
export default { export default {
props: { props: {
@@ -93,29 +97,30 @@ export default {
canShow: { canShow: {
handler (newVal) { handler (newVal) {
const valToSet = newVal === true ? this.height : '0px'; const valToSet = newVal === true ? this.height : '0px';
document.documentElement.style updateBannerHeight(this.bannerId, valToSet);
.setProperty(`--banner-${this.bannerId}-height`, valToSet); this.$root.$emit(EVENTS.BANNER_HEIGHT_UPDATED);
}, },
immediate: true, immediate: true,
}, },
show (newVal) { show (newVal) {
// When the show condition is set to false externally, remove the session storage setting // When the show condition is set to false externally, remove the session storage setting
if (newVal === false) { if (newVal === false) {
window.sessionStorage.removeItem(`hide-banner-${this.bannerId}`); clearBannerSetting(this.bannerId);
this.hidden = false; this.hidden = false;
} }
}, },
}, },
mounted () { mounted () {
const hideStatus = window.sessionStorage.getItem(`hide-banner-${this.bannerId}`); if (isBannerHidden(this.bannerId)) {
if (hideStatus === 'true') {
this.hidden = true; this.hidden = true;
} }
}, },
methods: { methods: {
close () { close () {
window.sessionStorage.setItem(`hide-banner-${this.bannerId}`, 'true'); hideBanner(this.bannerId);
this.hidden = true; 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 health from '@/assets/svg/health.svg';
import sword from '@/assets/svg/sword.svg'; import sword from '@/assets/svg/sword.svg';
import { worldStateMixin } from '@/mixins/worldState';
export default { export default {
mixins: [
worldStateMixin,
],
components: { components: {
BaseNotification, BaseNotification,
}, },
@@ -203,8 +207,8 @@ export default {
return this.questData.boss.hp.toLocaleString(); return this.questData.boss.hp.toLocaleString();
}, },
}, },
async mounted () { mounted () {
await this.$store.dispatch('worldState:getWorldState'); this.triggerGetWorldState();
}, },
methods: { methods: {
action () { action () {

View File

@@ -377,6 +377,7 @@ import svgClose from '@/assets/svg/close.svg';
import gifts from '@/assets/svg/gifts.svg'; import gifts from '@/assets/svg/gifts.svg';
import paymentsButtons from '@/components/payments/buttons/list'; import paymentsButtons from '@/components/payments/buttons/list';
import { worldStateMixin } from '@/mixins/worldState';
export default { export default {
components: { components: {
@@ -385,7 +386,7 @@ export default {
directives: { directives: {
markdown, markdown,
}, },
mixins: [paymentsMixin], mixins: [paymentsMixin, worldStateMixin],
data () { data () {
return { return {
icons: Object.freeze({ icons: Object.freeze({
@@ -446,13 +447,13 @@ export default {
}, },
}, },
async mounted () { async mounted () {
await this.$store.dispatch('worldState:getWorldState'); await this.triggerGetWorldState();
this.$root.$on('bv::show::modal', modalId => { this.$root.$on('bv::show::modal', modalId => {
if (modalId === 'buy-gems') { if (modalId === 'buy-gems') {
// We force reloading the world state every time the modal is reopened // We force reloading the world state every time the modal is reopened
// To make sure the promo status is always up to date // 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 currencyMixin from '../_currencyMixin';
import inventoryUtils from '@/mixins/inventoryUtils'; import inventoryUtils from '@/mixins/inventoryUtils';
import pinUtils from '@/mixins/pinUtils'; import pinUtils from '@/mixins/pinUtils';
import { worldStateMixin } from '@/mixins/worldState';
const sortItems = ['AZ', 'sortByNumber'].map(g => ({ id: g })); const sortItems = ['AZ', 'sortByNumber'].map(g => ({ id: g }));
@@ -192,7 +193,14 @@ export default {
CategoryRow, CategoryRow,
MarketFilter, MarketFilter,
}, },
mixins: [notifications, buyMixin, currencyMixin, inventoryUtils, pinUtils], mixins: [
notifications,
buyMixin,
currencyMixin,
inventoryUtils,
pinUtils,
worldStateMixin,
],
data () { data () {
return { return {
viewOptions: { viewOptions: {
@@ -301,12 +309,13 @@ export default {
this.searchTextThrottled = this.searchText.toLowerCase(); this.searchTextThrottled = this.searchText.toLowerCase();
}, 250), }, 250),
}, },
async mounted () { mounted () {
this.$store.dispatch('common:setTitle', { this.$store.dispatch('common:setTitle', {
subSection: this.$t('market'), subSection: this.$t('market'),
section: this.$t('shops'), section: this.$t('shops'),
}); });
await this.$store.dispatch('worldState:getWorldState');
this.triggerGetWorldState();
}, },
methods: { methods: {
sellItem (itemScope) { sellItem (itemScope) {

View File

@@ -424,6 +424,7 @@ import FilterSidebar from '@/components/ui/filterSidebar';
import FilterGroup from '@/components/ui/filterGroup'; import FilterGroup from '@/components/ui/filterGroup';
import SelectTranslatedArray from '@/components/tasks/modal-controls/selectTranslatedArray'; import SelectTranslatedArray from '@/components/tasks/modal-controls/selectTranslatedArray';
import QuestPopover from './questPopover'; import QuestPopover from './questPopover';
import { worldStateMixin } from '@/mixins/worldState';
export default { export default {
components: { components: {
@@ -441,7 +442,7 @@ export default {
PinBadge, PinBadge,
QuestInfo, QuestInfo,
}, },
mixins: [buyMixin, currencyMixin, pinUtils], mixins: [buyMixin, currencyMixin, pinUtils, worldStateMixin],
data () { data () {
return { return {
viewOptions: {}, viewOptions: {},
@@ -510,7 +511,7 @@ export default {
subSection: this.$t('quests'), subSection: this.$t('quests'),
section: this.$t('shops'), section: this.$t('shops'),
}); });
await this.$store.dispatch('worldState:getWorldState'); await this.triggerGetWorldState();
this.$root.$on('bv::modal::hidden', event => { this.$root.$on('bv::modal::hidden', event => {
if (event.componentId === 'buy-quest-modal') { 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 FilterSidebar from '@/components/ui/filterSidebar';
import FilterGroup from '@/components/ui/filterGroup'; import FilterGroup from '@/components/ui/filterGroup';
import { getClassName } from '../../../../../common/script/libs/getClassName'; import { getClassName } from '../../../../../common/script/libs/getClassName';
import { worldStateMixin } from '@/mixins/worldState';
export default { export default {
components: { components: {
@@ -382,7 +383,7 @@ export default {
ShopItem, ShopItem,
toggleSwitch, toggleSwitch,
}, },
mixins: [buyMixin, currencyMixin, pinUtils], mixins: [buyMixin, currencyMixin, pinUtils, worldStateMixin],
data () { data () {
return { return {
viewOptions: {}, viewOptions: {},
@@ -506,7 +507,7 @@ export default {
this.searchTextThrottled = this.searchText.toLowerCase(); this.searchTextThrottled = this.searchText.toLowerCase();
}, 250), }, 250),
}, },
async mounted () { mounted () {
this.$store.dispatch('common:setTitle', { this.$store.dispatch('common:setTitle', {
subSection: this.$t('seasonalShop'), subSection: this.$t('seasonalShop'),
section: this.$t('shops'), section: this.$t('shops'),
@@ -516,7 +517,7 @@ export default {
this.backgroundUpdate = new Date(); this.backgroundUpdate = new Date();
}); });
await this.$store.dispatch('worldState:getWorldState'); this.triggerGetWorldState();
}, },
beforeDestroy () { beforeDestroy () {
this.$root.$off('buyModal::boughtItem'); this.$root.$off('buyModal::boughtItem');

View File

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