import { throttle } from 'lodash' import { mapActions, mapState } from 'pinia' import { defineAsyncComponent } from 'vue' import DesktopNav from 'src/components/desktop_nav/desktop_nav.vue' import EditStatusModal from 'src/components/edit_status_modal/edit_status_modal.vue' import FeaturesPanel from 'src/components/features_panel/features_panel.vue' import GlobalNoticeList from 'src/components/global_notice_list/global_notice_list.vue' import InstanceSpecificPanel from 'src/components/instance_specific_panel/instance_specific_panel.vue' import MediaModal from 'src/components/media_modal/media_modal.vue' import MobileNav from 'src/components/mobile_nav/mobile_nav.vue' import MobilePostStatusButton from 'src/components/mobile_post_status_button/mobile_post_status_button.vue' import NavPanel from 'src/components/nav_panel/nav_panel.vue' import PostStatusModal from 'src/components/post_status_modal/post_status_modal.vue' import ShoutPanel from 'src/components/shout_panel/shout_panel.vue' import SideDrawer from 'src/components/side_drawer/side_drawer.vue' import StatusHistoryModal from 'src/components/status_history_modal/status_history_modal.vue' import UserPanel from 'src/components/user_panel/user_panel.vue' import UserReportingModal from 'src/components/user_reporting_modal/user_reporting_modal.vue' import WhoToFollowPanel from 'src/components/who_to_follow_panel/who_to_follow_panel.vue' import { useI18nStore } from 'src/stores/i18n.js' import { useInstanceStore } from 'src/stores/instance.js' import { useInterfaceStore } from 'src/stores/interface.js' import { useShoutStore } from 'src/stores/shout.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' import { applyStyleConfig } from 'src/services/style_setter/style_setter.js' import { getOrCreateServiceWorker } from 'src/services/sw/sw' import { windowHeight, windowWidth, } from 'src/services/window_utils/window_utils' export default { name: 'app', components: { UserPanel, NavPanel, Notifications: defineAsyncComponent( () => import('./components/notifications/notifications.vue'), ), InstanceSpecificPanel, FeaturesPanel, WhoToFollowPanel, ShoutPanel, MediaModal, SideDrawer, MobilePostStatusButton, MobileNav, DesktopNav, SettingsModal: defineAsyncComponent( () => import('./components/settings_modal/settings_modal.vue'), ), UpdateNotification: defineAsyncComponent( () => import('./components/update_notification/update_notification.vue'), ), UserReportingModal, PostStatusModal, EditStatusModal, StatusHistoryModal, GlobalNoticeList, }, data: () => ({ mobileActivePanel: 'timeline', }), watch: { themeApplied() { this.removeSplash() }, currentTheme() { this.setThemeBodyClass() }, layoutType() { document.getElementById('modal').classList = ['-' + this.layoutType] }, }, created() { document.getElementById('modal').classList = ['-' + this.layoutType] useI18nStore().setLanguage() applyStyleConfig(useSyncConfigStore().mergedConfig) // Create bound handlers this.updateScrollState = throttle(this.scrollHandler, 200) this.updateMobileState = throttle(this.resizeHandler, 200) }, mounted() { window.addEventListener('resize', this.updateMobileState) this.scrollParent.addEventListener('scroll', this.updateScrollState) if (useInterfaceStore().themeApplied) { this.setThemeBodyClass() this.removeSplash() } getOrCreateServiceWorker() }, unmounted() { window.removeEventListener('resize', this.updateMobileState) this.scrollParent.removeEventListener('scroll', this.updateScrollState) }, computed: { themeApplied() { return useInterfaceStore().themeApplied }, currentTheme() { if (useInterfaceStore().styleDataUsed) { const styleMeta = useInterfaceStore().styleDataUsed.find( (x) => x.component === '@meta', ) if (styleMeta !== undefined) { return styleMeta.directives.name.replaceAll(' ', '-').toLowerCase() } } return 'stock' }, layoutModalClass() { return '-' + this.layoutType }, classes() { return [ { '-reverse': this.reverseLayout, '-no-sticky-headers': this.noSticky, '-has-new-post-button': this.newPostButtonShown, }, '-' + this.layoutType, ] }, navClasses() { const { navbarColumnStretch } = useSyncConfigStore().mergedConfig return [ '-' + this.layoutType, ...(navbarColumnStretch ? ['-column-stretch'] : []), ] }, currentUser() { return this.$store.state.users.currentUser }, userBackground() { return this.currentUser.background_image }, background() { return this.userBackground || this.instanceBackground }, bgStyle() { if (this.background) { return { '--body-background-image': `url(${this.background})`, } } }, shout() { return useShoutStore().joined }, isChats() { return this.$route.name === 'chat' || this.$route.name === 'chats' }, isListEdit() { return this.$route.name === 'lists-edit' }, newPostButtonShown() { if (this.isChats) return false if (this.isListEdit) return false return this.alwaysShowNewPostButton || this.layoutType === 'mobile' }, reverseLayout() { const { thirdColumnMode, sidebarRight: reverseSetting } = useSyncConfigStore().mergedConfig if (this.layoutType !== 'wide') { return reverseSetting } else { return thirdColumnMode === 'notifications' ? reverseSetting : !reverseSetting } }, scrollParent() { return window /* this.$refs.appContentRef */ }, ...mapState(useSyncConfigStore, { shoutboxPosition: (store) => store.mergedConfig.alwaysShowSubjectInput || false, alwaysShowSubjectInput: (store) => store.mergedConfig.alwaysShowSubjectInput, }), ...mapState(useInterfaceStore, ['layoutType']), ...mapState(useSyncConfigStore, { hideShoutbox: (store) => store.mergedConfig.hideShoutbox, noSticky: (store) => store.mergedConfig.disableStickyHeaders, showScrollbars: (store) => store.mergedConfig.showScrollbars, }), ...mapState(useInstanceStore, { instanceBackground: (store) => useSyncConfigStore().mergedConfig.hideInstanceWallpaper ? null : store.background, showInstanceSpecificPanel: (store) => store.showInstanceSpecificPanel && !useSyncConfigStore().mergedConfig.hideISP && store.instanceSpecificPanelContent, }), ...mapState(useInstanceStore, [ 'editingAvailable', 'showFeaturesPanel', 'private', 'suggestionsEnabled', ]), }, methods: { resizeHandler() { this.setLayoutWidth(windowWidth()) this.setLayoutHeight(windowHeight()) }, scrollHandler() { const scrollPosition = this.scrollParent === window ? window.scrollY : this.scrollParent.scrollTop if (scrollPosition != 0) { this.$refs.appContentRef.classList.add(['-scrolled']) } else { this.$refs.appContentRef.classList.remove(['-scrolled']) } }, setThemeBodyClass() { const themeName = this.currentTheme const classList = Array.from(document.body.classList) const oldTheme = classList.filter((c) => c.startsWith('theme-')) if (themeName !== null && themeName !== '') { const newTheme = `theme-${themeName.toLowerCase()}` // remove old theme reference if there are any if (oldTheme.length) { document.body.classList.replace(oldTheme[0], newTheme) } else { document.body.classList.add(newTheme) } } else { // remove theme reference if non-V3 theme is used document.body.classList.remove(...oldTheme) } }, removeSplash() { document.querySelector('#status').textContent = this.$t( 'splash.fun_' + Math.ceil(Math.random() * 4), ) const splashscreenRoot = document.querySelector('#splash') splashscreenRoot.addEventListener('transitionend', () => { splashscreenRoot.remove() }) setTimeout(() => { splashscreenRoot.remove() // forcibly remove it, should fix my plasma browser widget t. HJ }, 600) splashscreenRoot.classList.add('hidden') document.querySelector('#app').classList.remove('hidden') }, ...mapActions(useInterfaceStore, ['setLayoutWidth', 'setLayoutHeight']), }, }