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.
session-desktop/ts/types/Attachment.ts

121 lines
3.2 KiB
TypeScript

import is from '@sindresorhus/is';
import moment from 'moment';
import * as GoogleChrome from '../util/GoogleChrome';
import * as MIME from './MIME';
import { arrayBufferToObjectURL } from '../util/arrayBufferToObjectURL';
import { saveURLAsFile } from '../util/saveURLAsFile';
import { SignalService } from '../protobuf';
export type Attachment = {
fileName?: string | null;
flags?: SignalService.AttachmentPointer.Flags;
contentType?: MIME.MIMEType;
size?: number;
data: ArrayBuffer;
// // Omit unused / deprecated keys:
// schemaVersion?: number;
// id?: string;
// width?: number;
// height?: number;
// thumbnail?: ArrayBuffer;
// key?: ArrayBuffer;
// digest?: ArrayBuffer;
} & Partial<AttachmentSchemaVersion3>;
interface AttachmentSchemaVersion3 {
path: string;
}
export const isVisualMedia = (attachment: Attachment): boolean => {
const { contentType } = attachment;
if (is.undefined(contentType)) {
return false;
}
const isSupportedImageType = GoogleChrome.isImageTypeSupported(contentType);
const isSupportedVideoType = GoogleChrome.isVideoTypeSupported(contentType);
return isSupportedImageType || isSupportedVideoType;
};
export const isVoiceMessage = (attachment: Attachment): boolean => {
const flag = SignalService.AttachmentPointer.Flags.VOICE_MESSAGE;
const hasFlag =
// tslint:disable-next-line no-bitwise
!is.undefined(attachment.flags) && (attachment.flags & flag) === flag;
if (hasFlag) {
return true;
}
const isLegacyAndroidVoiceMessage =
!is.undefined(attachment.contentType) &&
MIME.isAudio(attachment.contentType) &&
attachment.fileName === null;
if (isLegacyAndroidVoiceMessage) {
return true;
}
return false;
};
export const save = ({
attachment,
document,
getAbsolutePath,
timestamp,
}: {
attachment: Attachment;
document: Document;
getAbsolutePath: (relativePath: string) => string;
timestamp?: number;
}): void => {
const isObjectURLRequired = is.undefined(attachment.path);
const url = !is.undefined(attachment.path)
? getAbsolutePath(attachment.path)
: arrayBufferToObjectURL({
data: attachment.data,
type: MIME.APPLICATION_OCTET_STREAM,
});
const filename = getSuggestedFilename({ attachment, timestamp });
saveURLAsFile({ url, filename, document });
if (isObjectURLRequired) {
URL.revokeObjectURL(url);
}
};
export const getSuggestedFilename = ({
attachment,
timestamp,
}: {
attachment: Attachment;
timestamp?: number | Date;
}): string => {
if (attachment.fileName) {
return attachment.fileName;
}
const prefix = 'signal-attachment';
const suffix = timestamp
? moment(timestamp).format('-YYYY-MM-DD-HHmmss')
: '';
const fileType = getFileExtension(attachment);
const extension = fileType ? `.${fileType}` : '';
return `${prefix}${suffix}${extension}`;
};
export const getFileExtension = (attachment: Attachment): string | null => {
if (!attachment.contentType) {
return null;
}
switch (attachment.contentType) {
case 'video/quicktime':
return 'mov';
default:
// TODO: Use better MIME --> file extension mapping:
return attachment.contentType.split('/')[1];
}
};