import { StagedAttachmentType } from '../components/session/conversation/SessionCompositionBox'; import { SignalService } from '../protobuf'; import { Constants } from '../session'; import loadImage from 'blueimp-load-image'; export interface MaxScaleSize { maxSize?: number; maxHeight?: number; maxWidth?: number; } export async function autoScale( attachment: T, maxMeasurements?: MaxScaleSize ): Promise { const { contentType, file } = attachment; if (contentType.split('/')[0] !== 'image' || contentType === 'image/tiff') { // nothing to do return Promise.resolve(attachment); } return new Promise((resolve, reject) => { const url = URL.createObjectURL(file); const img = document.createElement('img'); img.onerror = reject; img.onload = () => { URL.revokeObjectURL(url); const maxSize = maxMeasurements?.maxSize || Constants.CONVERSATION.MAX_ATTACHMENT_FILESIZE_BYTES; const maxHeight = maxMeasurements?.maxHeight || 4096; const maxWidth = maxMeasurements?.maxWidth || 4096; if ( img.naturalWidth <= maxWidth && img.naturalHeight <= maxHeight && file.size <= maxSize ) { resolve(attachment); return; } if (file.type === 'image/gif' && file.size <= Constants.CONVERSATION.MAX_ATTACHMENT_FILESIZE_BYTES) { resolve(attachment); return; } if (file.type === 'image/gif') { reject(new Error('GIF is too large')); return; } const canvas = (loadImage as any).scale(img, { canvas: true, maxWidth, maxHeight, }); let quality = 0.95; let i = 4; let blob; do { i -= 1; blob = window.dataURLToBlobSync( canvas.toDataURL('image/jpeg', quality) ); quality = (quality * maxSize) / blob.size; if (quality > 1) { quality = 0.95; } } while (i > 0 && blob.size > maxSize); resolve({ ...attachment, file: blob, }); }; img.src = url; }); } export async function getFile( attachment: StagedAttachmentType, maxMeasurements?: MaxScaleSize ) { if (!attachment) { return Promise.resolve(); } const attachmentFlags = attachment.isVoiceMessage ? SignalService.AttachmentPointer.Flags.VOICE_MESSAGE : null; const scaled = await autoScale(attachment, maxMeasurements); const fileRead = await readFile(scaled); return { ...fileRead, url: undefined, flags: attachmentFlags || null, }; } export async function readFile(attachment: any): Promise { return new Promise((resolve, reject) => { const FR = new FileReader(); FR.onload = e => { const data = e?.target?.result as ArrayBuffer; resolve({ ...attachment, data, size: data.byteLength, }); }; FR.onerror = reject; FR.onabort = reject; FR.readAsArrayBuffer(attachment.file); }); }