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:
parent
c692664c93
commit
0ee0bc428c
4 changed files with 175 additions and 35 deletions
|
|
@ -42,7 +42,7 @@ export const appConfig: AppConfig = {
|
|||
chat: {
|
||||
name: 'chat',
|
||||
enabled: true,
|
||||
lazy: true, // Load on demand
|
||||
lazy: false, // Load on startup to register routes
|
||||
config: {
|
||||
maxMessages: 500,
|
||||
autoScroll: true,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { auth } from '@/composables/useAuth'
|
|||
import { useMarketPreloader } from '@/composables/useMarketPreloader'
|
||||
import { useMarketStore } from '@/stores/market'
|
||||
import { nostrChat } from '@/composables/useNostrChat'
|
||||
import { useModularNavigation } from '@/composables/useModularNavigation'
|
||||
|
||||
interface NavigationItem {
|
||||
name: string
|
||||
|
|
@ -31,13 +32,9 @@ const showProfileDialog = ref(false)
|
|||
const showLogoutConfirm = ref(false)
|
||||
const marketPreloader = useMarketPreloader()
|
||||
const marketStore = useMarketStore()
|
||||
const { navigation: modularNavigation, userMenuItems } = useModularNavigation()
|
||||
|
||||
const navigation = computed<NavigationItem[]>(() => [
|
||||
{ name: t('nav.home'), href: '/' },
|
||||
{ name: t('nav.events'), href: '/events' },
|
||||
{ name: t('nav.market'), href: '/market' },
|
||||
{ name: t('nav.chat'), href: '/chat' },
|
||||
])
|
||||
const navigation = modularNavigation
|
||||
|
||||
// Compute total wallet balance
|
||||
const totalBalance = computed(() => {
|
||||
|
|
@ -167,18 +164,13 @@ const handleLogout = async () => {
|
|||
<User class="h-4 w-4" />
|
||||
Profile
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="() => router.push('/my-tickets')" class="gap-2">
|
||||
<Ticket class="h-4 w-4" />
|
||||
My Tickets
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem @click="() => router.push('/market-dashboard')" class="gap-2">
|
||||
<BarChart3 class="h-4 w-4" />
|
||||
Market Dashboard
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="() => router.push('/relay-hub-status')" class="gap-2">
|
||||
<Activity class="h-4 w-4" />
|
||||
Relay Hub Status
|
||||
<DropdownMenuItem
|
||||
v-for="item in userMenuItems"
|
||||
:key="item.href"
|
||||
@click="() => router.push(item.href)"
|
||||
class="gap-2">
|
||||
<component :is="item.icon === 'Ticket' ? Ticket : item.icon === 'BarChart3' ? BarChart3 : Activity" class="h-4 w-4" />
|
||||
{{ item.name }}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem @click="showLogoutConfirm = true" class="gap-2 text-destructive">
|
||||
|
|
@ -256,21 +248,15 @@ const handleLogout = async () => {
|
|||
<User class="h-4 w-4" />
|
||||
Profile
|
||||
</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">
|
||||
<Ticket class="h-4 w-4" />
|
||||
My Tickets
|
||||
</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
|
||||
<component :is="item.icon === 'Ticket' ? Ticket : item.icon === 'BarChart3' ? BarChart3 : Activity" class="h-4 w-4" />
|
||||
{{ item.name }}
|
||||
</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">
|
||||
<LogOut class="h-4 w-4" />
|
||||
|
|
|
|||
106
src/composables/useModularNavigation.ts
Normal file
106
src/composables/useModularNavigation.ts
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,57 @@
|
|||
<template>
|
||||
<div class="h-full w-full">
|
||||
<ChatComponent />
|
||||
<div class="h-full w-full p-8">
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from '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>
|
||||
Loading…
Add table
Add a link
Reference in a new issue