fix: Refine mobile chat layout and peer selection logic

- Adjust mobile chat component to ensure proper display of peer list and chat view based on selection.
- Enhance avatar and username rendering for selected peers, ensuring fallback options are in place.
- Improve responsiveness and usability of the chat interface, particularly for mobile users.
- Update message display logic to maintain consistency across different screen sizes.

refactor: Update desktop chat layout for improved structure

- Change the layout of the desktop chat component to use a full-height column flexbox, enhancing the overall structure and responsiveness.
- Ensure better alignment and spacing within the chat interface for a more organized appearance.
This commit is contained in:
padreug 2025-08-05 20:52:12 +02:00
parent 87663d1d87
commit 3bd87ee712

View file

@ -1,7 +1,7 @@
<template> <template>
<div class="flex flex-col h-full"> <div class="flex flex-col h-full">
<!-- Mobile: Peer List View --> <!-- Mobile: Peer List View -->
<div v-if="!selectedPeer || (isMobile && !showChat)" class="flex flex-col h-full"> <div v-if="isMobile && (!selectedPeer || !showChat)" class="flex flex-col h-full">
<!-- Header --> <!-- Header -->
<div class="flex items-center justify-between p-4 border-b"> <div class="flex items-center justify-between p-4 border-b">
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">
@ -60,26 +60,26 @@
<div v-else-if="isMobile && showChat" class="flex flex-col h-full"> <div v-else-if="isMobile && showChat" class="flex flex-col h-full">
<!-- Chat Header with Back Button --> <!-- Chat Header with Back Button -->
<div class="flex items-center justify-between p-4 border-b"> <div class="flex items-center justify-between p-4 border-b">
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
@click="goBackToPeers" @click="goBackToPeers"
class="mr-2" class="mr-2"
> >
<ArrowLeft class="h-5 w-5" /> <ArrowLeft class="h-5 w-5" />
</Button> </Button>
<Avatar class="h-8 w-8"> <Avatar class="h-8 w-8">
<AvatarImage v-if="getPeerAvatar(selectedPeer)" :src="getPeerAvatar(selectedPeer)!" /> <AvatarImage v-if="selectedPeer && getPeerAvatar(selectedPeer)" :src="getPeerAvatar(selectedPeer)!" />
<AvatarFallback>{{ getPeerInitials(selectedPeer) }}</AvatarFallback> <AvatarFallback>{{ selectedPeer ? getPeerInitials(selectedPeer) : 'U' }}</AvatarFallback>
</Avatar> </Avatar>
<div> <div>
<h3 class="font-medium">{{ selectedPeer.username || 'Unknown User' }}</h3> <h3 class="font-medium">{{ selectedPeer?.username || 'Unknown User' }}</h3>
<p class="text-xs text-muted-foreground"> <p class="text-xs text-muted-foreground">
{{ formatPubkey(selectedPeer.pubkey) }} {{ selectedPeer ? formatPubkey(selectedPeer.pubkey) : '' }}
</p> </p>
</div>
</div> </div>
</div>
<Badge v-if="isConnected" variant="default" class="text-xs"> <Badge v-if="isConnected" variant="default" class="text-xs">
Connected Connected
</Badge> </Badge>
@ -134,114 +134,135 @@
</div> </div>
<!-- Desktop: Split View --> <!-- Desktop: Split View -->
<div v-else class="flex flex-1 overflow-hidden"> <div v-else-if="!isMobile" class="flex flex-col h-full">
<!-- Peer List --> <!-- Header -->
<div class="w-80 border-r bg-muted/30"> <div class="flex items-center justify-between p-4 border-b">
<div class="p-4 border-b"> <div class="flex items-center space-x-3">
<h3 class="font-medium">Peers ({{ peers.length }})</h3> <h2 class="text-lg font-semibold">Chat</h2>
<Badge v-if="isConnected" variant="default" class="text-xs">
Connected
</Badge>
<Badge v-else variant="secondary" class="text-xs">
Disconnected
</Badge>
</div> </div>
<ScrollArea class="h-full"> <Button @click="refreshPeers" :disabled="isLoading" size="sm">
<div class="p-2 space-y-1"> <RefreshCw v-if="isLoading" class="h-4 w-4 animate-spin" />
<div <RefreshCw v-else class="h-4 w-4" />
v-for="peer in peers" Refresh Peers
:key="peer.user_id" </Button>
@click="selectPeer(peer)"
:class="[
'flex items-center space-x-3 p-3 rounded-lg cursor-pointer transition-colors',
selectedPeer?.user_id === peer.user_id
? 'bg-primary text-primary-foreground'
: 'hover:bg-muted'
]"
>
<Avatar class="h-8 w-8">
<AvatarImage v-if="getPeerAvatar(peer)" :src="getPeerAvatar(peer)!" />
<AvatarFallback>{{ getPeerInitials(peer) }}</AvatarFallback>
</Avatar>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium truncate">
{{ peer.username || 'Unknown User' }}
</p>
<p class="text-xs text-muted-foreground truncate">
{{ formatPubkey(peer.pubkey) }}
</p>
</div>
</div>
</div>
</ScrollArea>
</div> </div>
<!-- Chat Area --> <!-- Main Content -->
<div class="flex-1 flex flex-col"> <div class="flex flex-1 overflow-hidden">
<div v-if="selectedPeer" class="flex-1 flex flex-col"> <!-- Peer List -->
<!-- Chat Header --> <div class="w-80 border-r bg-muted/30">
<div class="p-4 border-b"> <div class="p-4 border-b">
<div class="flex items-center space-x-3"> <h3 class="font-medium">Peers ({{ peers.length }})</h3>
<Avatar class="h-8 w-8">
<AvatarImage v-if="getPeerAvatar(selectedPeer)" :src="getPeerAvatar(selectedPeer)!" />
<AvatarFallback>{{ getPeerInitials(selectedPeer) }}</AvatarFallback>
</Avatar>
<div>
<h3 class="font-medium">{{ selectedPeer.username || 'Unknown User' }}</h3>
<p class="text-sm text-muted-foreground">
{{ formatPubkey(selectedPeer.pubkey) }}
</p>
</div>
</div>
</div> </div>
<ScrollArea class="h-full">
<!-- Messages --> <div class="p-2 space-y-1">
<ScrollArea class="flex-1 p-4">
<div class="space-y-4">
<div <div
v-for="message in currentMessages" v-for="peer in peers"
:key="message.id" :key="peer.user_id"
@click="selectPeer(peer)"
:class="[ :class="[
'flex', 'flex items-center space-x-3 p-3 rounded-lg cursor-pointer transition-colors',
message.sent ? 'justify-end' : 'justify-start' selectedPeer?.user_id === peer.user_id
? 'bg-primary text-primary-foreground'
: 'hover:bg-muted'
]" ]"
> >
<div <Avatar class="h-8 w-8">
:class="[ <AvatarImage v-if="getPeerAvatar(peer)" :src="getPeerAvatar(peer)!" />
'max-w-xs lg:max-w-md px-4 py-2 rounded-lg', <AvatarFallback>{{ getPeerInitials(peer) }}</AvatarFallback>
message.sent </Avatar>
? 'bg-primary text-primary-foreground' <div class="flex-1 min-w-0">
: 'bg-muted' <p class="text-sm font-medium truncate">
]" {{ peer.username || 'Unknown User' }}
> </p>
<p class="text-sm">{{ message.content }}</p> <p class="text-xs text-muted-foreground truncate">
<p class="text-xs opacity-70 mt-1"> {{ formatPubkey(peer.pubkey) }}
{{ formatTime(message.created_at) }}
</p> </p>
</div> </div>
</div> </div>
</div> </div>
<div ref="messagesEndRef" />
</ScrollArea> </ScrollArea>
<!-- Message Input -->
<div class="p-4 border-t">
<form @submit.prevent="sendMessage" class="flex space-x-2">
<Input
v-model="messageInput"
placeholder="Type a message..."
:disabled="!isConnected || !selectedPeer"
class="flex-1"
/>
<Button type="submit" :disabled="!isConnected || !selectedPeer || !messageInput.trim()">
<Send class="h-4 w-4" />
</Button>
</form>
</div>
</div> </div>
<!-- No Peer Selected --> <!-- Chat Area -->
<div v-else class="flex-1 flex items-center justify-center"> <div class="flex-1 flex flex-col">
<div class="text-center"> <div v-if="selectedPeer" class="flex-1 flex flex-col">
<MessageSquare class="h-12 w-12 text-muted-foreground mx-auto mb-4" /> <!-- Chat Header -->
<h3 class="text-lg font-medium mb-2">No peer selected</h3> <div class="p-4 border-b">
<p class="text-muted-foreground"> <div class="flex items-center space-x-3">
Select a peer from the list to start chatting <Avatar class="h-8 w-8">
</p> <AvatarImage v-if="getPeerAvatar(selectedPeer)" :src="getPeerAvatar(selectedPeer)!" />
<AvatarFallback>{{ getPeerInitials(selectedPeer) }}</AvatarFallback>
</Avatar>
<div>
<h3 class="font-medium">{{ selectedPeer.username || 'Unknown User' }}</h3>
<p class="text-sm text-muted-foreground">
{{ formatPubkey(selectedPeer.pubkey) }}
</p>
</div>
</div>
</div>
<!-- Messages -->
<ScrollArea class="flex-1 p-4">
<div class="space-y-4">
<div
v-for="message in currentMessages"
:key="message.id"
:class="[
'flex',
message.sent ? 'justify-end' : 'justify-start'
]"
>
<div
:class="[
'max-w-xs lg:max-w-md px-4 py-2 rounded-lg',
message.sent
? 'bg-primary text-primary-foreground'
: 'bg-muted'
]"
>
<p class="text-sm">{{ message.content }}</p>
<p class="text-xs opacity-70 mt-1">
{{ formatTime(message.created_at) }}
</p>
</div>
</div>
</div>
<div ref="messagesEndRef" />
</ScrollArea>
<!-- Message Input -->
<div class="p-4 border-t">
<form @submit.prevent="sendMessage" class="flex space-x-2">
<Input
v-model="messageInput"
placeholder="Type a message..."
:disabled="!isConnected || !selectedPeer"
class="flex-1"
/>
<Button type="submit" :disabled="!isConnected || !selectedPeer || !messageInput.trim()">
<Send class="h-4 w-4" />
</Button>
</form>
</div>
</div>
<!-- No Peer Selected -->
<div v-else class="flex-1 flex items-center justify-center">
<div class="text-center">
<MessageSquare class="h-12 w-12 text-muted-foreground mx-auto mb-4" />
<h3 class="text-lg font-medium mb-2">No peer selected</h3>
<p class="text-muted-foreground">
Select a peer from the list to start chatting
</p>
</div>
</div> </div>
</div> </div>
</div> </div>