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:
Dorian
2026-03-01 18:07:35 +00:00
parent 7a05e11834
commit 94eb1e4283
7 changed files with 218 additions and 399 deletions
+19 -14
View File
@@ -171,7 +171,7 @@
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { computed, ref, onBeforeUnmount } from 'vue'
import { useRouter, RouterLink } from 'vue-router'
import { useAppStore } from '../stores/app'
import { useAppLauncherStore } from '../stores/appLauncher'
@@ -187,7 +187,7 @@ const loadingActions = ref<Record<string, boolean>>({})
// Use real packages from store - no more dummy apps
const packages = computed(() => {
const realPackages = store.packages
console.log('[Apps] Real packages from store:', Object.keys(realPackages || {}).length, 'apps')
if (import.meta.env.DEV) console.log('[Apps] Real packages from store:', Object.keys(realPackages || {}).length, 'apps')
return realPackages || {}
})
@@ -276,7 +276,7 @@ function launchApp(id: string) {
}
// For other apps, navigate to app details which has launch functionality
router.push(`/dashboard/apps/${id}`)
router.push(`/dashboard/apps/${id}`).catch(() => {})
}
function getStatusClass(state: PackageState): string {
@@ -297,19 +297,20 @@ function getStatusClass(state: PackageState): string {
}
function goToApp(id: string) {
router.push(`/dashboard/apps/${id}`)
router.push(`/dashboard/apps/${id}`).catch(() => {})
}
const actionTimers = new Map<string, ReturnType<typeof setTimeout>>()
async function startApp(id: string) {
loadingActions.value[id] = true
try {
await store.startPackage(id)
// Wait for state update from WebSocket
// The loader will be cleared when we receive the updated state
// For now, keep a max timeout as fallback
setTimeout(() => {
if (actionTimers.has(id)) clearTimeout(actionTimers.get(id)!)
actionTimers.set(id, setTimeout(() => {
loadingActions.value[id] = false
}, 5000)
actionTimers.delete(id)
}, 5000))
} catch (err) {
console.error('Failed to start app:', err)
loadingActions.value[id] = false
@@ -320,18 +321,22 @@ async function stopApp(id: string) {
loadingActions.value[id] = true
try {
await store.stopPackage(id)
// Wait for state update from WebSocket
// The loader will be cleared when we receive the updated state
// For now, keep a max timeout as fallback
setTimeout(() => {
if (actionTimers.has(id)) clearTimeout(actionTimers.get(id)!)
actionTimers.set(id, setTimeout(() => {
loadingActions.value[id] = false
}, 5000)
actionTimers.delete(id)
}, 5000))
} catch (err) {
console.error('Failed to stop app:', err)
loadingActions.value[id] = false
}
}
onBeforeUnmount(() => {
for (const t of actionTimers.values()) clearTimeout(t)
actionTimers.clear()
})
// @ts-ignore - Function kept for future use
// eslint-disable-next-line @typescript-eslint/no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-unused-vars