+
+
@@ -47,11 +94,28 @@ import { nextTick, onActivated, onMounted, onUnmounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { useUserStore } from '@/store/userStore'
import { useAppNotificationBadge } from '@/composables/useAppNotificationBadge'
+import { usePwaInstall } from '@/composables/usePwaInstall'
+import { PWA_MANIFEST_READY_EVENT } from '@/modules/pwa/manifest'
+import {
+ hasNativeInstallPrompt,
+ initPwaInstallPromptListener,
+} from '@/modules/pwa/pwaInstall'
+import {
+ canRequestBrowserNotification,
+ requestBrowserNotificationPermission,
+} from '@/utils/browserNotification'
import router from '@/router'
import Icon from '@/components/BasicPacks/BasicIcon.vue'
+const emit = defineEmits<{
+ logout: []
+}>()
+
const headerRef = ref(null)
+const menuRoot = ref(null)
+const menuOpen = ref(false)
const isHeaderScrolled = ref(false)
+const canNativeInstall = ref(false)
const route = useRoute()
const userStore = useUserStore()
@@ -60,12 +124,48 @@ const {
unreadCount,
clearUnreadLocally,
} = useAppNotificationBadge()
+const { tryInstallOrOpenGuide, showInstallEntry } = usePwaInstall()
-function goNotifications() {
+function refreshCanInstall() {
+ canNativeInstall.value = hasNativeInstallPrompt()
+}
+
+function closeMenu() {
+ menuOpen.value = false
+}
+
+async function goNotifications() {
+ closeMenu()
clearUnreadLocally()
+ if (canRequestBrowserNotification()) {
+ void requestBrowserNotificationPermission()
+ }
void router.push({ name: 'appNotifications' })
}
+async function onInstall() {
+ closeMenu()
+ await tryInstallOrOpenGuide()
+ refreshCanInstall()
+}
+
+function onLogout() {
+ closeMenu()
+ emit('logout')
+}
+
+function reloadApp() {
+ window.location.reload()
+}
+
+function onDocPointerDown(event: PointerEvent) {
+ if (!menuOpen.value) return
+ const root = menuRoot.value
+ if (root && !root.contains(event.target as Node)) {
+ closeMenu()
+ }
+}
+
let intersectionObserver: IntersectionObserver | null = null
let scrollTarget: HTMLElement | Window | null = null
@@ -166,6 +266,12 @@ function setupScrollTracking() {
}
onMounted(async () => {
+ initPwaInstallPromptListener()
+ refreshCanInstall()
+ window.addEventListener(PWA_MANIFEST_READY_EVENT, refreshCanInstall)
+ window.addEventListener('appinstalled', refreshCanInstall)
+ document.addEventListener('pointerdown', onDocPointerDown)
+
setupScrollTracking()
await nextTick()
setupScrollTracking()
@@ -189,11 +295,10 @@ watch(
onUnmounted(() => {
teardownScrollTracking()
+ window.removeEventListener(PWA_MANIFEST_READY_EVENT, refreshCanInstall)
+ window.removeEventListener('appinstalled', refreshCanInstall)
+ document.removeEventListener('pointerdown', onDocPointerDown)
})
-
-defineEmits<{
- logout: []
-}>()
diff --git a/src/views/base/home/components/DesktopHomeTopbar.vue b/src/views/base/home/components/DesktopHomeTopbar.vue
index 97e07ce..01bfbb8 100644
--- a/src/views/base/home/components/DesktopHomeTopbar.vue
+++ b/src/views/base/home/components/DesktopHomeTopbar.vue
@@ -13,7 +13,7 @@
-
@@ -37,8 +46,16 @@
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import { useUserStore } from '@/store/userStore'
import { useAppNotificationBadge } from '@/composables/useAppNotificationBadge'
+import { canRequestBrowserNotification, requestBrowserNotificationPermission } from '@/utils/browserNotification'
import router from '@/router'
+withDefaults(
+ defineProps<{
+ showLogout?: boolean
+ }>(),
+ { showLogout: true },
+)
+
defineEmits<{
logout: []
}>()
@@ -50,10 +67,17 @@ const {
clearUnreadLocally,
} = useAppNotificationBadge()
-function goNotifications() {
+async function goNotifications() {
clearUnreadLocally()
+ if (canRequestBrowserNotification()) {
+ await requestBrowserNotificationPermission()
+ }
void router.push({ name: 'appNotifications' })
}
+
+function reloadApp() {
+ window.location.reload()
+}