Refactor chat module and navigation components for improved user experience

- Update app configuration to load chat module on startup for route registration.
- Introduce useModularNavigation composable for dynamic navigation based on enabled modules.
- Simplify Navbar.vue by utilizing userMenuItems for dropdown and button rendering, enhancing maintainability.
- Enhance ChatPage.vue with detailed debug information and user authentication checks for better feedback.
This commit is contained in:
padreug 2025-09-05 00:31:53 +02:00
parent c692664c93
commit 0ee0bc428c
4 changed files with 175 additions and 35 deletions

View file

@ -42,7 +42,7 @@ export const appConfig: AppConfig = {
chat: { chat: {
name: 'chat', name: 'chat',
enabled: true, enabled: true,
lazy: true, // Load on demand lazy: false, // Load on startup to register routes
config: { config: {
maxMessages: 500, maxMessages: 500,
autoScroll: true, autoScroll: true,

View file

@ -16,6 +16,7 @@ import { auth } from '@/composables/useAuth'
import { useMarketPreloader } from '@/composables/useMarketPreloader' import { useMarketPreloader } from '@/composables/useMarketPreloader'
import { useMarketStore } from '@/stores/market' import { useMarketStore } from '@/stores/market'
import { nostrChat } from '@/composables/useNostrChat' import { nostrChat } from '@/composables/useNostrChat'
import { useModularNavigation } from '@/composables/useModularNavigation'
interface NavigationItem { interface NavigationItem {
name: string name: string
@ -31,13 +32,9 @@ const showProfileDialog = ref(false)
const showLogoutConfirm = ref(false) const showLogoutConfirm = ref(false)
const marketPreloader = useMarketPreloader() const marketPreloader = useMarketPreloader()
const marketStore = useMarketStore() const marketStore = useMarketStore()
const { navigation: modularNavigation, userMenuItems } = useModularNavigation()
const navigation = computed<NavigationItem[]>(() => [ const navigation = modularNavigation
{ name: t('nav.home'), href: '/' },
{ name: t('nav.events'), href: '/events' },
{ name: t('nav.market'), href: '/market' },
{ name: t('nav.chat'), href: '/chat' },
])
// Compute total wallet balance // Compute total wallet balance
const totalBalance = computed(() => { const totalBalance = computed(() => {
@ -167,18 +164,13 @@ const handleLogout = async () => {
<User class="h-4 w-4" /> <User class="h-4 w-4" />
Profile Profile
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem @click="() => router.push('/my-tickets')" class="gap-2"> <DropdownMenuItem
<Ticket class="h-4 w-4" /> v-for="item in userMenuItems"
My Tickets :key="item.href"
</DropdownMenuItem> @click="() => router.push(item.href)"
class="gap-2">
<DropdownMenuItem @click="() => router.push('/market-dashboard')" class="gap-2"> <component :is="item.icon === 'Ticket' ? Ticket : item.icon === 'BarChart3' ? BarChart3 : Activity" class="h-4 w-4" />
<BarChart3 class="h-4 w-4" /> {{ item.name }}
Market Dashboard
</DropdownMenuItem>
<DropdownMenuItem @click="() => router.push('/relay-hub-status')" class="gap-2">
<Activity class="h-4 w-4" />
Relay Hub Status
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem @click="showLogoutConfirm = true" class="gap-2 text-destructive"> <DropdownMenuItem @click="showLogoutConfirm = true" class="gap-2 text-destructive">
@ -256,21 +248,15 @@ const handleLogout = async () => {
<User class="h-4 w-4" /> <User class="h-4 w-4" />
Profile Profile
</Button> </Button>
<Button variant="ghost" size="sm" @click="() => router.push('/my-tickets')" <Button
v-for="item in userMenuItems"
:key="item.href"
variant="ghost"
size="sm"
@click="() => router.push(item.href)"
class="w-full justify-start gap-2"> class="w-full justify-start gap-2">
<Ticket class="h-4 w-4" /> <component :is="item.icon === 'Ticket' ? Ticket : item.icon === 'BarChart3' ? BarChart3 : Activity" class="h-4 w-4" />
My Tickets {{ item.name }}
</Button>
<Button variant="ghost" size="sm" @click="() => router.push('/market-dashboard')"
class="w-full justify-start gap-2">
<BarChart3 class="h-4 w-4" />
Market Dashboard
</Button>
<Button variant="ghost" size="sm" @click="() => router.push('/relay-hub-status')"
class="w-full justify-start gap-2">
<Activity class="h-4 w-4" />
Relay Hub Status
</Button> </Button>
<Button variant="ghost" size="sm" @click="showLogoutConfirm = true" class="w-full justify-start gap-2 text-destructive hover:text-destructive/90 hover:bg-destructive/10"> <Button variant="ghost" size="sm" @click="showLogoutConfirm = true" class="w-full justify-start gap-2 text-destructive hover:text-destructive/90 hover:bg-destructive/10">
<LogOut class="h-4 w-4" /> <LogOut class="h-4 w-4" />

View file

@ -0,0 +1,106 @@
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { pluginManager } from '@/core/plugin-manager'
import appConfig from '@/app.config'
interface NavigationItem {
name: string
href: string
icon?: string
requiresAuth?: boolean
}
/**
* Composable for dynamic navigation based on enabled modules
*/
export function useModularNavigation() {
const { t } = useI18n()
/**
* Get navigation items for enabled modules
*/
const navigation = computed<NavigationItem[]>(() => {
const items: NavigationItem[] = []
// Always include home
items.push({ name: t('nav.home'), href: '/', requiresAuth: true })
// Add navigation items based on enabled modules
if (appConfig.modules.events.enabled) {
items.push({
name: t('nav.events'),
href: '/events',
requiresAuth: true
})
}
if (appConfig.modules.market.enabled) {
items.push({
name: t('nav.market'),
href: '/market',
requiresAuth: true
})
}
if (appConfig.modules.chat.enabled) {
items.push({
name: t('nav.chat'),
href: '/chat',
requiresAuth: true
})
}
return items
})
/**
* Get dropdown menu items for authenticated users
*/
const userMenuItems = computed<NavigationItem[]>(() => {
const items: NavigationItem[] = []
// Events module items
if (appConfig.modules.events.enabled) {
items.push({
name: 'My Tickets',
href: '/my-tickets',
icon: 'Ticket',
requiresAuth: true
})
}
// Market module items
if (appConfig.modules.market.enabled) {
items.push({
name: 'Market Dashboard',
href: '/market-dashboard',
icon: 'BarChart3',
requiresAuth: true
})
}
// Base module items (always available)
items.push({
name: 'Relay Hub Status',
href: '/relay-hub-status',
icon: 'Activity',
requiresAuth: true
})
return items
})
/**
* Check if a module is enabled and installed
*/
const isModuleAvailable = (moduleName: string): boolean => {
return appConfig.modules[moduleName as keyof typeof appConfig.modules]?.enabled &&
pluginManager.isInstalled(moduleName)
}
return {
navigation,
userMenuItems,
isModuleAvailable
}
}

View file

@ -1,9 +1,57 @@
<template> <template>
<div class="h-full w-full"> <div class="h-full w-full p-8">
<ChatComponent /> <div class="text-center">
<h1 class="text-2xl font-bold mb-4">Chat Module</h1>
<p class="text-muted-foreground mb-4">Status: {{ debugInfo.status }}</p>
<div v-if="!debugInfo.isAuthenticated" class="bg-yellow-100 border border-yellow-400 text-yellow-700 px-4 py-3 rounded mb-4">
<p><strong>Authentication Required:</strong> Please log in to use chat</p>
</div>
<div v-else-if="!debugInfo.hasNostrKeys" class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
<p><strong>Nostr Keys Missing:</strong> Your account needs Nostr keys configured</p>
<p class="text-sm mt-2">Key status: {{ debugInfo.keyStatus.message }}</p>
</div>
<div v-else-if="!debugInfo.isConnected" class="bg-blue-100 border border-blue-400 text-blue-700 px-4 py-3 rounded mb-4">
<p><strong>Connecting to Nostr relays...</strong></p>
</div>
<div v-else class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-4">
<p><strong>Ready!</strong> Chat should work now</p>
<ChatComponent />
</div>
<details class="mt-8 text-left">
<summary class="cursor-pointer font-semibold">Debug Info</summary>
<pre class="mt-4 p-4 bg-gray-100 rounded text-xs overflow-auto">{{ JSON.stringify(debugInfo, null, 2) }}</pre>
</details>
</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import ChatComponent from '../components/ChatComponent.vue' import ChatComponent from '../components/ChatComponent.vue'
import { nostrChat } from '@/composables/useNostrChat'
import { useAuth } from '@/composables/useAuth'
const auth = useAuth()
const debugInfo = computed(() => {
const keyStatus = nostrChat.getNostrKeyStatus()
return {
status: nostrChat.isConnected.value ? 'Connected' : 'Disconnected',
isAuthenticated: auth.isAuthenticated.value,
hasNostrKeys: nostrChat.hasNostrKeys.value,
isConnected: nostrChat.isConnected.value,
keyStatus,
peerCount: nostrChat.peers.value.length,
totalUnread: nostrChat.totalUnreadCount.value,
currentUser: nostrChat.currentUser.value?.pubkey ? {
pubkey: nostrChat.currentUser.value.pubkey.slice(0, 16) + '...'
} : null
}
})
</script> </script>