Refactor app navigation and enhance background handling in various views
- Added error handling for router navigation to prevent unhandled promise rejections. - Improved background image management in Dashboard.vue to dynamically set images based on route. - Introduced timers for managing loading states and cleanup on component unmount in Apps.vue, Marketplace.vue, and other views. - Updated app detail navigation to ensure smoother transitions and error handling. - Enhanced clipboard copy functionality in Settings.vue with improved user feedback.
This commit is contained in:
@@ -2,33 +2,33 @@
|
||||
<div class="min-h-screen flex relative dashboard-view" :class="{ 'glass-throw-active': showZoomIn }">
|
||||
<!-- Background container with 3D perspective - full width to avoid letterboxing -->
|
||||
<div class="bg-perspective-container">
|
||||
<!-- Background - default (zoom animates on this layer, not container, to avoid letterboxing) -->
|
||||
<!-- Background - primary layer (visible for all routes, transitions out only for detail pages) -->
|
||||
<div
|
||||
ref="bgDefault"
|
||||
class="bg-layer bg-fullwidth"
|
||||
:class="[
|
||||
{ 'bg-transitioning-out': showAltBackground || showWeb5Background || showNetworkBackground || showSettingsBackground || showMyAppsBackground || showAppStoreBackground || showCloudBackground || showHomeBackground },
|
||||
{ 'bg-transitioning-out': showAltBackground },
|
||||
{ 'zoom-reveal-bg': showZoomIn }
|
||||
]"
|
||||
:style="{ backgroundImage: `url(/assets/img/${currentBackgroundImage})` }"
|
||||
:style="{ backgroundImage: `url(/assets/img/${backgroundImage})` }"
|
||||
/>
|
||||
<!-- Background - alternate for app details and Web5 -->
|
||||
<!-- Background - detail layer (only visible during app/marketplace detail 3D transition) -->
|
||||
<div
|
||||
ref="bgAlt"
|
||||
class="bg-layer bg-fullwidth"
|
||||
:class="{ 'bg-transitioning-in': showAltBackground || showWeb5Background || showNetworkBackground || showSettingsBackground || showMyAppsBackground || showAppStoreBackground || showCloudBackground || showHomeBackground }"
|
||||
:style="{ backgroundImage: `url(/assets/img/${altBackgroundImage})` }"
|
||||
:class="{ 'bg-transitioning-in': showAltBackground }"
|
||||
style="background-image: url(/assets/img/bg-intro-3.jpg)"
|
||||
/>
|
||||
<!-- Glitch overlays - trigger on background change -->
|
||||
<div
|
||||
class="bg-glitch-layer-1"
|
||||
:class="{ 'glitch-active': isGlitching }"
|
||||
:style="{ backgroundImage: `url(/assets/img/${altBackgroundImage})` }"
|
||||
:style="{ backgroundImage: `url(/assets/img/${backgroundImage})` }"
|
||||
/>
|
||||
<div
|
||||
class="bg-glitch-layer-2"
|
||||
:class="{ 'glitch-active': isGlitching }"
|
||||
:style="{ backgroundImage: `url(/assets/img/${altBackgroundImage})` }"
|
||||
:style="{ backgroundImage: `url(/assets/img/${backgroundImage})` }"
|
||||
/>
|
||||
<div
|
||||
class="bg-glitch-scan"
|
||||
@@ -37,11 +37,11 @@
|
||||
<!-- Continuous glitch/flash overlays - same as login, every 5s -->
|
||||
<div
|
||||
class="dashboard-glitch-layer dashboard-glitch-1 bg-fullwidth"
|
||||
:style="{ backgroundImage: `url(/assets/img/${currentBackgroundImage})` }"
|
||||
:style="{ backgroundImage: `url(/assets/img/${backgroundImage})` }"
|
||||
/>
|
||||
<div
|
||||
class="dashboard-glitch-layer dashboard-glitch-2 bg-fullwidth"
|
||||
:style="{ backgroundImage: `url(/assets/img/${currentBackgroundImage})` }"
|
||||
:style="{ backgroundImage: `url(/assets/img/${backgroundImage})` }"
|
||||
/>
|
||||
<div class="dashboard-glitch-scan" />
|
||||
</div>
|
||||
@@ -319,52 +319,61 @@ const store = useAppStore()
|
||||
const loginTransition = useLoginTransitionStore()
|
||||
|
||||
const showZoomIn = ref(false)
|
||||
const pendingTimers: ReturnType<typeof setTimeout>[] = []
|
||||
|
||||
function scheduledTimeout(fn: () => void, ms: number) {
|
||||
const id = setTimeout(fn, ms)
|
||||
pendingTimers.push(id)
|
||||
return id
|
||||
}
|
||||
|
||||
function isDetailRoute(path: string) {
|
||||
return (path.includes('/apps/') && !path.endsWith('/apps')) ||
|
||||
(path.includes('/marketplace/') && !path.endsWith('/marketplace'))
|
||||
}
|
||||
|
||||
// Background swap for app details, Web5, Network, Settings, My Apps, App Store, Cloud, and Home
|
||||
const showAltBackground = ref(false)
|
||||
const isHomeRoute = computed(() => route.path === '/dashboard' || route.path === '/dashboard/')
|
||||
const showHomeBackground = ref(route.path === '/dashboard' || route.path === '/dashboard/')
|
||||
const showWeb5Background = ref(route.path.includes('/dashboard/web5'))
|
||||
const showNetworkBackground = ref(route.path.includes('/dashboard/server'))
|
||||
const showSettingsBackground = ref(route.path.includes('/dashboard/settings'))
|
||||
const showMyAppsBackground = ref(route.path.includes('/dashboard/apps') && !route.path.includes('/dashboard/apps/'))
|
||||
const showAppStoreBackground = ref(route.path.includes('/dashboard/marketplace'))
|
||||
const showCloudBackground = ref(route.path.includes('/dashboard/cloud'))
|
||||
const showWeb5Overlay = ref(route.path.includes('/dashboard/web5')) // Separate ref for overlay to handle transition delay
|
||||
const showNetworkOverlay = ref(route.path.includes('/dashboard/server'))
|
||||
const showSettingsOverlay = ref(route.path.includes('/dashboard/settings'))
|
||||
const showMyAppsOverlay = ref(route.path.includes('/dashboard/apps') && !route.path.includes('/dashboard/apps/'))
|
||||
const showAppStoreOverlay = ref(route.path.includes('/dashboard/marketplace'))
|
||||
const showCloudOverlay = ref(route.path.includes('/dashboard/cloud'))
|
||||
const isGlitching = ref(false)
|
||||
|
||||
// Background images
|
||||
const currentBackgroundImage = computed(() => {
|
||||
if (showWeb5Background.value) return 'bg-web5.jpg'
|
||||
if (showNetworkBackground.value) return 'bg-network.jpg'
|
||||
if (showSettingsBackground.value) return 'bg-settings.jpg'
|
||||
if (showMyAppsBackground.value) return 'bg-myapps.jpg'
|
||||
if (showAppStoreBackground.value) return 'bg-appstore.jpg'
|
||||
if (showCloudBackground.value) return 'bg-cloud.jpg'
|
||||
if (showHomeBackground.value) return 'bg-home.jpg'
|
||||
return 'bg-intro.jpg'
|
||||
const ROUTE_BACKGROUNDS: Record<string, string> = {
|
||||
'/dashboard': 'bg-home.jpg',
|
||||
'/dashboard/': 'bg-home.jpg',
|
||||
'/dashboard/apps': 'bg-myapps.jpg',
|
||||
'/dashboard/marketplace': 'bg-appstore.jpg',
|
||||
'/dashboard/cloud': 'bg-cloud.jpg',
|
||||
'/dashboard/server': 'bg-network.jpg',
|
||||
'/dashboard/web5': 'bg-web5.jpg',
|
||||
'/dashboard/settings': 'bg-settings.jpg',
|
||||
}
|
||||
|
||||
const backgroundImage = computed(() => {
|
||||
if (isDetailRoute(route.path)) return 'bg-intro.jpg'
|
||||
return ROUTE_BACKGROUNDS[route.path] || 'bg-home.jpg'
|
||||
})
|
||||
|
||||
const altBackgroundImage = computed(() => {
|
||||
if (showWeb5Background.value) return 'bg-web5.jpg'
|
||||
if (showNetworkBackground.value) return 'bg-network.jpg'
|
||||
if (showSettingsBackground.value) return 'bg-settings.jpg'
|
||||
if (showMyAppsBackground.value) return 'bg-myapps.jpg'
|
||||
if (showAppStoreBackground.value) return 'bg-appstore.jpg'
|
||||
if (showCloudBackground.value) return 'bg-cloud.jpg'
|
||||
if (showHomeBackground.value) return 'bg-home.jpg'
|
||||
return 'bg-intro-3.jpg'
|
||||
const isDarkRoute = computed(() => {
|
||||
const p = route.path
|
||||
return p.includes('/dashboard/web5') ||
|
||||
p.includes('/dashboard/server') ||
|
||||
p.includes('/dashboard/settings') ||
|
||||
(p.includes('/dashboard/apps') && !isDetailRoute(p)) ||
|
||||
p.includes('/dashboard/marketplace') ||
|
||||
p.includes('/dashboard/cloud')
|
||||
})
|
||||
|
||||
// Check if overlay should be dark (0.8 opacity)
|
||||
const showDarkOverlay = computed(() => {
|
||||
return showWeb5Overlay.value || showNetworkOverlay.value || showSettingsOverlay.value || showMyAppsOverlay.value || showAppStoreOverlay.value || showCloudOverlay.value
|
||||
const showDarkOverlay = ref(isDarkRoute.value)
|
||||
let overlayTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
watch(isDarkRoute, (dark) => {
|
||||
if (overlayTimer) { clearTimeout(overlayTimer); overlayTimer = null }
|
||||
if (dark) {
|
||||
showDarkOverlay.value = true
|
||||
} else {
|
||||
overlayTimer = scheduledTimeout(() => { showDarkOverlay.value = false }, 450)
|
||||
}
|
||||
})
|
||||
|
||||
const mobileTabBar = ref<HTMLElement | null>(null)
|
||||
const appsTabRef = ref<HTMLElement | null>(null)
|
||||
const marketplaceTabRef = ref<HTMLElement | null>(null)
|
||||
@@ -376,116 +385,17 @@ const cloudTabRef = ref<HTMLElement | null>(null)
|
||||
const networkTabIndicatorLeft = ref(0)
|
||||
const networkTabIndicatorWidth = ref(0)
|
||||
|
||||
function isDetailRoute(path: string) {
|
||||
return (path.includes('/apps/') && !path.endsWith('/apps')) ||
|
||||
(path.includes('/marketplace/') && !path.endsWith('/marketplace'))
|
||||
}
|
||||
|
||||
watch(() => route.path, (newPath) => {
|
||||
// Check if we're on app details OR marketplace app details
|
||||
const isAppDetails = isDetailRoute(newPath)
|
||||
const wasAppDetails = showAltBackground.value
|
||||
|
||||
// Check if we're on special background routes
|
||||
const isHome = newPath === '/dashboard' || newPath === '/dashboard/'
|
||||
const isWeb5 = newPath.includes('/dashboard/web5')
|
||||
const wasWeb5 = showWeb5Background.value
|
||||
const isNetwork = newPath.includes('/dashboard/server')
|
||||
const wasNetwork = showNetworkBackground.value
|
||||
const isSettings = newPath.includes('/dashboard/settings')
|
||||
const wasSettings = showSettingsBackground.value
|
||||
const isMyApps = newPath.includes('/dashboard/apps') && !newPath.includes('/dashboard/apps/')
|
||||
const wasMyApps = showMyAppsBackground.value
|
||||
const isAppStore = newPath.includes('/dashboard/marketplace')
|
||||
const wasAppStore = showAppStoreBackground.value
|
||||
const isCloud = newPath.includes('/dashboard/cloud')
|
||||
const wasCloud = showCloudBackground.value
|
||||
|
||||
// Change background immediately
|
||||
|
||||
showAltBackground.value = isAppDetails
|
||||
showHomeBackground.value = isHome
|
||||
showWeb5Background.value = isWeb5
|
||||
showNetworkBackground.value = isNetwork
|
||||
showSettingsBackground.value = isSettings
|
||||
showMyAppsBackground.value = isMyApps
|
||||
showAppStoreBackground.value = isAppStore
|
||||
showCloudBackground.value = isCloud
|
||||
|
||||
// Handle overlay transitions with delay when leaving special backgrounds
|
||||
// Web5 overlay
|
||||
if (isWeb5) {
|
||||
showWeb5Overlay.value = true
|
||||
} else if (wasWeb5 && !isWeb5) {
|
||||
setTimeout(() => {
|
||||
showWeb5Overlay.value = false
|
||||
}, 450)
|
||||
} else {
|
||||
showWeb5Overlay.value = isWeb5
|
||||
}
|
||||
|
||||
// Network overlay
|
||||
if (isNetwork) {
|
||||
showNetworkOverlay.value = true
|
||||
} else if (wasNetwork && !isNetwork) {
|
||||
setTimeout(() => {
|
||||
showNetworkOverlay.value = false
|
||||
}, 450)
|
||||
} else {
|
||||
showNetworkOverlay.value = isNetwork
|
||||
}
|
||||
|
||||
// Settings overlay
|
||||
if (isSettings) {
|
||||
showSettingsOverlay.value = true
|
||||
} else if (wasSettings && !isSettings) {
|
||||
setTimeout(() => {
|
||||
showSettingsOverlay.value = false
|
||||
}, 450)
|
||||
} else {
|
||||
showSettingsOverlay.value = isSettings
|
||||
}
|
||||
|
||||
// My Apps overlay
|
||||
if (isMyApps) {
|
||||
showMyAppsOverlay.value = true
|
||||
} else if (wasMyApps && !isMyApps) {
|
||||
setTimeout(() => {
|
||||
showMyAppsOverlay.value = false
|
||||
}, 450)
|
||||
} else {
|
||||
showMyAppsOverlay.value = isMyApps
|
||||
}
|
||||
|
||||
// App Store overlay
|
||||
if (isAppStore) {
|
||||
showAppStoreOverlay.value = true
|
||||
} else if (wasAppStore && !isAppStore) {
|
||||
setTimeout(() => {
|
||||
showAppStoreOverlay.value = false
|
||||
}, 450)
|
||||
} else {
|
||||
showAppStoreOverlay.value = isAppStore
|
||||
}
|
||||
|
||||
// Cloud overlay
|
||||
if (isCloud) {
|
||||
showCloudOverlay.value = true
|
||||
} else if (wasCloud && !isCloud) {
|
||||
setTimeout(() => {
|
||||
showCloudOverlay.value = false
|
||||
}, 450)
|
||||
} else {
|
||||
showCloudOverlay.value = isCloud
|
||||
}
|
||||
|
||||
// Trigger glitch ONLY when going forward (to app details), not back
|
||||
|
||||
if (isAppDetails && !wasAppDetails) {
|
||||
setTimeout(() => {
|
||||
scheduledTimeout(() => {
|
||||
isGlitching.value = true
|
||||
setTimeout(() => {
|
||||
isGlitching.value = false
|
||||
}, 375) // Glitch duration - 25% faster
|
||||
}, 500) // Wait for background 3D transition to complete
|
||||
scheduledTimeout(() => { isGlitching.value = false }, 375)
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -550,6 +460,12 @@ function updateNetworkTabIndicator() {
|
||||
networkTabIndicatorWidth.value = tabRect.width
|
||||
}
|
||||
|
||||
function onResize() {
|
||||
updateTabBarHeight()
|
||||
updateAppsTabIndicator()
|
||||
updateNetworkTabIndicator()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.body.classList.add('dashboard-active')
|
||||
if (loginTransition.justLoggedIn) {
|
||||
@@ -557,39 +473,30 @@ onMounted(() => {
|
||||
showZoomIn.value = true
|
||||
loginTransition.setPendingWelcomeTyping(true)
|
||||
loginTransition.setJustLoggedIn(false)
|
||||
// Trigger glitches during background reveal (0.5s, 1.2s, 2s into 2.8s zoom)
|
||||
const triggerRevealGlitch = () => {
|
||||
isGlitching.value = true
|
||||
setTimeout(() => { isGlitching.value = false }, 380)
|
||||
scheduledTimeout(() => { isGlitching.value = false }, 380)
|
||||
}
|
||||
setTimeout(triggerRevealGlitch, 500)
|
||||
setTimeout(triggerRevealGlitch, 1200)
|
||||
setTimeout(triggerRevealGlitch, 2000)
|
||||
// Keep glass-throw active long enough for sidebar (last to animate, ~7.5s)
|
||||
setTimeout(() => {
|
||||
showZoomIn.value = false
|
||||
}, 8000)
|
||||
// Trigger welcome typing on Home after main content animation finishes
|
||||
setTimeout(() => {
|
||||
scheduledTimeout(triggerRevealGlitch, 500)
|
||||
scheduledTimeout(triggerRevealGlitch, 1200)
|
||||
scheduledTimeout(triggerRevealGlitch, 2000)
|
||||
scheduledTimeout(() => { showZoomIn.value = false }, 8000)
|
||||
scheduledTimeout(() => {
|
||||
loginTransition.setStartWelcomeTyping(true)
|
||||
loginTransition.setPendingWelcomeTyping(false)
|
||||
}, 4000)
|
||||
}
|
||||
|
||||
updateTabBarHeight()
|
||||
updateAppsTabIndicator()
|
||||
updateNetworkTabIndicator()
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
updateTabBarHeight()
|
||||
updateAppsTabIndicator()
|
||||
updateNetworkTabIndicator()
|
||||
})
|
||||
onResize()
|
||||
window.addEventListener('resize', onResize)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.body.classList.remove('dashboard-active')
|
||||
window.removeEventListener('resize', updateTabBarHeight)
|
||||
window.removeEventListener('resize', onResize)
|
||||
for (const id of pendingTimers) clearTimeout(id)
|
||||
pendingTimers.length = 0
|
||||
if (overlayTimer) { clearTimeout(overlayTimer); overlayTimer = null }
|
||||
})
|
||||
|
||||
// Watch route changes to update indicator position
|
||||
@@ -676,14 +583,6 @@ const mobileNavItems = [
|
||||
},
|
||||
]
|
||||
|
||||
// Use appropriate nav items based on screen size
|
||||
// @ts-ignore - Computed kept for future use
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const navItems = computed(() => {
|
||||
if (typeof window === 'undefined') return desktopNavItems
|
||||
return window.innerWidth >= 768 ? desktopNavItems : mobileNavItems
|
||||
})
|
||||
|
||||
function getIconPath(iconName: string): string[] {
|
||||
const icons: Record<string, string[]> = {
|
||||
home: ['M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6'],
|
||||
@@ -846,9 +745,6 @@ function getTransitionName(currentRoute: any) {
|
||||
.zoom-reveal-bg {
|
||||
animation: zoom-reveal 2.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.zoom-reveal-bg {
|
||||
opacity: 0;
|
||||
transform: scale(0.15);
|
||||
filter: blur(24px);
|
||||
|
||||
Reference in New Issue
Block a user