From bd380086a486cc72bb9a15ec6955380fd2ef1778 Mon Sep 17 00:00:00 2001 From: Scott Nonnenberg Date: Tue, 5 Oct 2021 15:10:08 -0700 Subject: [PATCH] Send related emoji along with Sticker, fix SendMessage types --- app/attachments.ts | 13 +- protos/SignalService.proto | 1 + test/app/attachments_test.js | 8 +- ts/components/CaptionEditor.stories.tsx | 18 +- ts/components/CompositionArea.stories.tsx | 8 +- ts/components/ForwardMessageModal.stories.tsx | 1 + ts/components/Lightbox.stories.tsx | 18 +- .../conversation/AttachmentList.stories.tsx | 38 ++-- .../conversation/ContactDetail.stories.tsx | 10 +- .../conversation/EmbeddedContact.stories.tsx | 10 +- ts/components/conversation/Image.stories.tsx | 18 +- .../conversation/ImageGrid.stories.tsx | 109 ++++++------ .../conversation/Message.stories.tsx | 134 +++++++------- .../StagedGenericAttachment.stories.tsx | 1 + .../StagedLinkPreview.stories.tsx | 1 + ts/jobs/normalMessageSendJobQueue.ts | 6 +- ts/models/conversations.ts | 21 ++- ts/models/messages.ts | 15 +- ts/test-both/helpers/fakeAttachment.ts | 15 ++ .../shouldUseFullSizeLinkPreviewImage_test.ts | 16 +- ts/test-both/state/ducks/composer_test.ts | 5 +- .../media-gallery/groupMessagesByDate_test.ts | 45 ++--- ts/test-node/types/Attachment_test.ts | 66 +++---- ts/test-node/types/EmbeddedContact_test.ts | 29 ++-- ts/textsecure/SendMessage.ts | 164 +++++++++++------- ts/textsecure/WebAPI.ts | 3 +- ts/textsecure/downloadAttachment.ts | 1 + ts/types/Attachment.ts | 12 +- ts/types/MIME.ts | 3 + ts/types/Stickers.ts | 37 +++- ts/types/message/LinkPreviews.ts | 2 +- ts/util/getProfile.ts | 51 +++--- ts/util/processAttachment.ts | 1 + ts/views/conversation_view.ts | 14 +- ts/window.d.ts | 4 +- 35 files changed, 522 insertions(+), 376 deletions(-) create mode 100644 ts/test-both/helpers/fakeAttachment.ts diff --git a/app/attachments.ts b/app/attachments.ts index 4b9f9c5f2..6be80698a 100644 --- a/app/attachments.ts +++ b/app/attachments.ts @@ -168,14 +168,16 @@ export const createDoesExist = ( export const copyIntoAttachmentsDirectory = ( root: string -): ((sourcePath: string) => Promise) => { +): ((sourcePath: string) => Promise<{ path: string; size: number }>) => { if (!isString(root)) { throw new TypeError("'root' must be a path"); } const userDataPath = getApp().getPath('userData'); - return async (sourcePath: string): Promise => { + return async ( + sourcePath: string + ): Promise<{ path: string; size: number }> => { if (!isString(sourcePath)) { throw new TypeError('sourcePath must be a string'); } @@ -196,7 +198,12 @@ export const copyIntoAttachmentsDirectory = ( await fse.ensureFile(normalized); await fse.copy(sourcePath, normalized); - return relativePath; + const { size } = await fse.stat(normalized); + + return { + path: relativePath, + size, + }; }; }; diff --git a/protos/SignalService.proto b/protos/SignalService.proto index db462c842..df693bb3c 100644 --- a/protos/SignalService.proto +++ b/protos/SignalService.proto @@ -220,6 +220,7 @@ message DataMessage { optional bytes packKey = 2; optional uint32 stickerId = 3; optional AttachmentPointer data = 4; + optional string emoji = 5; } message Reaction { diff --git a/test/app/attachments_test.js b/test/app/attachments_test.js index 1e454e9d1..ce0569540 100644 --- a/test/app/attachments_test.js +++ b/test/app/attachments_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only const fs = require('fs'); @@ -222,14 +222,14 @@ describe('Attachments', () => { }); }); - it('returns a function that copies the source path into the attachments directory', async function thisNeeded() { + it('returns a function that copies the source path into the attachments directory and returns its path and size', async function thisNeeded() { const attachmentsPath = await this.getFakeAttachmentsDirectory(); const someOtherPath = path.join(app.getPath('userData'), 'somethingElse'); await fse.outputFile(someOtherPath, 'hello world'); this.filesToRemove.push(someOtherPath); const copier = Attachments.copyIntoAttachmentsDirectory(attachmentsPath); - const relativePath = await copier(someOtherPath); + const { path: relativePath, size } = await copier(someOtherPath); const absolutePath = path.join(attachmentsPath, relativePath); assert.notEqual(someOtherPath, absolutePath); @@ -237,6 +237,8 @@ describe('Attachments', () => { await fs.promises.readFile(absolutePath, 'utf8'), 'hello world' ); + + assert.strictEqual(size, 'hello world'.length); }); }); diff --git a/ts/components/CaptionEditor.stories.tsx b/ts/components/CaptionEditor.stories.tsx index 301de696c..75f353d4c 100644 --- a/ts/components/CaptionEditor.stories.tsx +++ b/ts/components/CaptionEditor.stories.tsx @@ -12,17 +12,19 @@ import { AUDIO_MP3, IMAGE_JPEG, VIDEO_MP4 } from '../types/MIME'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; +import { fakeAttachment } from '../test-both/helpers/fakeAttachment'; + const i18n = setupI18n('en', enMessages); const stories = storiesOf('Components/Caption Editor', module); const createProps = (overrideProps: Partial = {}): Props => ({ - attachment: { + attachment: fakeAttachment({ contentType: IMAGE_JPEG, fileName: '', url: '', ...overrideProps.attachment, - }, + }), caption: text('caption', overrideProps.caption || ''), close: action('close'), i18n, @@ -50,11 +52,11 @@ stories.add('Image with Caption', () => { stories.add('Video', () => { const props = createProps({ - attachment: { + attachment: fakeAttachment({ contentType: VIDEO_MP4, fileName: 'pixabay-Soap-Bubble-7141.mp4', url: '/fixtures/pixabay-Soap-Bubble-7141.mp4', - }, + }), url: '/fixtures/pixabay-Soap-Bubble-7141.mp4', }); @@ -63,11 +65,11 @@ stories.add('Video', () => { stories.add('Video with Caption', () => { const props = createProps({ - attachment: { + attachment: fakeAttachment({ contentType: VIDEO_MP4, fileName: 'pixabay-Soap-Bubble-7141.mp4', url: '/fixtures/pixabay-Soap-Bubble-7141.mp4', - }, + }), caption: 'This is the user-provided caption. We show it overlaid on the image. If it is really long, then it wraps, but it does not get too close to the edges of the image.', url: '/fixtures/pixabay-Soap-Bubble-7141.mp4', @@ -78,11 +80,11 @@ stories.add('Video with Caption', () => { stories.add('Unsupported Attachment Type', () => { const props = createProps({ - attachment: { + attachment: fakeAttachment({ contentType: AUDIO_MP3, fileName: 'incompetech-com-Agnus-Dei-X.mp3', url: '/fixtures/incompetech-com-Agnus-Dei-X.mp3', - }, + }), url: '/fixtures/incompetech-com-Agnus-Dei-X.mp3', }); diff --git a/ts/components/CompositionArea.stories.tsx b/ts/components/CompositionArea.stories.tsx index 58aa33997..4f089c88f 100644 --- a/ts/components/CompositionArea.stories.tsx +++ b/ts/components/CompositionArea.stories.tsx @@ -12,6 +12,9 @@ import { CompositionArea, Props } from './CompositionArea'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; +import { fakeAttachment } from '../test-both/helpers/fakeAttachment'; +import { landscapeGreenUrl } from '../storybook/Fixtures'; + const i18n = setupI18n('en', enMessages); const story = storiesOf('Components/CompositionArea', module); @@ -154,9 +157,10 @@ story.add('SMS-only', () => { story.add('Attachments', () => { const props = createProps({ draftAttachments: [ - { + fakeAttachment({ contentType: IMAGE_JPEG, - }, + url: landscapeGreenUrl, + }), ], }); diff --git a/ts/components/ForwardMessageModal.stories.tsx b/ts/components/ForwardMessageModal.stories.tsx index a8d628bc9..744be13a1 100644 --- a/ts/components/ForwardMessageModal.stories.tsx +++ b/ts/components/ForwardMessageModal.stories.tsx @@ -23,6 +23,7 @@ const createAttachment = ( fileName: text('attachment fileName', props.fileName || ''), screenshot: props.screenshot, url: text('attachment url', props.url || ''), + size: 3433, }); const story = storiesOf('Components/ForwardMessageModal', module); diff --git a/ts/components/Lightbox.stories.tsx b/ts/components/Lightbox.stories.tsx index 4891f4460..714a86c96 100644 --- a/ts/components/Lightbox.stories.tsx +++ b/ts/components/Lightbox.stories.tsx @@ -19,6 +19,8 @@ import { stringToMIMEType, } from '../types/MIME'; +import { fakeAttachment } from '../test-both/helpers/fakeAttachment'; + const i18n = setupI18n('en', enMessages); const story = storiesOf('Components/Lightbox', module); @@ -29,12 +31,12 @@ function createMediaItem( overrideProps: OverridePropsMediaItemType ): MediaItemType { return { - attachment: { + attachment: fakeAttachment({ caption: overrideProps.caption || '', contentType: IMAGE_JPEG, fileName: overrideProps.objectURL, url: overrideProps.objectURL, - }, + }), contentType: IMAGE_JPEG, index: 0, message: { @@ -63,13 +65,13 @@ story.add('Multimedia', () => { const props = createProps({ media: [ { - attachment: { + attachment: fakeAttachment({ contentType: IMAGE_JPEG, fileName: 'tina-rolf-269345-unsplash.jpg', url: '/fixtures/tina-rolf-269345-unsplash.jpg', caption: 'Still from The Lighthouse, starring Robert Pattinson and Willem Defoe.', - }, + }), contentType: IMAGE_JPEG, index: 0, message: { @@ -83,11 +85,11 @@ story.add('Multimedia', () => { objectURL: '/fixtures/tina-rolf-269345-unsplash.jpg', }, { - attachment: { + attachment: fakeAttachment({ contentType: VIDEO_MP4, fileName: 'pixabay-Soap-Bubble-7141.mp4', url: '/fixtures/pixabay-Soap-Bubble-7141.mp4', - }, + }), contentType: VIDEO_MP4, index: 1, message: { @@ -122,11 +124,11 @@ story.add('Missing Media', () => { const props = createProps({ media: [ { - attachment: { + attachment: fakeAttachment({ contentType: IMAGE_JPEG, fileName: 'tina-rolf-269345-unsplash.jpg', url: '/fixtures/tina-rolf-269345-unsplash.jpg', - }, + }), contentType: IMAGE_JPEG, index: 0, message: { diff --git a/ts/components/conversation/AttachmentList.stories.tsx b/ts/components/conversation/AttachmentList.stories.tsx index 3bd6481ad..709174b20 100644 --- a/ts/components/conversation/AttachmentList.stories.tsx +++ b/ts/components/conversation/AttachmentList.stories.tsx @@ -17,6 +17,8 @@ import { import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; +import { fakeAttachment } from '../../test-both/helpers/fakeAttachment'; + const i18n = setupI18n('en', enMessages); const story = storiesOf('Components/Conversation/AttachmentList', module); @@ -33,11 +35,11 @@ const createProps = (overrideProps: Partial = {}): Props => ({ story.add('One File', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: IMAGE_JPEG, fileName: 'tina-rolf-269345-unsplash.jpg', url: '/fixtures/tina-rolf-269345-unsplash.jpg', - }, + }), ], }); return ; @@ -46,12 +48,12 @@ story.add('One File', () => { story.add('Multiple Visual Attachments', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: IMAGE_JPEG, fileName: 'tina-rolf-269345-unsplash.jpg', url: '/fixtures/tina-rolf-269345-unsplash.jpg', - }, - { + }), + fakeAttachment({ contentType: VIDEO_MP4, fileName: 'pixabay-Soap-Bubble-7141.mp4', url: '/fixtures/pixabay-Soap-Bubble-7141.mp4', @@ -62,12 +64,12 @@ story.add('Multiple Visual Attachments', () => { contentType: IMAGE_JPEG, path: 'originalpath', }, - }, - { + }), + fakeAttachment({ contentType: IMAGE_GIF, fileName: 'giphy-GVNv0UpeYm17e', url: '/fixtures/giphy-GVNvOUpeYmI7e.gif', - }, + }), ], }); @@ -77,22 +79,22 @@ story.add('Multiple Visual Attachments', () => { story.add('Multiple with Non-Visual Types', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: IMAGE_JPEG, fileName: 'tina-rolf-269345-unsplash.jpg', url: '/fixtures/tina-rolf-269345-unsplash.jpg', - }, - { + }), + fakeAttachment({ contentType: stringToMIMEType('text/plain'), fileName: 'lorem-ipsum.txt', url: '/fixtures/lorem-ipsum.txt', - }, - { + }), + fakeAttachment({ contentType: AUDIO_MP3, fileName: 'incompetech-com-Agnus-Dei-X.mp3', url: '/fixtures/incompetech-com-Agnus-Dei-X.mp3', - }, - { + }), + fakeAttachment({ contentType: VIDEO_MP4, fileName: 'pixabay-Soap-Bubble-7141.mp4', url: '/fixtures/pixabay-Soap-Bubble-7141.mp4', @@ -103,12 +105,12 @@ story.add('Multiple with Non-Visual Types', () => { contentType: IMAGE_JPEG, path: 'originalpath', }, - }, - { + }), + fakeAttachment({ contentType: IMAGE_GIF, fileName: 'giphy-GVNv0UpeYm17e', url: '/fixtures/giphy-GVNvOUpeYmI7e.gif', - }, + }), ], }); diff --git a/ts/components/conversation/ContactDetail.stories.tsx b/ts/components/conversation/ContactDetail.stories.tsx index c2c8be8ce..abf807d75 100644 --- a/ts/components/conversation/ContactDetail.stories.tsx +++ b/ts/components/conversation/ContactDetail.stories.tsx @@ -13,6 +13,8 @@ import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import { IMAGE_GIF } from '../../types/MIME'; +import { fakeAttachment } from '../../test-both/helpers/fakeAttachment'; + const i18n = setupI18n('en', enMessages); const story = storiesOf('Components/Conversation/ContactDetail', module); @@ -72,10 +74,10 @@ const fullContact = { }, ], avatar: { - avatar: { + avatar: fakeAttachment({ path: '/fixtures/giphy-GVNvOUpeYmI7e.gif', contentType: IMAGE_GIF, - }, + }), isProfile: true, }, email: [ @@ -209,10 +211,10 @@ story.add('Loading Avatar', () => { const props = createProps({ contact: { avatar: { - avatar: { + avatar: fakeAttachment({ contentType: IMAGE_GIF, pending: true, - }, + }), isProfile: true, }, }, diff --git a/ts/components/conversation/EmbeddedContact.stories.tsx b/ts/components/conversation/EmbeddedContact.stories.tsx index bbe4c4c16..b688c3f65 100644 --- a/ts/components/conversation/EmbeddedContact.stories.tsx +++ b/ts/components/conversation/EmbeddedContact.stories.tsx @@ -13,6 +13,8 @@ import enMessages from '../../../_locales/en/messages.json'; import { ContactFormType } from '../../types/EmbeddedContact'; import { IMAGE_GIF } from '../../types/MIME'; +import { fakeAttachment } from '../../test-both/helpers/fakeAttachment'; + const i18n = setupI18n('en', enMessages); const story = storiesOf('Components/Conversation/EmbeddedContact', module); @@ -35,10 +37,10 @@ const createProps = (overrideProps: Partial = {}): Props => ({ const fullContact = { avatar: { - avatar: { + avatar: fakeAttachment({ path: '/fixtures/giphy-GVNvOUpeYmI7e.gif', contentType: IMAGE_GIF, - }, + }), isProfile: true, }, email: [ @@ -134,10 +136,10 @@ story.add('Loading Avatar', () => { displayName: 'Jerry Jordan', }, avatar: { - avatar: { + avatar: fakeAttachment({ pending: true, contentType: IMAGE_GIF, - }, + }), isProfile: true, }, }, diff --git a/ts/components/conversation/Image.stories.tsx b/ts/components/conversation/Image.stories.tsx index 071e5a87a..b45d4a2cc 100644 --- a/ts/components/conversation/Image.stories.tsx +++ b/ts/components/conversation/Image.stories.tsx @@ -14,17 +14,21 @@ import { ThemeType } from '../../types/Util'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; +import { fakeAttachment } from '../../test-both/helpers/fakeAttachment'; + const i18n = setupI18n('en', enMessages); const story = storiesOf('Components/Conversation/Image', module); const createProps = (overrideProps: Partial = {}): Props => ({ alt: text('alt', overrideProps.alt || ''), - attachment: overrideProps.attachment || { - contentType: IMAGE_PNG, - fileName: 'sax.png', - url: pngUrl, - }, + attachment: + overrideProps.attachment || + fakeAttachment({ + contentType: IMAGE_PNG, + fileName: 'sax.png', + url: pngUrl, + }), blurHash: text('blurHash', overrideProps.blurHash || ''), bottomOverlay: boolean('bottomOverlay', overrideProps.bottomOverlay || false), closeButton: boolean('closeButton', overrideProps.closeButton || false), @@ -99,11 +103,11 @@ story.add('Close Button', () => { story.add('No Border or Background', () => { const props = createProps({ - attachment: { + attachment: fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', url: pngUrl, - }, + }), noBackground: true, noBorder: true, url: pngUrl, diff --git a/ts/components/conversation/ImageGrid.stories.tsx b/ts/components/conversation/ImageGrid.stories.tsx index ff4866baa..78f49f3c0 100644 --- a/ts/components/conversation/ImageGrid.stories.tsx +++ b/ts/components/conversation/ImageGrid.stories.tsx @@ -19,6 +19,7 @@ import { import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import { pngUrl, squareStickerUrl } from '../../storybook/Fixtures'; +import { fakeAttachment } from '../../test-both/helpers/fakeAttachment'; const i18n = setupI18n('en', enMessages); @@ -26,13 +27,13 @@ const story = storiesOf('Components/Conversation/ImageGrid', module); const createProps = (overrideProps: Partial = {}): Props => ({ attachments: overrideProps.attachments || [ - { + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, + }), ], bottomOverlay: boolean('bottomOverlay', overrideProps.bottomOverlay || false), i18n, @@ -60,20 +61,20 @@ story.add('One Image', () => { story.add('Two Images', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, - { + }), + fakeAttachment({ contentType: IMAGE_JPEG, fileName: 'tina-rolf-269345-unsplash.jpg', height: 1680, url: '/fixtures/tina-rolf-269345-unsplash.jpg', width: 3000, - }, + }), ], }); @@ -83,27 +84,27 @@ story.add('Two Images', () => { story.add('Three Images', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, - { + }), + fakeAttachment({ contentType: IMAGE_JPEG, fileName: 'tina-rolf-269345-unsplash.jpg', height: 1680, url: '/fixtures/tina-rolf-269345-unsplash.jpg', width: 3000, - }, - { + }), + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, + }), ], }); @@ -113,34 +114,34 @@ story.add('Three Images', () => { story.add('Four Images', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, - { + }), + fakeAttachment({ contentType: IMAGE_JPEG, fileName: 'tina-rolf-269345-unsplash.jpg', height: 1680, url: '/fixtures/tina-rolf-269345-unsplash.jpg', width: 3000, - }, - { + }), + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, - { + }), + fakeAttachment({ contentType: IMAGE_JPEG, fileName: 'tina-rolf-269345-unsplash.jpg', height: 1680, url: '/fixtures/tina-rolf-269345-unsplash.jpg', width: 3000, - }, + }), ], }); @@ -150,41 +151,41 @@ story.add('Four Images', () => { story.add('Five Images', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, - { + }), + fakeAttachment({ contentType: IMAGE_JPEG, fileName: 'tina-rolf-269345-unsplash.jpg', height: 1680, url: '/fixtures/tina-rolf-269345-unsplash.jpg', width: 3000, - }, - { + }), + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, - { + }), + fakeAttachment({ contentType: IMAGE_JPEG, fileName: 'tina-rolf-269345-unsplash.jpg', height: 1680, url: '/fixtures/tina-rolf-269345-unsplash.jpg', width: 3000, - }, - { + }), + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, + }), ], }); @@ -194,55 +195,55 @@ story.add('Five Images', () => { story.add('6+ Images', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, - { + }), + fakeAttachment({ contentType: IMAGE_JPEG, fileName: 'tina-rolf-269345-unsplash.jpg', height: 1680, url: '/fixtures/tina-rolf-269345-unsplash.jpg', width: 3000, - }, - { + }), + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, - { + }), + fakeAttachment({ contentType: IMAGE_JPEG, fileName: 'tina-rolf-269345-unsplash.jpg', height: 1680, url: '/fixtures/tina-rolf-269345-unsplash.jpg', width: 3000, - }, - { + }), + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, - { + }), + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, - { + }), + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, + }), ], }); @@ -251,7 +252,7 @@ story.add('6+ Images', () => { story.add('Mixed Content Types', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: VIDEO_MP4, fileName: 'pixabay-Soap-Bubble-7141.mp4', height: 112, @@ -264,24 +265,24 @@ story.add('Mixed Content Types', () => { }, url: '/fixtures/pixabay-Soap-Bubble-7141.mp4', width: 112, - }, - { + }), + fakeAttachment({ contentType: IMAGE_PNG, fileName: 'sax.png', height: 1200, url: pngUrl, width: 800, - }, - { + }), + fakeAttachment({ contentType: stringToMIMEType('text/plain'), fileName: 'lorem-ipsum.txt', url: '/fixtures/lorem-ipsum.txt', - }, - { + }), + fakeAttachment({ contentType: AUDIO_MP3, fileName: 'incompetech-com-Agnus-Dei-X.mp3', url: '/fixtures/incompetech-com-Agnus-Dei-X.mp3', - }, + }), ], }); @@ -291,13 +292,13 @@ story.add('Mixed Content Types', () => { story.add('Sticker', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: IMAGE_WEBP, fileName: 'sticker.webp', height: 512, url: squareStickerUrl, width: 512, - }, + }), ], isSticker: true, stickerSize: 128, diff --git a/ts/components/conversation/Message.stories.tsx b/ts/components/conversation/Message.stories.tsx index dad12c0f0..8beed4e7d 100644 --- a/ts/components/conversation/Message.stories.tsx +++ b/ts/components/conversation/Message.stories.tsx @@ -28,6 +28,8 @@ import enMessages from '../../../_locales/en/messages.json'; import { pngUrl } from '../../storybook/Fixtures'; import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation'; +import { fakeAttachment } from '../../test-both/helpers/fakeAttachment'; + const i18n = setupI18n('en', enMessages); function getJoyReaction() { @@ -444,13 +446,13 @@ story.add('Avatar in Group', () => { story.add('Sticker', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ url: '/fixtures/512x515-thumbs-up-lincoln.webp', fileName: '512x515-thumbs-up-lincoln.webp', contentType: IMAGE_WEBP, width: 128, height: 128, - }, + }), ], isSticker: true, status: 'sent', @@ -524,13 +526,13 @@ story.add('Link Preview', () => { previews: [ { domain: 'signal.org', - image: { + image: fakeAttachment({ contentType: IMAGE_PNG, fileName: 'the-sax.png', height: 240, url: pngUrl, width: 320, - }, + }), isStickerPack: false, title: 'Signal', description: @@ -551,13 +553,13 @@ story.add('Link Preview with Small Image', () => { previews: [ { domain: 'signal.org', - image: { + image: fakeAttachment({ contentType: IMAGE_PNG, fileName: 'the-sax.png', height: 50, url: pngUrl, width: 50, - }, + }), isStickerPack: false, title: 'Signal', description: @@ -639,13 +641,13 @@ story.add('Link Preview with small image, long description', () => { previews: [ { domain: 'signal.org', - image: { + image: fakeAttachment({ contentType: IMAGE_PNG, fileName: 'the-sax.png', height: 50, url: pngUrl, width: 50, - }, + }), isStickerPack: false, title: 'Signal', description: Array(10) @@ -669,13 +671,13 @@ story.add('Link Preview with no date', () => { previews: [ { domain: 'signal.org', - image: { + image: fakeAttachment({ contentType: IMAGE_PNG, fileName: 'the-sax.png', height: 240, url: pngUrl, width: 320, - }, + }), isStickerPack: false, title: 'Signal', description: @@ -695,13 +697,13 @@ story.add('Link Preview with too new a date', () => { previews: [ { domain: 'signal.org', - image: { + image: fakeAttachment({ contentType: IMAGE_PNG, fileName: 'the-sax.png', height: 240, url: pngUrl, width: 320, - }, + }), isStickerPack: false, title: 'Signal', description: @@ -720,13 +722,13 @@ story.add('Link Preview with too new a date', () => { story.add('Image', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ url: '/fixtures/tina-rolf-269345-unsplash.jpg', fileName: 'tina-rolf-269345-unsplash.jpg', contentType: IMAGE_JPEG, width: 128, height: 128, - }, + }), ], status: 'sent', }); @@ -738,41 +740,41 @@ for (let i = 2; i <= 5; i += 1) { story.add(`Multiple Images x${i}`, () => { const props = createProps({ attachments: [ - { + fakeAttachment({ url: '/fixtures/tina-rolf-269345-unsplash.jpg', fileName: 'tina-rolf-269345-unsplash.jpg', contentType: IMAGE_JPEG, width: 128, height: 128, - }, - { + }), + fakeAttachment({ url: '/fixtures/tina-rolf-269345-unsplash.jpg', fileName: 'tina-rolf-269345-unsplash.jpg', contentType: IMAGE_JPEG, width: 128, height: 128, - }, - { + }), + fakeAttachment({ url: '/fixtures/tina-rolf-269345-unsplash.jpg', fileName: 'tina-rolf-269345-unsplash.jpg', contentType: IMAGE_JPEG, width: 128, height: 128, - }, - { + }), + fakeAttachment({ url: '/fixtures/tina-rolf-269345-unsplash.jpg', fileName: 'tina-rolf-269345-unsplash.jpg', contentType: IMAGE_JPEG, width: 128, height: 128, - }, - { + }), + fakeAttachment({ url: '/fixtures/tina-rolf-269345-unsplash.jpg', fileName: 'tina-rolf-269345-unsplash.jpg', contentType: IMAGE_JPEG, width: 128, height: 128, - }, + }), ].slice(0, i), status: 'sent', }); @@ -784,13 +786,13 @@ for (let i = 2; i <= 5; i += 1) { story.add('Image with Caption', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ url: '/fixtures/tina-rolf-269345-unsplash.jpg', fileName: 'tina-rolf-269345-unsplash.jpg', contentType: IMAGE_JPEG, width: 128, height: 128, - }, + }), ], status: 'sent', text: 'This is my home.', @@ -802,14 +804,14 @@ story.add('Image with Caption', () => { story.add('GIF', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: VIDEO_MP4, flags: SignalService.AttachmentPointer.Flags.GIF, fileName: 'cat-gif.mp4', url: '/fixtures/cat-gif.mp4', width: 400, height: 332, - }, + }), ], status: 'sent', }); @@ -820,14 +822,14 @@ story.add('GIF', () => { story.add('GIF in a group', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: VIDEO_MP4, flags: SignalService.AttachmentPointer.Flags.GIF, fileName: 'cat-gif.mp4', url: '/fixtures/cat-gif.mp4', width: 400, height: 332, - }, + }), ], conversationType: 'group', status: 'sent', @@ -839,7 +841,7 @@ story.add('GIF in a group', () => { story.add('Not Downloaded GIF', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: VIDEO_MP4, flags: SignalService.AttachmentPointer.Flags.GIF, fileName: 'cat-gif.mp4', @@ -847,7 +849,7 @@ story.add('Not Downloaded GIF', () => { blurHash: 'LDA,FDBnm+I=p{tkIUI;~UkpELV]', width: 400, height: 332, - }, + }), ], status: 'sent', }); @@ -858,7 +860,7 @@ story.add('Not Downloaded GIF', () => { story.add('Pending GIF', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ pending: true, contentType: VIDEO_MP4, flags: SignalService.AttachmentPointer.Flags.GIF, @@ -867,7 +869,7 @@ story.add('Pending GIF', () => { blurHash: 'LDA,FDBnm+I=p{tkIUI;~UkpELV]', width: 400, height: 332, - }, + }), ], status: 'sent', }); @@ -881,11 +883,11 @@ story.add('Audio', () => { const messageProps = createProps({ attachments: [ - { + fakeAttachment({ contentType: AUDIO_MP3, fileName: 'incompetech-com-Agnus-Dei-X.mp3', url: '/fixtures/incompetech-com-Agnus-Dei-X.mp3', - }, + }), ], ...(isPlayed ? { @@ -923,11 +925,11 @@ story.add('Audio', () => { story.add('Long Audio', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: AUDIO_MP3, fileName: 'long-audio.mp3', url: '/fixtures/long-audio.mp3', - }, + }), ], status: 'sent', }); @@ -938,11 +940,11 @@ story.add('Long Audio', () => { story.add('Audio with Caption', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: AUDIO_MP3, fileName: 'incompetech-com-Agnus-Dei-X.mp3', url: '/fixtures/incompetech-com-Agnus-Dei-X.mp3', - }, + }), ], status: 'sent', text: 'This is what I sound like.', @@ -954,10 +956,10 @@ story.add('Audio with Caption', () => { story.add('Audio with Not Downloaded Attachment', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: AUDIO_MP3, fileName: 'incompetech-com-Agnus-Dei-X.mp3', - }, + }), ], status: 'sent', }); @@ -968,11 +970,11 @@ story.add('Audio with Not Downloaded Attachment', () => { story.add('Audio with Pending Attachment', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: AUDIO_MP3, fileName: 'incompetech-com-Agnus-Dei-X.mp3', pending: true, - }, + }), ], status: 'sent', }); @@ -983,11 +985,11 @@ story.add('Audio with Pending Attachment', () => { story.add('Other File Type', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: stringToMIMEType('text/plain'), fileName: 'my-resume.txt', url: 'my-resume.txt', - }, + }), ], status: 'sent', }); @@ -998,11 +1000,11 @@ story.add('Other File Type', () => { story.add('Other File Type with Caption', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: stringToMIMEType('text/plain'), fileName: 'my-resume.txt', url: 'my-resume.txt', - }, + }), ], status: 'sent', text: 'This is what I have done.', @@ -1014,12 +1016,12 @@ story.add('Other File Type with Caption', () => { story.add('Other File Type with Long Filename', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: stringToMIMEType('text/plain'), fileName: 'INSERT-APP-NAME_INSERT-APP-APPLE-ID_AppStore_AppsGamesWatch.psd.zip', url: 'a2/a2334324darewer4234', - }, + }), ], status: 'sent', text: 'This is what I have done.', @@ -1031,13 +1033,13 @@ story.add('Other File Type with Long Filename', () => { story.add('TapToView Image', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ url: '/fixtures/tina-rolf-269345-unsplash.jpg', fileName: 'tina-rolf-269345-unsplash.jpg', contentType: IMAGE_JPEG, width: 128, height: 128, - }, + }), ], isTapToView: true, status: 'sent', @@ -1049,13 +1051,13 @@ story.add('TapToView Image', () => { story.add('TapToView Video', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: VIDEO_MP4, fileName: 'pixabay-Soap-Bubble-7141.mp4', height: 128, url: '/fixtures/pixabay-Soap-Bubble-7141.mp4', width: 128, - }, + }), ], isTapToView: true, status: 'sent', @@ -1067,14 +1069,14 @@ story.add('TapToView Video', () => { story.add('TapToView GIF', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: VIDEO_MP4, flags: SignalService.AttachmentPointer.Flags.GIF, fileName: 'cat-gif.mp4', url: '/fixtures/cat-gif.mp4', width: 400, height: 332, - }, + }), ], isTapToView: true, status: 'sent', @@ -1086,13 +1088,13 @@ story.add('TapToView GIF', () => { story.add('TapToView Expired', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ url: '/fixtures/tina-rolf-269345-unsplash.jpg', fileName: 'tina-rolf-269345-unsplash.jpg', contentType: IMAGE_JPEG, width: 128, height: 128, - }, + }), ], isTapToView: true, isTapToViewExpired: true, @@ -1105,13 +1107,13 @@ story.add('TapToView Expired', () => { story.add('TapToView Error', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ url: '/fixtures/tina-rolf-269345-unsplash.jpg', fileName: 'tina-rolf-269345-unsplash.jpg', contentType: IMAGE_JPEG, width: 128, height: 128, - }, + }), ], isTapToView: true, isTapToViewError: true, @@ -1124,13 +1126,13 @@ story.add('TapToView Error', () => { story.add('Dangerous File Type', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ contentType: stringToMIMEType( 'application/vnd.microsoft.portable-executable' ), fileName: 'terrible.exe', url: 'terrible.exe', - }, + }), ], status: 'sent', }); @@ -1174,13 +1176,13 @@ story.add('@Mentions', () => { story.add('All the context menus', () => { const props = createProps({ attachments: [ - { + fakeAttachment({ url: '/fixtures/tina-rolf-269345-unsplash.jpg', fileName: 'tina-rolf-269345-unsplash.jpg', contentType: IMAGE_JPEG, width: 128, height: 128, - }, + }), ], status: 'partial-sent', canDeleteForEveryone: true, @@ -1194,13 +1196,13 @@ story.add('Not approved, with link preview', () => { previews: [ { domain: 'signal.org', - image: { + image: fakeAttachment({ contentType: IMAGE_PNG, fileName: 'the-sax.png', height: 240, url: pngUrl, width: 320, - }, + }), isStickerPack: false, title: 'Signal', description: diff --git a/ts/components/conversation/StagedGenericAttachment.stories.tsx b/ts/components/conversation/StagedGenericAttachment.stories.tsx index c96fd4715..9cc7d11f0 100644 --- a/ts/components/conversation/StagedGenericAttachment.stories.tsx +++ b/ts/components/conversation/StagedGenericAttachment.stories.tsx @@ -33,6 +33,7 @@ const createAttachment = ( ), fileName: text('attachment fileName', props.fileName || ''), url: '', + size: 14243, }); story.add('Text File', () => { diff --git a/ts/components/conversation/StagedLinkPreview.stories.tsx b/ts/components/conversation/StagedLinkPreview.stories.tsx index b009c0da5..575b0986a 100644 --- a/ts/components/conversation/StagedLinkPreview.stories.tsx +++ b/ts/components/conversation/StagedLinkPreview.stories.tsx @@ -32,6 +32,7 @@ const createAttachment = ( ), fileName: text('attachment fileName', props.fileName || ''), url: text('attachment url', props.url || ''), + size: 24325, }); const createProps = (overrideProps: Partial = {}): Props => ({ diff --git a/ts/jobs/normalMessageSendJobQueue.ts b/ts/jobs/normalMessageSendJobQueue.ts index 15628f31a..9545a5fe1 100644 --- a/ts/jobs/normalMessageSendJobQueue.ts +++ b/ts/jobs/normalMessageSendJobQueue.ts @@ -27,8 +27,8 @@ import type { AttachmentType, GroupV1InfoType, GroupV2InfoType, - PreviewType, } from '../textsecure/SendMessage'; +import type { LinkPreviewType } from '../types/message/LinkPreviews'; import type { BodyRangesType } from '../types/Util'; import type { WhatIsThis } from '../window.d'; @@ -299,7 +299,7 @@ export class NormalMessageSendJobQueue extends JobQueue; + preview: Array; profileKey: undefined | Uint8Array; quote: WhatIsThis; sticker: WhatIsThis; diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts index 7b7508da1..c4c18743e 100644 --- a/ts/models/conversations.ts +++ b/ts/models/conversations.ts @@ -23,6 +23,7 @@ import { CapabilityError } from '../types/errors'; import type { GroupV1InfoType, GroupV2InfoType, + StickerType, } from '../textsecure/SendMessage'; import createTaskWithTimeout from '../textsecure/TaskWithTimeout'; import { CallbackResultType } from '../textsecure/Types.d'; @@ -60,6 +61,7 @@ import { sendReadReceiptsFor } from '../util/sendReadReceiptsFor'; import { updateConversationsWithUuidLookup } from '../updateConversationsWithUuidLookup'; import { ReadStatus } from '../messages/MessageReadStatus'; import { SendState, SendStatus } from '../messages/MessageSendState'; +import type { LinkPreviewType } from '../types/message/LinkPreviews'; import * as durations from '../util/durations'; import { concat, @@ -3279,8 +3281,8 @@ export class ConversationModel extends window.Backbone } const { key } = packData; - const { path, width, height } = stickerData; - const arrayBuffer = await readStickerData(path); + const { emoji, path, width, height } = stickerData; + const data = await readStickerData(path); // We need this content type to be an image so we can display an `` instead of a // `