Updating ConversationsView wiring and the nav badge next.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue';
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { api } from '@/lib/api';
|
||||
import type {
|
||||
@@ -8,7 +8,12 @@ import type {
|
||||
ConversationRoomDto,
|
||||
} from '@mento/shared';
|
||||
import { formatClockFa, toFaDigits } from '@/lib/jalali';
|
||||
import { useOrgChat } from '@/lib/useOrgChat';
|
||||
import {
|
||||
onRoomLiveEvent,
|
||||
useOrgChat,
|
||||
type ChatDelivery,
|
||||
type RoomLiveEvent,
|
||||
} from '@/lib/useOrgChat';
|
||||
import UiIcon from '@/components/UiIcon.vue';
|
||||
import UserAvatar from '@/components/UserAvatar.vue';
|
||||
|
||||
@@ -24,9 +29,22 @@ const showNewGroup = ref(false);
|
||||
const groupName = ref('');
|
||||
const groupMemberIds = ref<string[]>([]);
|
||||
const dmPeerId = ref('');
|
||||
const chatUserId = ref('');
|
||||
|
||||
const { messages, loadingMessages, status, loadMessages, sendText, sendRead } = useOrgChat();
|
||||
const {
|
||||
messages,
|
||||
loadingMessages,
|
||||
status,
|
||||
loadMessages,
|
||||
sendText,
|
||||
sendRead,
|
||||
ensureConnected,
|
||||
setConversationsPageOpen,
|
||||
refreshUnreadTotal,
|
||||
clearActiveRoom,
|
||||
} = useOrgChat();
|
||||
const msgStreamEl = ref<HTMLElement | null>(null);
|
||||
let stopRoomLive: (() => boolean) | undefined;
|
||||
|
||||
const canCreateGroup = computed(() => access.value?.canCreateGroup ?? false);
|
||||
|
||||
@@ -45,8 +63,6 @@ const sortedRooms = computed(() =>
|
||||
}),
|
||||
);
|
||||
|
||||
const chatUserId = ref('');
|
||||
|
||||
function roomTitle(room: ConversationRoomDto) {
|
||||
if (room.kind === 'group') return room.name;
|
||||
const peer = peers.value.find((p) => p.id === room.peerUserId);
|
||||
@@ -62,6 +78,44 @@ function formatListTime(iso: string | null) {
|
||||
}
|
||||
}
|
||||
|
||||
function deliveryLabel(delivery?: ChatDelivery) {
|
||||
if (delivery === 'read') return '✓✓';
|
||||
if (delivery === 'sent') return '✓';
|
||||
return '…';
|
||||
}
|
||||
|
||||
function applyRoomLive(event: RoomLiveEvent) {
|
||||
const idx = rooms.value.findIndex((r) => r.externalRoomId === event.roomId);
|
||||
if (idx < 0) {
|
||||
void refreshRoomsSoft();
|
||||
return;
|
||||
}
|
||||
const room = rooms.value[idx];
|
||||
const isActive = activeRoom.value?.externalRoomId === event.roomId;
|
||||
const next: ConversationRoomDto = {
|
||||
...room,
|
||||
lastMessagePreview: event.preview,
|
||||
lastMessageAt: event.at,
|
||||
unreadCount: event.fromSelf || isActive ? 0 : (room.unreadCount || 0) + 1,
|
||||
};
|
||||
const nextRooms = [...rooms.value];
|
||||
nextRooms[idx] = next;
|
||||
rooms.value = nextRooms;
|
||||
if (isActive && activeRoom.value) {
|
||||
activeRoom.value = next;
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshRoomsSoft() {
|
||||
try {
|
||||
const { data } = await api.get<ConversationRoomDto[]>('/conversations/rooms');
|
||||
rooms.value = data;
|
||||
await refreshUnreadTotal();
|
||||
} catch {
|
||||
// keep previous
|
||||
}
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
loading.value = true;
|
||||
error.value = '';
|
||||
@@ -87,6 +141,7 @@ async function bootstrap() {
|
||||
}
|
||||
if (roomsOutcome.status === 'fulfilled') {
|
||||
rooms.value = roomsOutcome.value.data;
|
||||
await refreshUnreadTotal();
|
||||
} else {
|
||||
const e = roomsOutcome.reason;
|
||||
if (axios.isAxiosError(e)) {
|
||||
@@ -101,6 +156,8 @@ async function bootstrap() {
|
||||
error.value = 'بارگذاری لیست گفتگو ناموفق بود.';
|
||||
}
|
||||
}
|
||||
|
||||
await ensureConnected();
|
||||
} catch (e) {
|
||||
if (axios.isAxiosError(e) && e.response?.status === 403) {
|
||||
error.value = 'ماژول گفتگو برای این محیط فعال نیست.';
|
||||
@@ -118,9 +175,14 @@ async function bootstrap() {
|
||||
}
|
||||
|
||||
async function openRoom(room: ConversationRoomDto) {
|
||||
activeRoom.value = room;
|
||||
const cleared = { ...room, unreadCount: 0 };
|
||||
activeRoom.value = cleared;
|
||||
rooms.value = rooms.value.map((r) =>
|
||||
r.externalRoomId === room.externalRoomId ? cleared : r,
|
||||
);
|
||||
await loadMessages(room.externalRoomId);
|
||||
sendRead(room.externalRoomId);
|
||||
void refreshUnreadTotal();
|
||||
}
|
||||
|
||||
async function submitMessage() {
|
||||
@@ -193,8 +255,16 @@ watch(loadingMessages, (v) => {
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
setConversationsPageOpen(true);
|
||||
stopRoomLive = onRoomLiveEvent(applyRoomLive);
|
||||
void bootstrap();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
setConversationsPageOpen(false);
|
||||
stopRoomLive?.();
|
||||
clearActiveRoom();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -274,7 +344,21 @@ onMounted(() => {
|
||||
>
|
||||
<div class="bubble">
|
||||
<p>{{ msg.content }}</p>
|
||||
<time>{{ formatListTime(msg.createdAt) }}</time>
|
||||
<div class="bubble-meta">
|
||||
<time>{{ formatListTime(msg.createdAt) }}</time>
|
||||
<span
|
||||
v-if="msg.senderId === chatUserId || msg.optimistic"
|
||||
class="ticks"
|
||||
:class="msg.delivery"
|
||||
:title="
|
||||
msg.delivery === 'read'
|
||||
? 'خوانده شد'
|
||||
: msg.delivery === 'sent'
|
||||
? 'ارسال شد'
|
||||
: 'در حال ارسال'
|
||||
"
|
||||
>{{ deliveryLabel(msg.delivery) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -572,10 +656,32 @@ onMounted(() => {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
.bubble-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
.bubble time {
|
||||
font-size: 0.68rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
.ticks {
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: -0.08em;
|
||||
line-height: 1;
|
||||
color: #94a3b8;
|
||||
user-select: none;
|
||||
}
|
||||
.ticks.sending {
|
||||
color: #cbd5e1;
|
||||
}
|
||||
.ticks.sent {
|
||||
color: #64748b;
|
||||
}
|
||||
.ticks.read {
|
||||
color: #0d9488;
|
||||
}
|
||||
.composer {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
|
||||
Reference in New Issue
Block a user