+
```
diff --git a/ts/components/conversation/media-gallery/MediaGallery.md b/ts/components/conversation/media-gallery/MediaGallery.md
index ee65d6cda..6a41ff7e2 100644
--- a/ts/components/conversation/media-gallery/MediaGallery.md
+++ b/ts/components/conversation/media-gallery/MediaGallery.md
@@ -74,3 +74,14 @@ const messages = _.sortBy(
;
```
+
+## Media gallery with one document
+
+```jsx
+const messages = [
+ {
+ attachments: [{ fileName: 'foo.jpg', contentType: 'application/json' }],
+ },
+];
+
;
+```
diff --git a/ts/components/styles/Colors.ts b/ts/components/styles/Colors.ts
index bcc7f814f..1730f9e81 100644
--- a/ts/components/styles/Colors.ts
+++ b/ts/components/styles/Colors.ts
@@ -2,3 +2,4 @@
* @prettier
*/
export const TEXT_SECONDARY = '#bbb';
+export const ICON_SECONDARY = '#ccc';
diff --git a/ts/styles/colorSVG.ts b/ts/styles/colorSVG.ts
new file mode 100644
index 000000000..a749e5fdc
--- /dev/null
+++ b/ts/styles/colorSVG.ts
@@ -0,0 +1,7 @@
+export const colorSVG = (url: string, color: string) => {
+ return {
+ WebkitMask: `url(${url}) no-repeat center`,
+ WebkitMaskSize: '100%',
+ backgroundColor: color,
+ };
+};
diff --git a/ts/test/types/Attachment_test.ts b/ts/test/types/Attachment_test.ts
index 14b0cdcee..b7ef5f754 100644
--- a/ts/test/types/Attachment_test.ts
+++ b/ts/test/types/Attachment_test.ts
@@ -59,6 +59,84 @@ describe('Attachment', () => {
});
});
+ describe('isVisualMedia', () => {
+ it('should return true for images', () => {
+ const attachment: Attachment.Attachment = {
+ fileName: 'meme.gif',
+ data: stringToArrayBuffer('gif'),
+ contentType: MIME.IMAGE_GIF,
+ };
+ assert.isTrue(Attachment.isVisualMedia(attachment));
+ });
+
+ it('should return true for videos', () => {
+ const attachment: Attachment.Attachment = {
+ fileName: 'meme.mp4',
+ data: stringToArrayBuffer('mp4'),
+ contentType: MIME.VIDEO_MP4,
+ };
+ assert.isTrue(Attachment.isVisualMedia(attachment));
+ });
+
+ it('should return false for voice message attachment', () => {
+ const attachment: Attachment.Attachment = {
+ fileName: 'Voice Message.aac',
+ flags: SignalService.AttachmentPointer.Flags.VOICE_MESSAGE,
+ data: stringToArrayBuffer('voice message'),
+ contentType: MIME.AUDIO_AAC,
+ };
+ assert.isFalse(Attachment.isVisualMedia(attachment));
+ });
+
+ it('should return false for other attachments', () => {
+ const attachment: Attachment.Attachment = {
+ fileName: 'foo.json',
+ data: stringToArrayBuffer('{"foo": "bar"}'),
+ contentType: MIME.APPLICATION_JSON,
+ };
+ assert.isFalse(Attachment.isVisualMedia(attachment));
+ });
+ });
+
+ describe('isFile', () => {
+ it('should return true for JSON', () => {
+ const attachment: Attachment.Attachment = {
+ fileName: 'foo.json',
+ data: stringToArrayBuffer('{"foo": "bar"}'),
+ contentType: MIME.APPLICATION_JSON,
+ };
+ assert.isTrue(Attachment.isFile(attachment));
+ });
+
+ it('should return false for images', () => {
+ const attachment: Attachment.Attachment = {
+ fileName: 'meme.gif',
+ data: stringToArrayBuffer('gif'),
+ contentType: MIME.IMAGE_GIF,
+ };
+ assert.isFalse(Attachment.isFile(attachment));
+ });
+
+ it('should return false for videos', () => {
+ const attachment: Attachment.Attachment = {
+ fileName: 'meme.mp4',
+ data: stringToArrayBuffer('mp4'),
+ contentType: MIME.VIDEO_MP4,
+ };
+ assert.isFalse(Attachment.isFile(attachment));
+ });
+
+ it('should return false for voice message attachment', () => {
+ const attachment: Attachment.Attachment = {
+ fileName: 'Voice Message.aac',
+ flags: SignalService.AttachmentPointer.Flags.VOICE_MESSAGE,
+ data: stringToArrayBuffer('voice message'),
+ contentType: MIME.AUDIO_AAC,
+ };
+ assert.isFalse(Attachment.isFile(attachment));
+ });
+ });
+
describe('isVoiceMessage', () => {
it('should return true for voice message attachment', () => {
const attachment: Attachment.Attachment = {
diff --git a/ts/test/types/message/initializeAttachmentMetadata_test.ts b/ts/test/types/message/initializeAttachmentMetadata_test.ts
index 9c87215f3..16ba687e3 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/Attachment.ts b/ts/types/Attachment.ts
index e4e58ffe8..3599f184f 100644
--- a/ts/types/Attachment.ts
+++ b/ts/types/Attachment.ts
@@ -1,7 +1,6 @@
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';
@@ -35,9 +34,29 @@ export const isVisualMedia = (attachment: Attachment): boolean => {
return false;
}
- const isSupportedImageType = GoogleChrome.isImageTypeSupported(contentType);
- const isSupportedVideoType = GoogleChrome.isVideoTypeSupported(contentType);
- return isSupportedImageType || isSupportedVideoType;
+ 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 => {
diff --git a/ts/types/MIME.ts b/ts/types/MIME.ts
index c2cc66ac5..cc4c61b3a 100644
--- a/ts/types/MIME.ts
+++ b/ts/types/MIME.ts
@@ -1,10 +1,12 @@
export type MIMEType = string & { _mimeTypeBrand: any };
export const APPLICATION_OCTET_STREAM = 'application/octet-stream' as MIMEType;
+export const APPLICATION_JSON = 'application/json' as MIMEType;
export const AUDIO_AAC = 'audio/aac' as MIMEType;
export const AUDIO_MP3 = 'audio/mp3' as MIMEType;
export const IMAGE_GIF = 'image/gif' as MIMEType;
export const IMAGE_JPEG = 'image/jpeg' as MIMEType;
+export const VIDEO_MP4 = 'video/mp4' as MIMEType;
export const VIDEO_QUICKTIME = 'video/quicktime' as MIMEType;
export const isJPEG = (value: MIMEType): boolean => value === 'image/jpeg';
diff --git a/ts/types/message/initializeAttachmentMetadata.ts b/ts/types/message/initializeAttachmentMetadata.ts
index 317dacb48..3ab02722e 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,
};
};