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: {
|
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,
|
||||||
|
|
|
||||||
|
|
@ -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" />
|
||||||
|
|
|
||||||
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>
|
<template>
|
||||||
<div class="h-full w-full">
|
<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 />
|
<ChatComponent />
|
||||||
</div>
|
</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>
|
</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>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue