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:
parent
87663d1d87
commit
3bd87ee712
1 changed files with 133 additions and 112 deletions
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue