
Biggest changes forced by this: alt tags for all images, resulting in new strings added to messages.json, and a new i18n paramter/prop added in a plot of places. Another change of note is that there are two new tslint.json files under ts/test and ts/styleguide to relax our rules a bit there. This required a change to our package.json script, as manually specifying the config file there made it ignore our tslint.json files in subdirectories
140 lines
3.3 KiB
TypeScript
140 lines
3.3 KiB
TypeScript
import is from '@sindresorhus/is';
|
|
import moment from 'moment';
|
|
|
|
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;
|
|
}
|
|
|
|
if (isVoiceMessage(attachment)) {
|
|
return false;
|
|
}
|
|
|
|
return MIME.isImage(contentType) || MIME.isVideo(contentType);
|
|
};
|
|
|
|
export const isFile = (attachment: Attachment): boolean => {
|
|
const { contentType } = attachment;
|
|
|
|
if (is.undefined(contentType)) {
|
|
return false;
|
|
}
|
|
|
|
if (isVisualMedia(attachment)) {
|
|
return false;
|
|
}
|
|
|
|
if (isVoiceMessage(attachment)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
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:
|
|
return attachment.contentType.split('/')[1];
|
|
}
|
|
};
|