diff --git a/js/modules/types/message.js b/js/modules/types/message.js index defefb4f373..bb03f0d11bb 100644 --- a/js/modules/types/message.js +++ b/js/modules/types/message.js @@ -25,13 +25,21 @@ const PRIVATE = 'private'; // - Attachments: Write attachment data to disk and store relative path to it. // Version 4 // - Quotes: Write thumbnail data to disk and store relative path to it. -// Version 5 +// Version 5 (deprecated) // - Attachments: Track number and kind of attachments for media gallery // - `hasAttachments?: 1 | 0` // - `hasVisualMediaAttachments?: 1 | undefined` (for media gallery ‘Media’ view) // - `hasFileAttachments?: 1 | undefined` (for media gallery ‘Documents’ view) +// - IMPORTANT: Version 7 changes the classification of visual media and files. +// Therefore version 5 is considered deprecated. For an easier implementation, +// new files have the same classification in version 5 as in version 7. // Version 6 // - Contact: Write contact avatar to disk, ensure contact data is well-formed +// Version 7 (supersedes attachment classification in version 5) +// - Attachments: Update classification for: +// - `hasVisualMediaAttachments`: Include all images and video regardless of +// whether Chromium can render it or not. +// - `hasFileAttachments`: Exclude voice messages. const INITIAL_SCHEMA_VERSION = 0; @@ -228,6 +236,10 @@ const toVersion6 = exports._withSchemaVersion( Contact.parseAndWriteAvatar(Attachment.migrateDataToFileSystem) ) ); +// IMPORTANT: We’ve updated our definition of `initializeAttachmentMetadata`, so +// we need to run it again on existing items that have previously been incorrectly +// classified: +const toVersion7 = exports._withSchemaVersion(7, initializeAttachmentMetadata); const VERSIONS = [ toVersion0, @@ -236,6 +248,8 @@ const VERSIONS = [ toVersion3, toVersion4, toVersion5, + toVersion6, + toVersion7, ]; exports.CURRENT_SCHEMA_VERSION = VERSIONS.length - 1; diff --git a/test/modules/types/message_test.js b/test/modules/types/message_test.js index 2343bd803a6..61535c569c7 100644 --- a/test/modules/types/message_test.js +++ b/test/modules/types/message_test.js @@ -2,6 +2,7 @@ const { assert } = require('chai'); const sinon = require('sinon'); const Message = require('../../../js/modules/types/message'); +const { SignalService } = require('../../../ts/protobuf'); const { stringToArrayBuffer, } = require('../../../js/modules/string_to_array_buffer'); @@ -242,7 +243,8 @@ describe('Message', () => { const input = { attachments: [ { - contentType: 'application/json', + contentType: 'audio/aac', + flags: SignalService.AttachmentPointer.Flags.VOICE_MESSAGE, data: stringToArrayBuffer('It’s easy if you try'), fileName: 'test\u202Dfig.exe', size: 1111, @@ -253,7 +255,8 @@ describe('Message', () => { const expected = { attachments: [ { - contentType: 'application/json', + contentType: 'audio/aac', + flags: 1, path: 'abc/abcdefg', fileName: 'test\uFFFDfig.exe', size: 1111, @@ -261,7 +264,7 @@ describe('Message', () => { ], hasAttachments: 1, hasVisualMediaAttachments: undefined, - hasFileAttachments: 1, + hasFileAttachments: undefined, schemaVersion: Message.CURRENT_SCHEMA_VERSION, contact: [], }; diff --git a/ts/test/types/message/initializeAttachmentMetadata_test.ts b/ts/test/types/message/initializeAttachmentMetadata_test.ts index 9c87215f3bd..16ba687e3a0 100644 --- a/ts/test/types/message/initializeAttachmentMetadata_test.ts +++ b/ts/test/types/message/initializeAttachmentMetadata_test.ts @@ -3,13 +3,14 @@ import { assert } from 'chai'; import * as Message from '../../../../ts/types/message/initializeAttachmentMetadata'; import { IncomingMessage } from '../../../../ts/types/Message'; +import { SignalService } from '../../../../ts/protobuf'; import * as MIME from '../../../../ts/types/MIME'; // @ts-ignore import { stringToArrayBuffer } from '../../../../js/modules/string_to_array_buffer'; describe('Message', () => { describe('initializeAttachmentMetadata', () => { - it('should handle visual media attachments', async () => { + it('should classify visual media attachments', async () => { const input: IncomingMessage = { type: 'incoming', conversationId: 'foo', @@ -49,5 +50,89 @@ describe('Message', () => { const actual = await Message.initializeAttachmentMetadata(input); assert.deepEqual(actual, expected); }); + + it('should classify file attachments', async () => { + const input: IncomingMessage = { + type: 'incoming', + conversationId: 'foo', + id: '11111111-1111-1111-1111-111111111111', + timestamp: 1523317140899, + received_at: 1523317140899, + sent_at: 1523317140800, + attachments: [ + { + contentType: MIME.APPLICATION_OCTET_STREAM, + data: stringToArrayBuffer('foo'), + fileName: 'foo.bin', + size: 1111, + }, + ], + }; + const expected: IncomingMessage = { + type: 'incoming', + conversationId: 'foo', + id: '11111111-1111-1111-1111-111111111111', + timestamp: 1523317140899, + received_at: 1523317140899, + sent_at: 1523317140800, + attachments: [ + { + contentType: MIME.APPLICATION_OCTET_STREAM, + data: stringToArrayBuffer('foo'), + fileName: 'foo.bin', + size: 1111, + }, + ], + hasAttachments: 1, + hasVisualMediaAttachments: undefined, + hasFileAttachments: 1, + }; + + const actual = await Message.initializeAttachmentMetadata(input); + assert.deepEqual(actual, expected); + }); + + it('should classify voice message attachments', async () => { + const input: IncomingMessage = { + type: 'incoming', + conversationId: 'foo', + id: '11111111-1111-1111-1111-111111111111', + timestamp: 1523317140899, + received_at: 1523317140899, + sent_at: 1523317140800, + attachments: [ + { + contentType: MIME.AUDIO_AAC, + flags: SignalService.AttachmentPointer.Flags.VOICE_MESSAGE, + data: stringToArrayBuffer('foo'), + fileName: 'Voice Message.aac', + size: 1111, + }, + ], + }; + const expected: IncomingMessage = { + type: 'incoming', + conversationId: 'foo', + id: '11111111-1111-1111-1111-111111111111', + timestamp: 1523317140899, + received_at: 1523317140899, + sent_at: 1523317140800, + attachments: [ + { + contentType: MIME.AUDIO_AAC, + flags: SignalService.AttachmentPointer.Flags.VOICE_MESSAGE, + data: stringToArrayBuffer('foo'), + fileName: 'Voice Message.aac', + size: 1111, + }, + ], + hasAttachments: 1, + hasVisualMediaAttachments: undefined, + hasFileAttachments: undefined, + }; + + const actual = await Message.initializeAttachmentMetadata(input); + assert.deepEqual(actual, expected); + }); }); }); diff --git a/ts/types/message/initializeAttachmentMetadata.ts b/ts/types/message/initializeAttachmentMetadata.ts index 317dacb482f..3ab02722e4b 100644 --- a/ts/types/message/initializeAttachmentMetadata.ts +++ b/ts/types/message/initializeAttachmentMetadata.ts @@ -1,8 +1,14 @@ -import { partition } from 'lodash'; - import * as Attachment from '../Attachment'; import * as IndexedDB from '../IndexedDB'; -import { Message } from '../Message'; +import { Message, UserMessage } from '../Message'; + +const hasAttachment = ( + predicate: (value: Attachment.Attachment) => boolean +) => (message: UserMessage): IndexedDB.IndexablePresence => + IndexedDB.toIndexablePresence(message.attachments.some(predicate)); + +const hasFileAttachment = hasAttachment(Attachment.isFile); +const hasVisualMediaAttachment = hasAttachment(Attachment.isVisualMedia); export const initializeAttachmentMetadata = async ( message: Message @@ -14,17 +20,14 @@ export const initializeAttachmentMetadata = async ( const hasAttachments = IndexedDB.toIndexableBoolean( message.attachments.length > 0 ); - const [hasVisualMediaAttachments, hasFileAttachments] = partition( - message.attachments, - Attachment.isVisualMedia - ) - .map(attachments => attachments.length > 0) - .map(IndexedDB.toIndexablePresence); + + const hasFileAttachments = hasFileAttachment(message); + const hasVisualMediaAttachments = hasVisualMediaAttachment(message); return { ...message, hasAttachments, - hasVisualMediaAttachments, hasFileAttachments, + hasVisualMediaAttachments, }; };