70 lines
2.6 KiB
TypeScript
70 lines
2.6 KiB
TypeScript
import { FormFileTooLargeError, prepareFormFieldFile } from '@/lib/form-file-compress';
|
|||
|
|
import { compressVoiceFile } from '@/lib/voice-note';
|
||
|
|
|
||
|
|
export const ACTIVITY_FILE_MAX_BYTES = 10 * 1024 * 1024;
|
||
|
|
|
||
|
|
const FILE_MIME_BY_EXT: Record<string, string> = {
|
||
|
|
pdf: 'application/pdf',
|
||
|
|
txt: 'text/plain',
|
||
|
|
csv: 'text/csv',
|
||
|
|
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',
|
||
|
|
zip: 'application/zip',
|
||
|
|
};
|
||
|
|
|
||
|
|
const IMAGE_EXT = new Set(['jpg', 'jpeg', 'png', 'gif', 'webp']);
|
||
|
|
const AUDIO_EXT = new Set(['webm', 'ogg', 'mp3', 'm4a', 'aac', 'wav', 'mpeg']);
|
||
|
|
|
||
|
|
export type PreparedAttachment = {
|
||
|
|
kind: 'image' | 'audio' | 'file';
|
||
|
|
name: string;
|
||
|
|
file: File;
|
||
|
|
previewUrl: string;
|
||
|
|
};
|
||
|
|
|
||
|
|
function extensionOf(name: string): string {
|
||
|
|
return name.split('.').pop()?.toLowerCase().replace(/[^a-z0-9]/g, '') ?? '';
|
||
|
|
}
|
||
|
|
|
||
|
|
function kindOf(file: File): 'image' | 'audio' | 'file' {
|
||
|
|
const type = file.type.toLowerCase().split(';')[0] ?? '';
|
||
|
|
const ext = extensionOf(file.name);
|
||
|
|
if (type.startsWith('image/') || IMAGE_EXT.has(ext)) return 'image';
|
||
|
|
if (type.startsWith('audio/') || AUDIO_EXT.has(ext)) return 'audio';
|
||
|
|
return 'file';
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function prepareActivityAttachment(file: File): Promise<PreparedAttachment> {
|
||
|
|
const kind = kindOf(file);
|
||
|
|
if (kind === 'image') {
|
||
|
|
try {
|
||
|
|
const prepared = await prepareFormFieldFile(file);
|
||
|
|
return { kind, name: prepared.name, file: prepared.file, previewUrl: prepared.dataUrl };
|
||
|
|
} catch (error) {
|
||
|
|
if (error instanceof FormFileTooLargeError) {
|
||
|
|
throw new Error('حجم عکس باید زیر ۲۰۰ کیلوبایت باشد');
|
||
|
|
}
|
||
|
|
throw new Error('فشردهسازی عکس ناموفق بود.');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (kind === 'audio') {
|
||
|
|
const compressed = await compressVoiceFile(file);
|
||
|
|
return {
|
||
|
|
kind,
|
||
|
|
name: compressed.name,
|
||
|
|
file: compressed,
|
||
|
|
previewUrl: URL.createObjectURL(compressed),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
const ext = extensionOf(file.name);
|
||
|
|
const mime = FILE_MIME_BY_EXT[ext];
|
||
|
|
if (!mime) throw new Error('این نوع فایل مجاز نیست.');
|
||
|
|
if (file.size > ACTIVITY_FILE_MAX_BYTES) throw new Error('حجم فایل باید زیر ۱۰ مگابایت باشد');
|
||
|
|
const named = file.type === mime ? file : new File([file], file.name, { type: mime });
|
||
|
|
return { kind: 'file', name: named.name, file: named, previewUrl: '' };
|
||
|
|
}
|