mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 22:27:26 +01:00
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:
@@ -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);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
28
website/client/src/libs/banner.func.js
Normal file
28
website/client/src/libs/banner.func.js
Normal 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);
|
||||
}
|
||||
@@ -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',
|
||||
};
|
||||
|
||||
15
website/client/src/mixins/worldState.js
Normal file
15
website/client/src/mixins/worldState.js
Normal 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);
|
||||
},
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user