Files
IdP/apps/frontend/lib/chat-media.ts
2026-06-24 14:37:15 +03:00

116 lines
3.8 KiB
TypeScript

export type ChatAttachmentKind = 'IMAGE' | 'AUDIO' | 'VOICE' | 'FILE';
const EXTENSION_TO_MIME: Record<string, string> = {
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
webp: 'image/webp',
gif: 'image/gif',
heic: 'image/heic',
heif: 'image/heif',
mp3: 'audio/mpeg',
m4a: 'audio/mp4',
ogg: 'audio/ogg',
opus: 'audio/ogg',
webm: 'audio/webm',
wav: 'audio/wav',
aac: 'audio/aac',
flac: 'audio/flac',
mp4: 'video/mp4',
mov: 'video/quicktime',
pdf: 'application/pdf',
doc: 'application/msword',
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
xls: 'application/vnd.ms-excel',
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
ppt: 'application/vnd.ms-powerpoint',
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
txt: 'text/plain',
csv: 'text/csv',
zip: 'application/zip',
rar: 'application/vnd.rar',
'7z': 'application/x-7z-compressed',
json: 'application/json',
xml: 'application/xml'
};
const MIME_ALIASES: Record<string, string> = {
'audio/x-m4a': 'audio/mp4',
'audio/x-aac': 'audio/aac',
'audio/x-wav': 'audio/wav',
'audio/x-flac': 'audio/flac',
'audio/x-mpeg': 'audio/mpeg',
'audio/mp3': 'audio/mpeg',
'image/jpg': 'image/jpeg',
'image/pjpeg': 'image/jpeg',
'application/x-pdf': 'application/pdf',
'application/x-zip-compressed': 'application/zip'
};
function extractFileExtension(value?: string | null) {
if (!value) return '';
const clean = value.split('?')[0]?.split('#')[0] ?? value;
const parts = clean.split('.');
if (parts.length < 2) return '';
return parts.pop()?.trim().toLowerCase() ?? '';
}
export function resolveChatContentType(file: Pick<File, 'type' | 'name'>) {
const raw = (file.type ?? '').trim().toLowerCase();
const base = raw.split(';')[0]?.trim() ?? '';
const aliased = MIME_ALIASES[base] ?? base;
if (aliased && aliased !== 'application/octet-stream') {
return aliased;
}
const extension = extractFileExtension(file.name);
if (extension && EXTENSION_TO_MIME[extension]) {
return EXTENSION_TO_MIME[extension];
}
return aliased || 'application/octet-stream';
}
export function inferChatMessageType(contentType: string, options?: { voice?: boolean }): ChatAttachmentKind {
const normalized = resolveChatContentType({ type: contentType, name: '' });
if (normalized.startsWith('image/')) return 'IMAGE';
if (normalized.startsWith('video/')) return 'FILE';
if (normalized.startsWith('audio/')) {
return options?.voice ? 'VOICE' : 'AUDIO';
}
return 'FILE';
}
export function detectChatMessageType(file: File, options?: { voice?: boolean }) {
const contentType = resolveChatContentType(file);
return inferChatMessageType(contentType, options);
}
export function formatFileSize(bytes?: number) {
if (!bytes || bytes <= 0) return '';
if (bytes < 1024) return `${bytes} Б`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} КБ`;
return `${(bytes / (1024 * 1024)).toFixed(1)} МБ`;
}
export function parseMessageMetadata(metadataJson?: string) {
if (!metadataJson) return {} as { fileName?: string; fileSize?: number; durationMs?: number };
try {
return JSON.parse(metadataJson) as { fileName?: string; fileSize?: number; durationMs?: number };
} catch {
return {};
}
}
export function fileIconLabel(fileName?: string, mimeType?: string) {
const extension = extractFileExtension(fileName);
if (extension === 'pdf' || mimeType === 'application/pdf') return 'PDF';
if (['doc', 'docx'].includes(extension)) return 'DOC';
if (['xls', 'xlsx', 'csv'].includes(extension)) return 'XLS';
if (['ppt', 'pptx'].includes(extension)) return 'PPT';
if (['zip', 'rar', '7z'].includes(extension)) return 'ZIP';
if (extension) return extension.toUpperCase().slice(0, 4);
return 'FILE';
}