update and fix messanger

This commit is contained in:
lendry
2026-06-26 00:16:17 +03:00
parent b0ea87e898
commit 4e78a81eb1
14 changed files with 898 additions and 125 deletions

View File

@@ -0,0 +1,12 @@
export const AVATAR_UPDATED_EVENT = 'id:avatar-updated';
export const CHAT_ROOM_AVATAR_UPDATED_EVENT = 'id:chat-room-avatar-updated';
export function dispatchAvatarUpdated(userId: string) {
if (typeof window === 'undefined') return;
window.dispatchEvent(new CustomEvent(AVATAR_UPDATED_EVENT, { detail: { userId } }));
}
export function dispatchChatRoomAvatarUpdated(roomId: string) {
if (typeof window === 'undefined') return;
window.dispatchEvent(new CustomEvent(CHAT_ROOM_AVATAR_UPDATED_EVENT, { detail: { roomId } }));
}

View File

@@ -0,0 +1,33 @@
export function getChatDateKey(value: string | Date) {
const date = typeof value === 'string' ? new Date(value) : value;
return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
}
export function formatChatDateLabel(value: string, now = new Date()) {
const date = new Date(value);
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const target = new Date(date.getFullYear(), date.getMonth(), date.getDate());
const diffDays = Math.round((today.getTime() - target.getTime()) / 86_400_000);
if (diffDays === 0) return 'Сегодня';
if (diffDays === 1) return 'Вчера';
return new Intl.DateTimeFormat('ru-RU', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
}).format(date);
}
export function shouldShowChatDateSeparator(currentCreatedAt: string, previousCreatedAt?: string) {
if (!previousCreatedAt) return true;
return getChatDateKey(currentCreatedAt) !== getChatDateKey(previousCreatedAt);
}
export function toDateInputValue(value: string) {
const date = new Date(value);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}

View File

@@ -0,0 +1,43 @@
import { isEmojiId } from '@/lib/emoji-catalog';
import { EMOJI_NATIVE_MAP, emojiIdToNative, resolveNativeEmojiContent } from '@/lib/emoji-native-map';
function countGraphemes(text: string) {
if (typeof Intl !== 'undefined' && 'Segmenter' in Intl) {
return [...new Intl.Segmenter('ru', { granularity: 'grapheme' }).segment(text)].length;
}
return [...text].length;
}
function resolveEmojiDisplayText(content?: string | null, visibleText?: string | null) {
const raw = (visibleText ?? content ?? '').trim();
if (!raw) return '';
if (isEmojiId(raw)) return emojiIdToNative(raw) ?? raw;
if (EMOJI_NATIVE_MAP[raw]) return EMOJI_NATIVE_MAP[raw]!;
return resolveNativeEmojiContent(raw).trim();
}
export function isSingleEmojiMessage(options: {
type?: string;
content?: string | null;
visibleText?: string | null;
}) {
if (options.type === 'POLL' || options.type === 'IMAGE' || options.type === 'VOICE' || options.type === 'AUDIO' || options.type === 'FILE') {
return false;
}
const text = resolveEmojiDisplayText(options.content, options.visibleText);
if (!text) return false;
if (countGraphemes(text) !== 1) return false;
return /\p{Extended_Pictographic}/u.test(text);
}
export function getSingleEmojiNative(options: {
type?: string;
content?: string | null;
visibleText?: string | null;
}) {
if (options.type === 'EMOJI') {
return resolveEmojiDisplayText(options.content, options.visibleText);
}
return resolveEmojiDisplayText(options.content, options.visibleText);
}