feat(activities): attach voice and files on activity notes
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
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: '' };
|
||||
}
|
||||
Reference in New Issue
Block a user