You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			122 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			122 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			TypeScript
		
	
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<T extends { contentType: string; file: any }>(
 | 
						|
  attachment: T,
 | 
						|
  maxMeasurements?: MaxScaleSize
 | 
						|
): Promise<T> {
 | 
						|
  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<object> {
 | 
						|
  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);
 | 
						|
  });
 | 
						|
}
 |