fix and update

This commit is contained in:
lendry
2026-07-01 00:06:36 +03:00
parent deb213bd77
commit a0c1722a8d
6 changed files with 188 additions and 17 deletions

View File

@@ -47,6 +47,7 @@ import {
deleteChatRoom,
editChatMessage,
fetchBotChatMessages,
fetchChatMediaUrl,
fetchChatMessages,
fetchChatRooms,
getApiErrorMessage,
@@ -67,6 +68,7 @@ import {
} from '@/lib/chat-message-utils';
import { detectChatMessageType, parseMessageMetadata, resolveChatContentType } from '@/lib/chat-media';
import { uploadChatMediaBatch } from '@/lib/chat-media-upload';
import { fetchMediaBlobWithRetry } from '@/lib/resilient-media-fetch';
import { getSingleEmojiNative, isSingleEmojiMessage } from '@/lib/chat-emoji-display';
import { shouldShowChatDateSeparator } from '@/lib/chat-date-utils';
import { encryptE2EBlob, e2eKindFromMessageType } from '@/lib/e2e-chat';
@@ -85,6 +87,74 @@ function formatMessageTime(value: string) {
return new Intl.DateTimeFormat('ru-RU', { hour: '2-digit', minute: '2-digit' }).format(new Date(value));
}
function MiniChatImageMessage({ message, token }: { message: ChatMessage; token: string }) {
const [src, setSrc] = useState<string | null>(null);
const [failed, setFailed] = useState(false);
const metadata = parseMessageMetadata(message.metadataJson) as { fileName?: string; width?: number; height?: number };
useEffect(() => {
if (!message.storageKey) return;
let cancelled = false;
let objectUrl: string | null = null;
setSrc(null);
setFailed(false);
async function loadImage() {
try {
const { accessUrl } = await fetchChatMediaUrl(message.roomId, message.storageKey!, token, metadata.fileName);
const blob = await fetchMediaBlobWithRetry(accessUrl, token, {
mimeType: message.mimeType,
label: 'фото чата'
});
if (cancelled) return;
objectUrl = URL.createObjectURL(blob);
setSrc(objectUrl);
} catch {
if (!cancelled) setFailed(true);
}
}
void loadImage();
return () => {
cancelled = true;
if (objectUrl) URL.revokeObjectURL(objectUrl);
};
}, [message.id, message.mimeType, message.roomId, message.storageKey, metadata.fileName, token]);
const ratio =
metadata.width && metadata.height
? Math.max(0.65, Math.min(1.35, metadata.height / metadata.width))
: 0.72;
if (failed) {
return (
<div className="flex min-h-[120px] w-[190px] flex-col items-center justify-center gap-2 rounded-2xl bg-white/55 text-xs text-[#667085]">
<ImageIcon className="h-5 w-5" />
<span>Фото недоступно</span>
</div>
);
}
if (!src) {
return (
<div className="flex w-[190px] items-center justify-center rounded-2xl bg-white/55" style={{ height: Math.round(190 * ratio) }}>
<Loader2 className="h-5 w-5 animate-spin text-[#8a94a6]" />
</div>
);
}
return (
<img
src={src}
alt={message.content?.trim() || metadata.fileName || 'Фото в чате'}
className="max-h-[260px] w-[190px] rounded-2xl object-cover shadow-sm"
style={{ aspectRatio: metadata.width && metadata.height ? `${metadata.width} / ${metadata.height}` : undefined }}
/>
);
}
export function MiniFamilyChat() {
const { user, token, isPinLocked, isContentReady } = useAuth();
const { showToast } = useToast();
@@ -1103,6 +1173,15 @@ export function MiniFamilyChat() {
);
})}
</div>
) : message.storageKey && message.type === 'IMAGE' && token ? (
<div className="space-y-1">
<MiniChatImageMessage message={message} token={token} />
{visibleText?.trim() ? (
<div className="whitespace-pre-wrap break-words text-sm leading-relaxed">
<ChatEmojiContent content={visibleText} size={18} />
</div>
) : null}
</div>
) : message.storageKey ? (
<p className="text-sm">{getMessagePreviewText(message, visibleText)}</p>
) : isLargeEmoji ? (