Правка уведомления

This commit is contained in:
Халимов Рустам
2026-04-20 22:48:25 +03:00
parent 72325f48e5
commit 7eea8ff6d1
3 changed files with 67 additions and 61 deletions
+3 -1
View File
@@ -194,7 +194,8 @@ const translations = {
clearChatConfirm: 'Очистить историю чата для себя? Собеседник сохранит свою историю.', clearChatConfirm: 'Очистить историю чата для себя? Собеседник сохранит свою историю.',
clearHistory: 'Очистить историю', clearHistory: 'Очистить историю',
clearHistoryConfirm: 'Очистить историю?', clearHistoryConfirm: 'Очистить историю?',
deleteChatConfirm: 'Удалить чат? Это действие нельзя отменить.', deleteChatConfirm: 'Удалить чат? Это действие нельзя отменить. Чат будет удалён у всех участников.',
deleteGroupChatConfirm: 'Удалить чат? Это действие нельзя отменить. Чат будет удалён у всех участников.',
pinChat: 'Закрепить чат', pinChat: 'Закрепить чат',
unpinChat: 'Открепить чат', unpinChat: 'Открепить чат',
chatCleared: 'Очищено', chatCleared: 'Очищено',
@@ -575,6 +576,7 @@ const translations = {
clearHistory: 'Clear history', clearHistory: 'Clear history',
clearHistoryConfirm: 'Clear history?', clearHistoryConfirm: 'Clear history?',
deleteChatConfirm: 'Delete this chat? This action cannot be undone.', deleteChatConfirm: 'Delete this chat? This action cannot be undone.',
deleteGroupChatConfirm: 'Delete this chat? This action cannot be undone. The chat will be removed for all participants.',
pinChat: 'Pin chat', pinChat: 'Pin chat',
unpinChat: 'Unpin chat', unpinChat: 'Unpin chat',
chatCleared: 'Chat cleared', chatCleared: 'Chat cleared',
@@ -283,7 +283,11 @@ function ChatListItem({ chat, isActive }: ChatListItemProps) {
<ConfirmModal <ConfirmModal
open={showDeleteConfirm} open={showDeleteConfirm}
message={isFavorites ? t('clearHistoryConfirm') : t('deleteChatConfirm')} message={
isFavorites ? t('clearHistoryConfirm') :
chat.type === 'group' ? t('deleteGroupChatConfirm') :
t('deleteChatConfirm')
}
onConfirm={confirmDelete} onConfirm={confirmDelete}
onCancel={() => setShowDeleteConfirm(false)} onCancel={() => setShowDeleteConfirm(false)}
/> />
@@ -104,7 +104,7 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
// Wait a bit for React to render // Wait a bit for React to render
setTimeout(() => { setTimeout(() => {
if (!tryScroll()) { if (!tryScroll()) {
NotificationStore.useNotificationStore.getState().addNotification('warning', (t as any)('messageNotFound') || 'Message not found'); NotificationStore.useNotificationStore.getState().addNotification('warning', (t as any)('messageNotFound') || 'Message not found');
} }
}, 300); }, 300);
} else { } else {
@@ -137,25 +137,25 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
useEffect(() => { useEffect(() => {
if (!chat?.isImporting || !chat?.importJobId) { if (!chat?.isImporting || !chat?.importJobId) {
setImportStatus(null); setImportStatus(null);
return; return;
} }
const poll = async () => { const poll = async () => {
try { try {
const data = await httpClient.request<any>(`/import/telegram/status/${chat.importJobId}`); const data = await httpClient.request<any>(`/import/telegram/status/${chat.importJobId}`);
setImportStatus({ processed: data.processedMessages, total: data.totalMessages, status: data.status }); setImportStatus({ processed: data.processedMessages, total: data.totalMessages, status: data.status });
if (data.status === 'Completed' || data.status === 'Failed') { if (data.status === 'Completed' || data.status === 'Failed') {
setImportStatus(null); setImportStatus(null);
loadChats(); loadChats();
}
} catch (e: any) {
if (e.status === 404) {
console.warn('Import job not found');
} else {
console.error('Failed to poll status', e);
}
} }
} catch (e: any) {
if (e.status === 404) {
console.warn('Import job not found');
} else {
console.error('Failed to poll status', e);
}
}
}; };
poll(); poll();
@@ -280,9 +280,9 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
isAtBottomRef.current = isAtBottomNow; isAtBottomRef.current = isAtBottomNow;
if (isAtBottomNow) { if (isAtBottomNow) {
localStorage.setItem(`chat_at_bottom_${chatId}`, 'true'); localStorage.setItem(`chat_at_bottom_${chatId}`, 'true');
localStorage.removeItem(`chat_anchor_${chatId}`); localStorage.removeItem(`chat_anchor_${chatId}`);
return; return;
} }
const messageElements = container.querySelectorAll('[data-message-id]'); const messageElements = container.querySelectorAll('[data-message-id]');
@@ -304,8 +304,8 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
} }
if (anchor && anchor.id) { if (anchor && anchor.id) {
localStorage.setItem(`chat_anchor_${chatId}`, JSON.stringify(anchor)); localStorage.setItem(`chat_anchor_${chatId}`, JSON.stringify(anchor));
localStorage.removeItem(`chat_at_bottom_${chatId}`); localStorage.removeItem(`chat_at_bottom_${chatId}`);
} }
}, [activeChat, scrollReady, chatMessages]); }, [activeChat, scrollReady, chatMessages]);
@@ -333,13 +333,13 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
// Поиск ближайшего, если точное сообщение еще не загружено // Поиск ближайшего, если точное сообщение еще не загружено
if (!el) { if (!el) {
const msgIndex = chatMessages.findIndex(m => m.id === id); const msgIndex = chatMessages.findIndex(m => m.id === id);
if (msgIndex !== -1) { if (msgIndex !== -1) {
for (let i = msgIndex; i < chatMessages.length; i++) { for (let i = msgIndex; i < chatMessages.length; i++) {
const nextEl = container.querySelector(`[data-message-id="${chatMessages[i].id}"]`) as HTMLElement; const nextEl = container.querySelector(`[data-message-id="${chatMessages[i].id}"]`) as HTMLElement;
if (nextEl) { el = nextEl; break; } if (nextEl) { el = nextEl; break; }
} }
} }
} }
if (el) { if (el) {
@@ -405,18 +405,18 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
// Сохраняем позицию старого чата // Сохраняем позицию старого чата
if (prevChatIdRef.current && scrollContainerRef.current && scrollReady) { if (prevChatIdRef.current && scrollContainerRef.current && scrollReady) {
saveScrollPosition(prevChatIdRef.current); saveScrollPosition(prevChatIdRef.current);
} }
setScrollReady(false); setScrollReady(false);
prevChatIdRef.current = activeChat; prevChatIdRef.current = activeChat;
if (scrollContainerRef.current) { if (scrollContainerRef.current) {
scrollContainerRef.current.scrollTop = 0; scrollContainerRef.current.scrollTop = 0;
} }
const timer = setTimeout(() => { const timer = setTimeout(() => {
isInitializingRef.current = false; isInitializingRef.current = false;
}, 600); }, 600);
return () => clearTimeout(timer); return () => clearTimeout(timer);
@@ -509,10 +509,10 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
isScrollingToBottomRef.current = true; isScrollingToBottomRef.current = true;
if (scrollContainerRef.current) { if (scrollContainerRef.current) {
scrollContainerRef.current.scrollTo({ scrollContainerRef.current.scrollTo({
top: scrollContainerRef.current.scrollHeight, top: scrollContainerRef.current.scrollHeight,
behavior: 'smooth' behavior: 'smooth'
}); });
} }
setTimeout(() => { setTimeout(() => {
@@ -646,7 +646,7 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
<section className="flex-1 h-full flex flex-col items-center justify-center bg-[#010101] relative overflow-hidden"> <section className="flex-1 h-full flex flex-col items-center justify-center bg-[#010101] relative overflow-hidden">
{/* Background Knot Texture */} {/* Background Knot Texture */}
<div className="absolute inset-0 opacity-[0.04] pointer-events-none flex items-center justify-center"> <div className="absolute inset-0 opacity-[0.04] pointer-events-none flex items-center justify-center">
<span className="material-symbols-outlined text-white text-[500px] select-none">cloud_download</span> <span className="material-symbols-outlined text-white text-[500px] select-none">cloud_download</span>
</div> </div>
{/* Chat Empty State View */} {/* Chat Empty State View */}
@@ -1060,7 +1060,7 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
setShowTopMenu(false); setShowTopMenu(false);
if (activeChat) { if (activeChat) {
setConfirmAction({ setConfirmAction({
message: t('deleteChatConfirm'), message: chat.type === 'group' ? t('deleteGroupChatConfirm') : t('deleteChatConfirm'),
action: async () => { action: async () => {
try { try {
await ChatApi.deleteChat(activeChat); await ChatApi.deleteChat(activeChat);
@@ -1153,7 +1153,7 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
<div className="flex items-center justify-between mb-2 px-1"> <div className="flex items-center justify-between mb-2 px-1">
<span className="text-xs font-black uppercase tracking-widest text-primary"> <span className="text-xs font-black uppercase tracking-widest text-primary">
{importStatus?.status === 'Processing' ? 'Обработка' : {importStatus?.status === 'Processing' ? 'Обработка' :
importStatus?.status === 'Queued' ? 'В очереди' : 'Загрузка'} importStatus?.status === 'Queued' ? 'В очереди' : 'Загрузка'}
</span> </span>
<span className="text-xs font-black text-on-surface tabular-nums"> <span className="text-xs font-black text-on-surface tabular-nums">
{importStatus?.processed || 0} / {importStatus?.total || 0} {importStatus?.processed || 0} / {importStatus?.total || 0}
@@ -1227,7 +1227,7 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
</p> </p>
<p className="text-sm text-zinc-300 truncate font-medium"> <p className="text-sm text-zinc-300 truncate font-medium">
{chatPinnedMessages[pinnedIndex % chatPinnedMessages.length]?.content || {chatPinnedMessages[pinnedIndex % chatPinnedMessages.length]?.content ||
(chatPinnedMessages[pinnedIndex % chatPinnedMessages.length]?.media?.length > 0 ? t('media') : '...')} (chatPinnedMessages[pinnedIndex % chatPinnedMessages.length]?.media?.length > 0 ? t('media') : '...')}
</p> </p>
</div> </div>
</button> </button>