| <script setup lang="ts"> |
| import { RouterView, useRouter, useRoute } from 'vue-router' |
| import { onMounted, onBeforeUnmount, watch } from 'vue' |
| import Toast from '@/components/common/Toast.vue' |
| import NavigationProgress from '@/components/common/NavigationProgress.vue' |
| import { resolveDocumentTitle } from '@/router/title' |
| import AnnouncementPopup from '@/components/common/AnnouncementPopup.vue' |
| import { useAppStore, useAuthStore, useSubscriptionStore, useAnnouncementStore } from '@/stores' |
| import { getSetupStatus } from '@/api/setup' |
| |
| const router = useRouter() |
| const route = useRoute() |
| const appStore = useAppStore() |
| const authStore = useAuthStore() |
| const subscriptionStore = useSubscriptionStore() |
| const announcementStore = useAnnouncementStore() |
| |
| |
| |
| |
| |
| function updateFavicon(logoUrl: string) { |
| |
| let link = document.querySelector<HTMLLinkElement>('link[rel="icon"]') |
| if (!link) { |
| link = document.createElement('link') |
| link.rel = 'icon' |
| document.head.appendChild(link) |
| } |
| link.type = logoUrl.endsWith('.svg') ? 'image/svg+xml' : 'image/x-icon' |
| link.href = logoUrl |
| } |
| |
| |
| watch( |
| () => appStore.siteLogo, |
| (newLogo) => { |
| if (newLogo) { |
| updateFavicon(newLogo) |
| } |
| }, |
| { immediate: true } |
| ) |
| |
| |
| function onVisibilityChange() { |
| if (document.visibilityState === 'visible' && authStore.isAuthenticated) { |
| announcementStore.fetchAnnouncements() |
| } |
| } |
| |
| watch( |
| () => authStore.isAuthenticated, |
| (isAuthenticated, oldValue) => { |
| if (isAuthenticated) { |
| |
| subscriptionStore.fetchActiveSubscriptions().catch((error) => { |
| console.error('Failed to preload subscriptions:', error) |
| }) |
| subscriptionStore.startPolling() |
| |
| |
| if (oldValue === false) { |
| |
| setTimeout(() => announcementStore.fetchAnnouncements(true), 3000) |
| } else { |
| |
| announcementStore.fetchAnnouncements() |
| } |
| |
| |
| document.addEventListener('visibilitychange', onVisibilityChange) |
| } else { |
| |
| subscriptionStore.clear() |
| announcementStore.reset() |
| document.removeEventListener('visibilitychange', onVisibilityChange) |
| } |
| }, |
| { immediate: true } |
| ) |
| |
| |
| router.afterEach(() => { |
| if (authStore.isAuthenticated) { |
| announcementStore.fetchAnnouncements() |
| } |
| }) |
| |
| onBeforeUnmount(() => { |
| document.removeEventListener('visibilitychange', onVisibilityChange) |
| }) |
| |
| onMounted(async () => { |
| |
| try { |
| const status = await getSetupStatus() |
| if (status.needs_setup && route.path !== '/setup') { |
| router.replace('/setup') |
| return |
| } |
| } catch { |
| |
| } |
| |
| |
| await appStore.fetchPublicSettings() |
| |
| |
| document.title = resolveDocumentTitle(route.meta.title, appStore.siteName, route.meta.titleKey as string) |
| }) |
| </script> |
|
|
| <template> |
| <NavigationProgress /> |
| <RouterView /> |
| <Toast /> |
| <AnnouncementPopup /> |
| </template> |
|
|