Implement Attachment.save
This commit is contained in:
parent
3727205606
commit
4a5e61eaf4
2 changed files with 116 additions and 0 deletions
60
ts/test/types/Attachment_test.ts
Normal file
60
ts/test/types/Attachment_test.ts
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
*/
|
||||||
|
import 'mocha';
|
||||||
|
import { assert } from 'chai';
|
||||||
|
|
||||||
|
import * as Attachment from '../../types/Attachment';
|
||||||
|
import { MIMEType } from '../../types/MIME';
|
||||||
|
// @ts-ignore
|
||||||
|
import { stringToArrayBuffer } from '../../../js/modules/string_to_array_buffer';
|
||||||
|
|
||||||
|
describe('Attachment', () => {
|
||||||
|
describe('getFileExtension', () => {
|
||||||
|
it('should return file extension from content type', () => {
|
||||||
|
const input: Attachment.Attachment = {
|
||||||
|
data: stringToArrayBuffer('foo'),
|
||||||
|
contentType: 'image/gif' as MIMEType,
|
||||||
|
};
|
||||||
|
assert.strictEqual(Attachment.getFileExtension(input), 'gif');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return file extension for QuickTime videos', () => {
|
||||||
|
const input: Attachment.Attachment = {
|
||||||
|
data: stringToArrayBuffer('foo'),
|
||||||
|
contentType: 'video/quicktime' as MIMEType,
|
||||||
|
};
|
||||||
|
assert.strictEqual(Attachment.getFileExtension(input), 'mov');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSuggestedFilename', () => {
|
||||||
|
context('for attachment with filename', () => {
|
||||||
|
it('should return existing filename if present', () => {
|
||||||
|
const attachment: Attachment.Attachment = {
|
||||||
|
fileName: 'funny-cat.mov',
|
||||||
|
data: stringToArrayBuffer('foo'),
|
||||||
|
contentType: 'video/quicktime' as MIMEType,
|
||||||
|
};
|
||||||
|
const actual = Attachment.getSuggestedFilename({ attachment });
|
||||||
|
const expected = 'funny-cat.mov';
|
||||||
|
assert.strictEqual(actual, expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
context('for attachment without filename', () => {
|
||||||
|
it('should generate a filename based on timestamp', () => {
|
||||||
|
const attachment: Attachment.Attachment = {
|
||||||
|
data: stringToArrayBuffer('foo'),
|
||||||
|
contentType: 'video/quicktime' as MIMEType,
|
||||||
|
};
|
||||||
|
const timestamp = new Date(new Date(0).getTimezoneOffset() * 60 * 1000);
|
||||||
|
const actual = Attachment.getSuggestedFilename({
|
||||||
|
attachment,
|
||||||
|
timestamp,
|
||||||
|
});
|
||||||
|
const expected = 'signal-attachment-1970-01-01-000000.mov';
|
||||||
|
assert.strictEqual(actual, expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -2,8 +2,10 @@
|
||||||
* @prettier
|
* @prettier
|
||||||
*/
|
*/
|
||||||
import is from '@sindresorhus/is';
|
import is from '@sindresorhus/is';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
import * as GoogleChrome from '../util/GoogleChrome';
|
import * as GoogleChrome from '../util/GoogleChrome';
|
||||||
|
import { arrayBufferToObjectURL } from '../util/arrayBufferToObjectURL';
|
||||||
import { MIMEType } from './MIME';
|
import { MIMEType } from './MIME';
|
||||||
|
|
||||||
export interface Attachment {
|
export interface Attachment {
|
||||||
|
@ -23,6 +25,8 @@ export interface Attachment {
|
||||||
// flags?: number;
|
// flags?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SAVE_CONTENT_TYPE = 'application/octet-stream' as MIMEType;
|
||||||
|
|
||||||
export const isVisualMedia = (attachment: Attachment): boolean => {
|
export const isVisualMedia = (attachment: Attachment): boolean => {
|
||||||
const { contentType } = attachment;
|
const { contentType } = attachment;
|
||||||
|
|
||||||
|
@ -34,3 +38,55 @@ export const isVisualMedia = (attachment: Attachment): boolean => {
|
||||||
const isSupportedVideoType = GoogleChrome.isVideoTypeSupported(contentType);
|
const isSupportedVideoType = GoogleChrome.isVideoTypeSupported(contentType);
|
||||||
return isSupportedImageType || isSupportedVideoType;
|
return isSupportedImageType || isSupportedVideoType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const save = ({
|
||||||
|
attachment,
|
||||||
|
timestamp,
|
||||||
|
}: {
|
||||||
|
attachment: Attachment;
|
||||||
|
timestamp?: number;
|
||||||
|
}): void => {
|
||||||
|
const url = arrayBufferToObjectURL({
|
||||||
|
data: attachment.data,
|
||||||
|
type: SAVE_CONTENT_TYPE,
|
||||||
|
});
|
||||||
|
const anchorElement = document.createElement('a');
|
||||||
|
anchorElement.href = url;
|
||||||
|
anchorElement.download = getSuggestedFilename({ attachment, timestamp });
|
||||||
|
anchorElement.click();
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue