mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +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>
|
<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);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 () {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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') {
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
@@ -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 () {
|
|
||||||
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 () {
|
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();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
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',
|
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',
|
||||||
};
|
};
|
||||||
|
|||||||
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