/**
* AttachmentPreview — рендер `Message.attachment` внутри bubble'а.
*
* Четыре формы:
* - image → Image с object-fit cover, aspect-ratio из width/height
* - video → то же + play-overlay в центре, duration внизу-справа
* - voice → row [play-icon] [waveform stub] [duration]
* - file → row [file-icon] [name + size]
*
* Вложения размещаются ВНУТРИ того же bubble'а что и текст, чуть ниже
* footer'а нет и ширина bubble'а снимает maxWidth-ограничение ради
* изображений (отдельный media-first-bubble case).
*/
import React from 'react';
import { View, Text, Image } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import type { Attachment } from '@/lib/types';
import { VoicePlayer } from '@/components/chat/VoicePlayer';
import { VideoCirclePlayer } from '@/components/chat/VideoCirclePlayer';
export interface AttachmentPreviewProps {
attachment: Attachment;
/** Используется для тонирования footer-элементов. */
own?: boolean;
}
function formatSize(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
if (bytes < 1024 * 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
}
function formatDuration(seconds: number): string {
const m = Math.floor(seconds / 60);
const s = Math.floor(seconds % 60);
return `${m}:${String(s).padStart(2, '0')}`;
}
export function AttachmentPreview({ attachment, own }: AttachmentPreviewProps) {
switch (attachment.kind) {
case 'image':
return ;
case 'video':
// circle=true — круглое видео-сообщение (Telegram-стиль).
return attachment.circle
?
: ;
case 'voice':
return ;
case 'file':
return ;
}
}
// ─── Image ──────────────────────────────────────────────────────────
function ImageAttachment({ att }: { att: Attachment }) {
// Aspect-ratio из реальных width/height; fallback 4:3.
const aspect = att.width && att.height ? att.width / att.height : 4 / 3;
return (
);
}
// ─── Video ──────────────────────────────────────────────────────────
function VideoAttachment({ att }: { att: Attachment }) {
const aspect = att.width && att.height ? att.width / att.height : 16 / 9;
return (
{/* Play overlay по центру */}
{att.duration !== undefined && (
{formatDuration(att.duration)}
)}
);
}
// ─── Voice ──────────────────────────────────────────────────────────
// Реальный плеер — см. components/chat/VoicePlayer.tsx (expo-av Sound).
// ─── File ───────────────────────────────────────────────────────────
function FileAttachment({ att, own }: { att: Attachment; own?: boolean }) {
return (
{att.name ?? 'file'}
{att.size !== undefined ? formatSize(att.size) : ''}
{att.size !== undefined && att.mime ? ' · ' : ''}
{att.mime ?? ''}
);
}