Aligning the chat bar with Telegram
This commit is contained in:
@@ -22,6 +22,8 @@ export type ChatMessage = {
|
||||
fileName?: string;
|
||||
uploadProgress?: number;
|
||||
localPreviewUrl?: string;
|
||||
edited?: boolean;
|
||||
deleted?: boolean;
|
||||
};
|
||||
|
||||
export type RoomLiveEvent = {
|
||||
@@ -299,6 +301,13 @@ function normalizeIncoming(
|
||||
const type = inferMediaType(declared, content, fileName);
|
||||
const isMedia = type !== 'text';
|
||||
const mediaUrl = isMedia ? buildChatFileUrl(content, downloadUrl) : undefined;
|
||||
const deleted = Boolean(
|
||||
base.deleted ||
|
||||
base.is_deleted ||
|
||||
base.isDeleted ||
|
||||
/^(?:\[?\s*deleted\s*\]?|این پیام حذف شد|پیام حذف شد)$/i.test(content),
|
||||
);
|
||||
const edited = Boolean(base.edited || base.is_edited || base.isEdited || base.edited_at);
|
||||
|
||||
return {
|
||||
id: id || `tmp-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
||||
@@ -306,16 +315,18 @@ function normalizeIncoming(
|
||||
senderId: String(
|
||||
base.sender_id ?? base.user_id ?? base.from_user_id ?? raw.sender_id ?? '',
|
||||
),
|
||||
content,
|
||||
type,
|
||||
content: deleted ? 'این پیام حذف شد' : content,
|
||||
type: deleted ? 'text' : type,
|
||||
createdAt: String(
|
||||
base.created_at ?? base.createdAt ?? raw.created_at ?? new Date().toISOString(),
|
||||
),
|
||||
reply: base.reply ? String(base.reply) : null,
|
||||
readAt: readAt ? String(readAt) : null,
|
||||
delivery: readAt ? 'read' : 'sent',
|
||||
mediaUrl,
|
||||
fileName,
|
||||
mediaUrl: deleted ? undefined : mediaUrl,
|
||||
fileName: deleted ? undefined : fileName,
|
||||
edited: deleted ? false : edited,
|
||||
deleted,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -340,6 +351,8 @@ function mergeMessages(incoming: ChatMessage, list: ChatMessage[]): ChatMessage[
|
||||
localPreviewUrl: incoming.localPreviewUrl ?? prev.localPreviewUrl,
|
||||
mediaUrl: incoming.mediaUrl || prev.mediaUrl,
|
||||
fileName: incoming.fileName || prev.fileName,
|
||||
edited: incoming.edited || prev.edited,
|
||||
deleted: incoming.deleted || prev.deleted,
|
||||
delivery:
|
||||
incoming.delivery === 'read' || prev.delivery === 'read'
|
||||
? 'read'
|
||||
@@ -379,6 +392,8 @@ function mergeMessages(incoming: ChatMessage, list: ChatMessage[]): ChatMessage[
|
||||
uploadProgress: undefined,
|
||||
mediaUrl: incoming.mediaUrl || prev.mediaUrl,
|
||||
fileName: incoming.fileName || prev.fileName,
|
||||
edited: incoming.edited || prev.edited,
|
||||
deleted: incoming.deleted || prev.deleted,
|
||||
delivery: incoming.readAt ? 'read' : 'sent',
|
||||
};
|
||||
if (prev.localPreviewUrl?.startsWith('blob:')) {
|
||||
@@ -496,6 +511,53 @@ function handleIncomingMessage(msg: ChatMessage) {
|
||||
});
|
||||
}
|
||||
|
||||
function applyEditedMessage(payload: Record<string, unknown>) {
|
||||
const base =
|
||||
payload.message && typeof payload.message === 'object'
|
||||
? (payload.message as Record<string, unknown>)
|
||||
: payload;
|
||||
const id = String(base.id ?? base.message_id ?? payload.message_id ?? '').trim();
|
||||
if (!id) return;
|
||||
const content = String(base.content ?? base.text ?? base.body ?? '').trim();
|
||||
if (!content) return;
|
||||
messages.value = messages.value.map((m) =>
|
||||
m.id === id
|
||||
? {
|
||||
...m,
|
||||
content,
|
||||
type: 'text',
|
||||
edited: true,
|
||||
deleted: false,
|
||||
mediaUrl: undefined,
|
||||
fileName: undefined,
|
||||
}
|
||||
: m,
|
||||
);
|
||||
}
|
||||
|
||||
function applyDeletedMessage(payload: Record<string, unknown>) {
|
||||
const base =
|
||||
payload.message && typeof payload.message === 'object'
|
||||
? (payload.message as Record<string, unknown>)
|
||||
: payload;
|
||||
const id = String(base.id ?? base.message_id ?? payload.message_id ?? '').trim();
|
||||
if (!id) return;
|
||||
messages.value = messages.value.map((m) =>
|
||||
m.id === id
|
||||
? {
|
||||
...m,
|
||||
content: 'این پیام حذف شد',
|
||||
type: 'text',
|
||||
deleted: true,
|
||||
edited: false,
|
||||
mediaUrl: undefined,
|
||||
fileName: undefined,
|
||||
localPreviewUrl: undefined,
|
||||
}
|
||||
: m,
|
||||
);
|
||||
}
|
||||
|
||||
function onSocketMessage(event: MessageEvent) {
|
||||
const raw = String(event.data ?? '').trim();
|
||||
if (!raw || raw === 'pong' || raw === 'ping') return;
|
||||
@@ -507,6 +569,28 @@ function onSocketMessage(event: MessageEvent) {
|
||||
applySeenPayload(parsed);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
type === 'edit' ||
|
||||
type === 'edited' ||
|
||||
type === 'update' ||
|
||||
type === 'message_edit' ||
|
||||
type === 'message:edit' ||
|
||||
type === 'message_edited'
|
||||
) {
|
||||
applyEditedMessage(parsed);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
type === 'delete' ||
|
||||
type === 'deleted' ||
|
||||
type === 'remove' ||
|
||||
type === 'message_delete' ||
|
||||
type === 'message:delete' ||
|
||||
type === 'message_deleted'
|
||||
) {
|
||||
applyDeletedMessage(parsed);
|
||||
return;
|
||||
}
|
||||
const msg = normalizeIncoming(parsed, activeRoomId.value);
|
||||
if (!msg) return;
|
||||
handleIncomingMessage(msg);
|
||||
@@ -916,6 +1000,66 @@ export function useOrgChat() {
|
||||
}
|
||||
}
|
||||
|
||||
async function editMessage(roomId: string, messageId: string, text: string) {
|
||||
const trimmed = text.trim();
|
||||
if (!trimmed || !messageId) return;
|
||||
await connectSocket();
|
||||
if (!socket || socket.readyState !== WebSocket.OPEN) throw new Error('ws not open');
|
||||
|
||||
messages.value = messages.value.map((m) =>
|
||||
m.id === messageId
|
||||
? {
|
||||
...m,
|
||||
content: trimmed,
|
||||
type: 'text',
|
||||
edited: true,
|
||||
deleted: false,
|
||||
mediaUrl: undefined,
|
||||
fileName: undefined,
|
||||
}
|
||||
: m,
|
||||
);
|
||||
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
type: 'edit',
|
||||
id: messageId,
|
||||
room_id: roomId,
|
||||
content: trimmed,
|
||||
}),
|
||||
);
|
||||
|
||||
emitRoomLive({
|
||||
roomId,
|
||||
preview: trimmed,
|
||||
at: new Date().toISOString(),
|
||||
fromSelf: true,
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteMessage(roomId: string, messageId: string) {
|
||||
if (!messageId) return;
|
||||
await connectSocket();
|
||||
if (!socket || socket.readyState !== WebSocket.OPEN) throw new Error('ws not open');
|
||||
|
||||
applyDeletedMessage({ id: messageId });
|
||||
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
type: 'delete',
|
||||
id: messageId,
|
||||
room_id: roomId,
|
||||
}),
|
||||
);
|
||||
|
||||
emitRoomLive({
|
||||
roomId,
|
||||
preview: 'این پیام حذف شد',
|
||||
at: new Date().toISOString(),
|
||||
fromSelf: true,
|
||||
});
|
||||
}
|
||||
|
||||
function clearActiveRoom() {
|
||||
activeRoomId.value = '';
|
||||
messages.value = [];
|
||||
@@ -937,6 +1081,8 @@ export function useOrgChat() {
|
||||
loadMessages,
|
||||
sendText,
|
||||
sendFile,
|
||||
editMessage,
|
||||
deleteMessage,
|
||||
sendRead,
|
||||
ensureConnected,
|
||||
setConversationsPageOpen,
|
||||
|
||||
Reference in New Issue
Block a user