signal-desktop/js/models/conversations.js

3388 lines
97 KiB
JavaScript
Raw Normal View History

2019-01-14 21:49:58 +00:00
/* global
_,
i18n,
Backbone,
libphonenumber,
ConversationController,
MessageController,
2019-01-14 21:49:58 +00:00
libsignal,
storage,
textsecure,
2020-05-27 21:37:06 +00:00
Whisper,
Signal
2019-01-14 21:49:58 +00:00
*/
/* eslint-disable more/no-then */
Auto-orient image attachments based on EXIF metadata As described in #998, images are sometimes displayed with an incorrect orientation. This is because cameras often write files in the native sensor byte order and attach the `Orientation` EXIF metadata to tell end-user devices how to display the images based on the original author’s capture orientation. Electron/Chromium (and therefore Signal Desktop) currently doesn’t support applying this metadata for `<img>` tags, e.g. CSS `image-orientation: from- image`. As a workaround, this change uses the `loadImage` library with the `orientation: true` flag to auto-orient images ~~before display~~ upon receipt and before sending. **Changes** - [x] ~~Auto-orient images during display in message list view~~ - [x] Ensure image is not displayed until loaded (to prevent layout reflow) . - [x] Auto-orient images upon receipt and before storing in IndexedDB (~~or preserve original data until Chromium offers native fix?~~) - [x] Auto-orient images in compose area preview. - [x] ~~Auto-orient images in lightbox view~~ - [x] Auto-orient images before sending / storage. - [x] Add EditorConfig for sharing code styles across editors. - [x] Fix ESLint ignore file. - [x] Change `function-paren-newline` ESLint rule from `consistent` to `multiline`. - [x] Add `operator-linebreak` ESLint rule for consistency. - [x] Added `blob-util` dependency for converting between array buffers, blobs, etc. - [x] Extracted `createMessageHandler` to consolidate logic for `onMessageReceived` and `onSentMessage`. - [x] Introduce `async` / `await` to simplify async coding (restore control flow for branching, loops, and exceptions). - [x] Introduce `window.Signal` namespace for exposing ES2015+ CommonJS modules. - [x] Introduce rudimentary `Message` and `Attachment` types to begin defining a schema and versioning. This will allow us to track which changes, e.g. auto-orient JPEGs, per message / attachment as well as which fields are stored. - [x] Renamed `window.dataURLtoBlob` to `window.dataURLToBlobSync` to both fix the strange `camelCase` as well as to highlight that this operation is synchronous and therefore blocks the user thread. - [x] Normalize all JPEG MIME types to `image/jpeg`, eliminating the invalid `image/jpg`. - [x] Add `npm run test-modules` command for testing non-browser specific CommonJS modules. - **Stretch Goals** - [ ] ~~Restrict `autoOrientImage` to `Blob` to narrow API interface.~~ Do this once we use PureScript. - [ ] ~~Return non-JPEGs as no-op from `autoOrientImage`.~~ Skipping `autoOrientImage` for non-JPEGs altogether. - [ ] Retroactively auto-orient existing JPEG image attachments in the background. --- Fixes #998 --- - **Blog:** EXIF Orientation Handling Is a Ghetto: https://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ - **Chromium Bug:** EXIF orientation is ignored: https://bugs.chromium.org/p/chromium/issues/detail?id=56845 - **Chromium Bug:** Support for the CSS image-orientation CSS property: https://bugs.chromium.org/p/chromium/issues/detail?id=158753 --- commit ce5090b473a2448229dc38e4c3f15d7ad0137714 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 16 10:35:36 2018 -0500 Inline message descriptors commit 329036e59c138c1e950ec7c654eebd7d87076de5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:34:40 2018 -0500 Clarify order of operations Semantically, it makes more sense to do `getFile` before `clearForm` even though it seems to work either way. commit f9d4cfb2ba0d8aa308b0923bbe6066ea34cb97bd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:18:26 2018 -0500 Simplify `operator-linebreak` configuration Enabling `before` caused more code changes and it turns out our previous configuration is already the default. commit db588997acdd90ed2ad829174ecbba744383c78b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:15:59 2018 -0500 Remove obsolete TODO commit 799c8817633f6afa0b731fc3b5434e463bd850e3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:12:18 2018 -0500 Enable ESLint `function-paren-newline` `multiline` Per discussion. commit b660b6bc8ef41df7601a411213d6cda80821df87 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:10:48 2018 -0500 Use `messageDescriptor.id` not `source` commit 5e7309d176f4a7e97d3dc4c738e6b0ccd4792871 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:29:01 2018 -0500 Remove unnecessary `eslint-env` commit 393b3da55eabd7413596c86cc3971b063a0efe31 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:19:17 2018 -0500 Refactor `onSentMessage` and `onMessageReceived` Since they are so similar, we create the handlers using new `createMessageHandler` function. This allows us to ensure both synced and received messages go through schema upgrade pipeline. commit b3db0bf179c9a5bea96480cde28c6fa7193ac117 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:18:21 2018 -0500 Add `Message` descriptor functions commit 8febf125b1b42fe4ae1888dd50fcee2749dc1ff0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 14:46:56 2018 -0500 Fix typo commit 98d951ef77bd578b313a4ff4b496b793e82e88d5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:39 2018 -0500 Remove `promises` reference commit a0e9559ed5bed947dabf28cb672e63d39948d854 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:13 2018 -0500 Fix `AttachmentView::mediaType` fall-through commit 67be916a83951b8a1f9b22efe78a6da6b1825f38 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:03:41 2018 -0500 Remove minor TODOs commit 0af186e118256b62905de38487ffacc41693ff47 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:41 2018 -0500 Enable ESLint for `js/views/attachment_view.js` commit 28a2dc5b8a28e1a087924fdc7275bf7d9a577b92 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:12 2018 -0500 Remove dynamic type checks commit f4ce36fcfc2737de32d911cd6103f889097813f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:27:56 2018 -0500 Rename `process` to `upgradeSchema` - `Message.process` -> `Message.upgradeSchema` - `Attachment.process` -> `Attachment.upgradeSchema` - `Attachment::processVersion` -> `Attachment::schemaVersion` Document version history. commit 41b92c0a31050ba05ddb1c43171d651f3568b9ac Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:11:50 2018 -0500 Add `operator-linebreak` ESLint rule Based on the following discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r168029106 commit 462defbe55879060fe25bc69103d4429bae2b2f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:01:30 2018 -0500 Add missing `await` for `ConversationController.getOrCreateAndWait` Tested this by setting `if` condition to `true` and confirming it works. It turns rotating a profile key is more involved and might require registering a new account according to Matthew. commit c08058ee4b883b3e23a40683de802ac81ed74874 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 16:32:24 2018 -0500 Convert `FileList` to `Array` commit 70a6c4201925f57be1f94d9da3547fdefc7bbb53 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:46:34 2018 -0500 :art: Fix lint errors commit 2ca7cdbc31d4120d6c6a838a6dcf43bc209d9788 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:07:09 2018 -0500 Skip `autoOrientImage` for non-JPEG images commit 58eac383013c16ca363a4ed33dca5c7ba61284e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:55:35 2018 -0500 Move new-style modules to `window.Signal` namespace commit 02c9328877dce289d6116a18b1c223891bd3cd0b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:35:23 2018 -0500 Extract `npm run test-modules` command commit 2c708eb94fba468b81ea9427734896114f5a7807 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:51 2018 -0500 Extract `Message.process` commit 4a2e52f68a77536a0fa04aa3c29ad3e541a8fa7e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:12 2018 -0500 Fix EditorConfig commit a346bab5db082720f5d47363f06301380e870425 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:13:02 2018 -0500 Remove `vim` directives on ESLint-ed files commit 7ec885c6359e495b407d5bc3eac9431d47c37fc6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:08:24 2018 -0500 Remove CSP whitelisting of `blob:` We no longer use `autoOrientImage` using blob URLs. Bring this back if we decide to auto-orient legacy attachments. commit 879b6f58f4a3f4a9ed6915af6b1be46c1e90e0ca Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:57:05 2018 -0500 Use `Message` type to determine send function Throws on invalid message type. commit 5203d945c98fd2562ae4e22c5c9838d27dec305b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:48 2018 -0500 Whitelist `Whisper` global commit 8ad0b066a3690d3382b86bf6ac00c03df7d1e20b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:32 2018 -0500 Add `Whisper.Types` namespace This avoids namespace collision for `Whisper.Message`. commit 785a949fce2656ca7dcaf0869d6b9e0648114e80 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:55:43 2018 -0500 Add `Message` type commit 674a7357abf0dcc365455695d56c0479998ebf27 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:35:23 2018 -0500 Run ESLint on `Conversation::sendMessage` commit cd985aa700caa80946245b17ea1b856449f152a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:34:38 2018 -0500 Document type signature of `FileInputView::readFile` commit d70d70e52c49588a1dc9833dfe5dd7128e13607f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:31:16 2018 -0500 Move attachment processing closer to sending This helps ensure processing happens uniformly, regardless of which code paths are taken to send an attachment. commit 532ac3e273a26b97f831247f9ee3412621b5c112 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:22:29 2018 -0500 Process attachment before it’s sent Picked this place since it already had various async steps, similar to `onMessageReceived` for the incoming `Attachment.process`. Could we try have this live closer to where we store it in IndexedDB, e.g. `Conversation::sendMessage`? commit a4582ae2fb6e1d3487131ba1f8fa6a00170cb32c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:21:42 2018 -0500 Refactor `getFile` and `getFiles` Lint them using ESLint. commit 07e9114e65046d791fc4f6ed90d6e2e938ad559d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:37:31 2018 -0500 Document incoming and outgoing attachments fields Note how outgoing message attachments only have 4 fields. This presumably means the others are not used in our code and could be discarded for simplicity. commit fdc3ef289d6ec1be344a12d496839d5ba747bb6a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:36:21 2018 -0500 Highlight that `dataURLToBlob` is synchronous commit b9c6bf600fcecedfd649ef2ae3c8629cced4e45a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:35:49 2018 -0500 Add EditorConfig configuration commit e56101e229d56810c8e31ad7289043a152c6c449 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:34:23 2018 -0500 Replace custom with `blob-util` functions IMPORTANT: All of them are async so we need to use `await`, otherwise we get strange or silent errors. commit f95150f6a9569fabcb31f3acd9f6b7bf50b5d145 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:17:30 2018 -0500 Revert "Replace custom functions with `blob-util`" This reverts commit 8a81e9c01bfe80c0e1bf76737092206c06949512. commit 33860d93f3d30ec55c32f3f4a58729df2eb43f0d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:13:02 2018 -0500 Revert "Replace `blueimp-canvas-to-blob` with `blob-util`" This reverts commit 31b3e853e4afc78fe80995921aa4152d9f6e4783. commit 7a0ba6fed622d76a3c39c7f03de541a7edb5b8dd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:12:58 2018 -0500 Replace `blueimp-canvas-to-blob` with `blob-util` commit 47a5f2bfd8b3f546e27e8d2b7e1969755d825a66 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:55:34 2018 -0500 Replace custom functions with `blob-util` commit 1cfa0efdb4fb1265369e2bf243c21f04f044fa01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:47:02 2018 -0500 Add `blob-util` dependency commit 9ac26be1bd783cd5070d886de107dd3ad9c91ad1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:44 2018 -0500 Document why we drop original image data during auto-orient commit 4136d6c382b99f41760a4da519d0db537fa7de8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:27 2018 -0500 Extract `DEFAULT_JPEG_QUALITY` commit 4a7156327eb5f94dba80cb300b344ac591226b0e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:37:11 2018 -0500 Drop support for invalid `image/jpg` MIME type commit 69fe96581f25413194032232f1bf704312e4754c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:30 2018 -0500 Document `window.onInvalidStateError` global commit a48ba1c77458da38583ee9cd488f70a59f6ee0fd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:04 2018 -0500 Selectively run ESLint on `js/background.js` Enabling ESLint on a per function basis allows us to incrementally improve the codebase without requiring large and potentially risky refactorings. commit e6d1cf826befc17ad4ec72fda8e761701665635e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:16:23 2018 -0500 Move async attachment processing to `onMessageReceived` We previously processed attachments in `handleDataMessage` which is mostly a synchronous function, except for the saving of the model. Moving the processing into the already async `onMessageReceived` improves code clarity. commit be6ca2a9aae5b59c360817deb1e18d39d705755e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:49 2018 -0500 Document import of ES2015+ modules commit eaaf7c41608fb988b8f4bbaa933cff110115610e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:29 2018 -0500 :art: Fix lint error commit a25b0e2e3d0f72c6a7bf0a15683f02450d5209ee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:13:57 2018 -0500 :art: Organize `require`s commit e0cc3d8fab6529d01b388acddf8605908c3d236b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:07:17 2018 -0500 Implement attachment process version Instead of keeping track of last normalization (processing) date, we now keep track of an internal processing version that will help us understand what kind of processing has already been completed for a given attachment. This will let us retroactively upgrade existing attachments. As we add more processing steps, we can build a processing pipeline that can convert any attachment processing version into a higher one, e.g. 4 -> 5 -> 6 -> 7. commit ad9083d0fdb880bc518e02251e51a39f7e1c585f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:50:31 2018 -0500 Ignore ES2015+ files during JSCS linting commit 96641205f734927aaebc2342d977c555799c3e3b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:48:07 2018 -0500 Improve ESLint ignore rules Apparently, using unqualified `/**` patterns prevents `!` include patterns. Using qualified glob patterns, e.g. `js/models/**/*.js`, lets us work around this. commit 255e0ab15bd1a0ca8ca5746e42d23977c8765d01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:44:59 2018 -0500 :abc: ESLint ignored files commit ebcb70258a26f234bd602072ac7c0a1913128132 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:47 2018 -0500 Whitelist `browser` environment for ESLint commit 3eaace6f3a21421c5aaaaf01592408c7ed83ecd3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:05 2018 -0500 Use `MIME` module commit ba2cf7770e614415733414a2dcc48f110b929892 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:32:54 2018 -0500 :art: Fix lint errors commit 65acc86e8580e88f7a6611eb4b8fa5d7291f7a3f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:30:42 2018 -0500 Add ES2015+ files to JSHint ignored list commit 8b6494ae6c9247acdfa059a9b361ec5ffcdb39f0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:29:20 2018 -0500 Document potentially unexpected `autoScale` behavior commit 8b4c69b2002d1777d3621be10f92cbf432f9d4d6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:26:47 2018 -0500 Test CommonJS modules separately Not sure how to test them as part of Grunt `unit-tests` task as `test/index.html` doesn’t allow for inclusion of CommonJS modules that use `require`. The tests are silently skipped. commit 213400e4b2bba3efee856a25b40e269221c3c39d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:24:27 2018 -0500 Add `MIME` type module commit 37a726e4fb4b3ed65914463122a5662847b5adee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:18:05 2018 -0500 Return proper `Error` from `blobArrayToBuffer` commit 164752db5612220e4dcf58d57bcd682cb489a399 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:15:41 2018 -0500 :art: Fix ESLint errors commit d498dd79a067c75098dd3179814c914780e5cb4f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:14:33 2018 -0500 Update `Attachment` type field definitions commit 141155a1533ff8fb616b70ea313432781bbebffd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:12:50 2018 -0500 Move `blueimp-canvas-to-blob` from Bower to npm commit 7ccb833e5d286ddd6235d3e491c62ac1e4544510 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:33:50 2018 -0500 :art: Clarify data flow commit e7da41591fde5a830467bebf1b6f51c1f7293e74 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:31:21 2018 -0500 Use `blobUrl` for consistency commit 523a80eefe0e2858aa1fb2bb9539ec44da502963 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:28:06 2018 -0500 Remove just-in-time image auto-orient for lightbox We can bring this back if our users would like auto-orient for old attachments. commit 0739feae9c47dd523c10740d6cdf746d539f270c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:27:21 2018 -0500 Remove just-in-time auto-orient of message attachments We can bring this back if our users would like auto-orient for old attachments. But better yet, we might implement this as database migration. commit ed43c66f92830ee233d5a94d0545eea4da43894d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:26:24 2018 -0500 Auto-orient JPEG attachments upon receipt commit e2eb8e36b017b048d57602fca14e45d657e0e1a1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:25:26 2018 -0500 Expose `Attachment` type through `Whisper.Attachment` commit 9638fbc987b84f143ca34211dc4666d96248ea2f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:39 2018 -0500 Use `contentType` from `model` commit 032c0ced46c3876cb9474b26f9d53d6f1c6b16a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:04 2018 -0500 Return `Error` object for `autoOrientImage` failures commit ff04bad8510c4b21aef350bed2b1887d0e055b98 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:22:32 2018 -0500 Add `options` for `autoOrientImage` output type / quality commit 87745b5586d1e182b51c9f9bc5e4eaf6dbc16722 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:18:46 2018 -0500 Add `Attachment` type Defines various functions on attachments, e.g. normalization (auto-orient JPEGs, etc.) commit de27fdc10a53bc8882a9c978e82265db9ac6d6f5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:16:34 2018 -0500 Add `yarn grunt` shortcut This allows us to use local `grunt-cli` for `grunt dev`. commit 59974db5a5da0d8f4cdc8ce5c4e3c974ecd5e754 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:10:11 2018 -0500 Improve readability commit b5ba96f1e6f40f2e1fa77490c583217768e1f412 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:08:12 2018 -0500 Use `snake_case` for module names Prevents problems across case-sensitive and case-insensitive file systems. We can work around this in the future using a lint rule such as `eslint-plugin-require-path-exists`. See discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r167365931 commit 48c5d3155c96ef628b00d99b52975e580d1d5501 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:05:44 2018 -0500 :art: Use destructuring commit 4822f49f22382a99ebf142b337375f7c25251d76 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:41:40 2018 -0500 Auto-orient images in lightbox view commit 7317110809677dddbbef3fadbf912cdba1c010bf Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:40:14 2018 -0500 Document magic number for escape key commit c790d07389a7d0bbf5298de83dbcfa8be1e7696b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:38:35 2018 -0500 Make second `View` argument an `options` object commit fbe010bb63d0088af9dfe11f153437fab34247e0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:27:40 2018 -0500 Allow `loadImage` to fetch `blob://` URLs commit ec35710d002b019a273eeb48f94dcaf2babe5d96 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:48 2018 -0500 :art: Shorten `autoOrientImage` import commit d07433e3cf316c6a143a0c9393ba26df9e3af17b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:19 2018 -0500 Make `autoOrientImage` module standalone commit c285bf5e33cdf10e0ef71e72cd6f55aef0df96ef Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:55:44 2018 -0500 Replace `loadImage` with `autoOrientImage` commit 44318549235af01fd061c25f557c93fd21cebb7a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:53:23 2018 -0500 Add `autoOrientImage` module This module exposes `loadImage` with a `Promise` based interface and pre- populates `orientation: true` option to auto-orient input. Returns data URL as string. The module uses a named export as refactoring references of modules with `default` (`module.exports`) export references can be error-prone. See: https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html commit c77063afc6366fe49615052796fe46f9b369de39 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:44:30 2018 -0500 Auto-orient preview images See: #998 commit 06dba5eb8f662c11af3a9ba8395bb453ab2e5f8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:43:23 2018 -0500 TODO: Use native `Canvas::toBlob` One challenge is that `Canvas::toBlob` is async whereas `dataURLtoBlob` is sync. commit b15c304a3125dd023fd90990e6225a7303f3596f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:42:45 2018 -0500 Make `null` check strict Appeases JSHint. ESLint has a nice `smart` option for `eqeqeq` rule: https://eslint.org/docs/rules/eqeqeq#smart commit ea70b92d9b18201758e11fdc25b09afc97b50055 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 15:23:58 2018 -0500 Use `Canvas::toDataURL` to preserve `ImageView` logic This way, all the other code paths remain untouched in case we want to remove the auto-orient code once Chrome supports the `image-orientation` CSS property. See: - #998 - https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation commit 62fd744f9f27d951573a68d2cdfe7ba2a3784b41 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:38:04 2018 -0500 Use CSS to constrain auto-oriented images commit f4d3392687168c237441b29140c7968b49dbef9e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:35:02 2018 -0500 Replace `ImageView` `el` with auto-oriented `canvas` See: #998 commit 1602d7f610e4993ad1291f88197f9ead1e25e776 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:48 2018 -0500 Pass `Blob` to `View` (for `ImageView`) This allows us to do JPEG auto-orientation based on EXIF metadata. commit e6a414f2b2a80da1137b839b348a38510efd04bb Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:12 2018 -0500 :hocho: Remove newline commit 5f0d9570d7862fc428ff89c2ecfd332a744537e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:17:02 2018 -0500 Expose `blueimp-load-image` as `window.loadImage` commit 1e1c62fe2f6a76dbcf1998dd468c26187c9871dc Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:16:46 2018 -0500 Add `blueimp-load-image` npm dependency commit ad17fa8a68a21ca5ddec336801b8568009bef3d4 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:14:40 2018 -0500 Remove `blueimp-load-image` Bower dependency
2018-02-21 15:26:59 +00:00
// eslint-disable-next-line func-names
2018-04-27 21:25:04 +00:00
(function() {
window.Whisper = window.Whisper || {};
2018-10-31 23:58:14 +00:00
const SEALED_SENDER = {
UNKNOWN: 0,
ENABLED: 1,
DISABLED: 2,
UNRESTRICTED: 3,
};
2020-09-09 00:56:23 +00:00
const { Services, Util } = window.Signal;
2020-08-07 00:50:54 +00:00
const { Contact, Message } = window.Signal.Types;
const {
deleteAttachmentData,
doesAttachmentExist,
getAbsoluteAttachmentPath,
loadAttachmentData,
readStickerData,
upgradeMessageSchema,
2018-09-21 01:47:19 +00:00
writeNewAttachmentData,
} = window.Signal.Migrations;
const { addStickerPackReference } = window.Signal.Data;
const {
arrayBufferToBase64,
base64ToArrayBuffer,
deriveAccessKey,
getRandomBytes,
stringFromBytes,
verifyAccessKey,
} = window.Signal.Crypto;
const COLORS = [
'red',
'deep_orange',
'brown',
'pink',
'purple',
'indigo',
'blue',
'teal',
'green',
'light_green',
'blue_grey',
'ultramarine',
];
2015-02-08 02:18:53 +00:00
Whisper.Conversation = Backbone.Model.extend({
storeName: 'conversations',
defaults() {
return {
unreadCount: 0,
verified: textsecure.storage.protocol.VerifiedStatus.DEFAULT,
2020-05-27 21:37:06 +00:00
messageCount: 0,
sentMessageCount: 0,
};
},
idForLogging() {
if (this.isPrivate()) {
const uuid = this.get('uuid');
const e164 = this.get('e164');
return `${uuid || e164} (${this.id})`;
}
2020-09-09 02:25:05 +00:00
if (this.get('groupVersion') > 1) {
return `groupv2(${this.get('groupId')})`;
}
2017-09-25 23:41:57 +00:00
const groupId = this.get('groupId');
return `group(${groupId})`;
2017-09-25 23:41:57 +00:00
},
// This is one of the few times that we want to collapse our uuid/e164 pair down into
// just one bit of data. If we have a UUID, we'll send using it.
getSendTarget() {
return this.get('uuid') || this.get('e164');
},
handleMessageError(message, errors) {
this.trigger('messageError', message, errors);
},
getContactCollection() {
const collection = new Backbone.Collection();
const collator = new Intl.Collator();
collection.comparator = (left, right) => {
const leftLower = left.getTitle().toLowerCase();
const rightLower = right.getTitle().toLowerCase();
return collator.compare(leftLower, rightLower);
};
return collection;
},
initialize(attributes) {
if (window.isValidE164(attributes.id)) {
this.set({ id: window.getGuid(), e164: attributes.id });
}
this.ourNumber = textsecure.storage.user.getNumber();
this.ourUuid = textsecure.storage.user.getUuid();
this.verifiedEnum = textsecure.storage.protocol.VerifiedStatus;
2020-05-27 21:37:06 +00:00
this.messageRequestEnum =
textsecure.protobuf.SyncMessage.MessageRequestResponse.Type;
// This may be overridden by ConversationController.getOrCreate, and signify
// our first save to the database. Or first fetch from the database.
this.initialPromise = Promise.resolve();
this.contactCollection = this.getContactCollection();
this.messageCollection = new Whisper.MessageCollection([], {
conversation: this,
});
this.messageCollection.on('change:errors', this.handleMessageError, this);
this.messageCollection.on('send-error', this.onMessageError, this);
this.throttledBumpTyping = _.throttle(this.bumpTyping, 300);
2019-09-26 19:56:31 +00:00
this.debouncedUpdateLastMessage = _.debounce(
this.updateLastMessage.bind(this),
200
);
2020-08-05 01:13:19 +00:00
this.listenTo(
this.messageCollection,
'add remove destroy content-changed',
2019-09-26 19:56:31 +00:00
this.debouncedUpdateLastMessage
);
this.listenTo(this.messageCollection, 'sent', this.updateLastMessage);
this.listenTo(
this.messageCollection,
'send-error',
this.updateLastMessage
);
this.on('newmessage', this.onNewMessage);
this.on('change:profileKey', this.onChangeProfileKey);
// Listening for out-of-band data updates
this.on('delivered', this.updateAndMerge);
this.on('read', this.updateAndMerge);
this.on('expiration-change', this.updateAndMerge);
this.on('expired', this.onExpired);
2018-10-31 23:58:14 +00:00
const sealedSender = this.get('sealedSender');
if (sealedSender === undefined) {
this.set({ sealedSender: SEALED_SENDER.UNKNOWN });
}
this.unset('unidentifiedDelivery');
this.unset('unidentifiedDeliveryUnrestricted');
this.unset('hasFetchedProfile');
this.unset('tokens');
2018-11-14 19:10:32 +00:00
this.typingRefreshTimer = null;
this.typingPauseTimer = null;
2019-01-14 21:49:58 +00:00
// Keep props ready
this.generateProps = () => {
2019-01-14 21:49:58 +00:00
this.cachedProps = this.getProps();
};
this.on('change', this.generateProps);
this.generateProps();
},
isMe() {
const e164 = this.get('e164');
const uuid = this.get('uuid');
return (
(e164 && e164 === this.ourNumber) || (uuid && uuid === this.ourUuid)
);
},
2020-09-04 01:25:19 +00:00
isEverUnregistered() {
return Boolean(this.get('discoveredUnregisteredAt'));
},
isUnregistered() {
const now = Date.now();
const sixHoursAgo = now - 1000 * 60 * 60 * 6;
const discoveredUnregisteredAt = this.get('discoveredUnregisteredAt');
if (discoveredUnregisteredAt && discoveredUnregisteredAt > sixHoursAgo) {
return true;
}
return false;
},
setUnregistered() {
window.log.info(
`Conversation ${this.idForLogging()} is now unregistered`
);
this.set({
discoveredUnregisteredAt: Date.now(),
});
window.Signal.Data.updateConversation(this.attributes);
},
setRegistered() {
window.log.info(
`Conversation ${this.idForLogging()} is registered once again`
);
this.set({
discoveredUnregisteredAt: undefined,
});
window.Signal.Data.updateConversation(this.attributes);
},
2020-05-27 21:37:06 +00:00
isBlocked() {
const uuid = this.get('uuid');
if (uuid) {
return window.storage.isUuidBlocked(uuid);
}
const e164 = this.get('e164');
if (e164) {
return window.storage.isBlocked(e164);
}
const groupId = this.get('groupId');
if (groupId) {
return window.storage.isGroupBlocked(groupId);
}
return false;
},
2020-09-09 00:56:23 +00:00
block({ viaStorageServiceSync = false } = {}) {
let blocked = false;
const isBlocked = this.isBlocked();
const uuid = this.get('uuid');
if (uuid) {
window.storage.addBlockedUuid(uuid);
2020-09-09 00:56:23 +00:00
blocked = true;
}
const e164 = this.get('e164');
if (e164) {
window.storage.addBlockedNumber(e164);
2020-09-09 00:56:23 +00:00
blocked = true;
}
const groupId = this.get('groupId');
if (groupId) {
window.storage.addBlockedGroup(groupId);
2020-09-09 00:56:23 +00:00
blocked = true;
}
if (!viaStorageServiceSync && !isBlocked && blocked) {
this.captureChange();
}
},
2020-09-09 00:56:23 +00:00
unblock({ viaStorageServiceSync = false } = {}) {
let unblocked = false;
const isBlocked = this.isBlocked();
2020-05-27 21:37:06 +00:00
const uuid = this.get('uuid');
if (uuid) {
window.storage.removeBlockedUuid(uuid);
2020-09-09 00:56:23 +00:00
unblocked = true;
2020-05-27 21:37:06 +00:00
}
const e164 = this.get('e164');
if (e164) {
window.storage.removeBlockedNumber(e164);
2020-09-09 00:56:23 +00:00
unblocked = true;
2020-05-27 21:37:06 +00:00
}
const groupId = this.get('groupId');
if (groupId) {
window.storage.removeBlockedGroup(groupId);
2020-09-09 00:56:23 +00:00
unblocked = true;
2020-05-27 21:37:06 +00:00
}
2020-09-09 00:56:23 +00:00
if (!viaStorageServiceSync && isBlocked && unblocked) {
this.captureChange();
}
return unblocked;
2020-05-27 21:37:06 +00:00
},
2020-09-09 00:56:23 +00:00
enableProfileSharing({ viaStorageServiceSync = false } = {}) {
const before = this.get('profileSharing');
2020-05-27 21:37:06 +00:00
this.set({ profileSharing: true });
2020-09-09 00:56:23 +00:00
const after = this.get('profileSharing');
if (!viaStorageServiceSync && Boolean(before) !== Boolean(after)) {
this.captureChange();
}
2020-05-27 21:37:06 +00:00
},
2020-09-09 00:56:23 +00:00
disableProfileSharing({ viaStorageServiceSync = false } = {}) {
const before = this.get('profileSharing');
2020-05-27 21:37:06 +00:00
this.set({ profileSharing: false });
2020-09-09 00:56:23 +00:00
const after = this.get('profileSharing');
if (!viaStorageServiceSync && Boolean(before) !== Boolean(after)) {
this.captureChange();
}
2020-05-27 21:37:06 +00:00
},
2019-08-07 00:40:25 +00:00
hasDraft() {
const draftAttachments = this.get('draftAttachments') || [];
return (
this.get('draft') ||
this.get('quotedMessageId') ||
draftAttachments.length > 0
);
},
getDraftPreview() {
const draft = this.get('draft');
if (draft) {
return draft;
}
const draftAttachments = this.get('draftAttachments') || [];
if (draftAttachments.length > 0) {
return i18n('Conversation--getDraftPreview--attachment');
}
const quotedMessageId = this.get('quotedMessageId');
if (quotedMessageId) {
return i18n('Conversation--getDraftPreview--quote');
}
return i18n('Conversation--getDraftPreview--draft');
},
2018-11-14 19:10:32 +00:00
bumpTyping() {
// We don't send typing messages if the setting is disabled
if (!storage.get('typingIndicators')) {
return;
}
if (!this.typingRefreshTimer) {
const isTyping = true;
this.setTypingRefreshTimer();
this.sendTypingMessage(isTyping);
}
this.setTypingPauseTimer();
},
setTypingRefreshTimer() {
if (this.typingRefreshTimer) {
clearTimeout(this.typingRefreshTimer);
}
this.typingRefreshTimer = setTimeout(
this.onTypingRefreshTimeout.bind(this),
10 * 1000
);
},
onTypingRefreshTimeout() {
const isTyping = true;
this.sendTypingMessage(isTyping);
// This timer will continue to reset itself until the pause timer stops it
this.setTypingRefreshTimer();
},
setTypingPauseTimer() {
if (this.typingPauseTimer) {
clearTimeout(this.typingPauseTimer);
}
this.typingPauseTimer = setTimeout(
this.onTypingPauseTimeout.bind(this),
3 * 1000
);
},
onTypingPauseTimeout() {
const isTyping = false;
this.sendTypingMessage(isTyping);
this.clearTypingTimers();
},
clearTypingTimers() {
if (this.typingPauseTimer) {
clearTimeout(this.typingPauseTimer);
this.typingPauseTimer = null;
}
if (this.typingRefreshTimer) {
clearTimeout(this.typingRefreshTimer);
this.typingRefreshTimer = null;
}
},
2020-09-09 02:25:05 +00:00
async fetchLatestGroupV2Data() {
if (this.get('groupVersion') !== 2) {
return;
}
await window.Signal.Groups.waitThenMaybeUpdateGroup({
conversation: this,
});
},
maybeRepairGroupV2(data) {
if (
this.get('groupVersion') &&
this.get('masterKey') &&
this.get('secretParams') &&
this.get('publicParams')
) {
return;
}
window.log.info(`Repairing GroupV2 conversation ${this.idForLogging()}`);
const { masterKey, secretParams, publicParams } = data;
this.set({ masterKey, secretParams, publicParams, groupVersion: 2 });
window.Signal.Data.updateConversation(this.attributes);
},
getGroupV2Info(groupChange) {
if (this.isPrivate() || this.get('groupVersion') !== 2) {
return null;
}
return {
masterKey: window.Signal.Crypto.base64ToArrayBuffer(
this.get('masterKey')
),
revision: this.get('revision'),
members: this.getRecipients(),
groupChange,
};
},
getGroupV1Info() {
if (this.isPrivate() || this.get('groupVersion') > 0) {
return null;
}
return {
id: this.get('groupId'),
members: this.getRecipients(),
};
},
2018-11-14 19:10:32 +00:00
sendTypingMessage(isTyping) {
if (!textsecure.messaging) {
return;
}
2020-09-09 02:25:05 +00:00
// We don't send typing messages to our other devices
if (this.isMe()) {
return;
}
const recipientId = this.isPrivate() ? this.getSendTarget() : null;
2020-09-09 02:25:05 +00:00
const groupId = !this.isPrivate() ? this.get('groupId') : null;
const groupMembers = this.getRecipients();
2018-11-14 19:10:32 +00:00
2020-09-09 02:25:05 +00:00
// We don't send typing messages if our recipients list is empty
if (!this.isPrivate() && !groupMembers.length) {
return;
}
2020-09-09 02:25:05 +00:00
const sendOptions = this.getSendOptions();
2018-11-14 19:10:32 +00:00
this.wrapSend(
textsecure.messaging.sendTypingMessage(
{
isTyping,
recipientId,
groupId,
2020-09-09 02:25:05 +00:00
groupMembers,
2018-11-14 19:10:32 +00:00
},
sendOptions
)
);
},
async cleanup() {
await window.Signal.Types.Conversation.deleteExternalFiles(
this.attributes,
{
deleteAttachmentData,
}
);
},
async updateAndMerge(message) {
2019-09-26 19:56:31 +00:00
this.debouncedUpdateLastMessage();
const mergeMessage = () => {
const existing = this.messageCollection.get(message.id);
if (!existing) {
return;
}
existing.merge(message.attributes);
};
await this.inProgressFetch;
mergeMessage();
},
async onExpired(message) {
2019-09-26 19:56:31 +00:00
this.debouncedUpdateLastMessage();
const removeMessage = () => {
const { id } = message;
const existing = this.messageCollection.get(id);
if (!existing) {
return;
}
window.log.info('Remove expired message from collection', {
sentAt: existing.get('sent_at'),
});
this.messageCollection.remove(id);
existing.trigger('expired');
existing.cleanup();
2020-05-27 21:37:06 +00:00
// An expired message only counts as decrementing the message count, not
// the sent message count
this.decrementMessageCount();
};
// If a fetch is in progress, then we need to wait until that's complete to
// do this removal. Otherwise we could remove from messageCollection, then
// the async database fetch could include the removed message.
await this.inProgressFetch;
removeMessage();
},
async onNewMessage(message) {
const uuid = message.get ? message.get('sourceUuid') : message.sourceUuid;
const e164 = message.get ? message.get('source') : message.source;
const sourceDevice = message.get
? message.get('sourceDevice')
: message.sourceDevice;
const sourceId = window.ConversationController.ensureContactIds({
uuid,
e164,
});
const typingToken = `${sourceId}.${sourceDevice}`;
2018-11-14 19:10:32 +00:00
// Clear typing indicator for a given contact if we receive a message from them
this.clearContactTypingTimer(typingToken);
2019-09-26 19:56:31 +00:00
this.debouncedUpdateLastMessage();
},
2018-11-14 19:10:32 +00:00
// For outgoing messages, we can call this directly. We're already loaded.
addSingleMessage(message) {
const { id } = message;
const existing = this.messageCollection.get(id);
const model = this.messageCollection.add(message, { merge: true });
model.setToExpire();
if (!existing) {
const { messagesAdded } = window.reduxActions.conversations;
const isNewMessage = true;
messagesAdded(
this.id,
[model.getReduxData()],
isNewMessage,
window.isActive()
);
}
return model;
},
// For incoming messages, they might arrive while we're in the middle of a bulk fetch
// from the database. We'll wait until that is done to process this newly-arrived
// message.
async addIncomingMessage(message) {
await this.inProgressFetch;
this.addSingleMessage(message);
},
format() {
2019-01-14 21:49:58 +00:00
return this.cachedProps;
},
getProps() {
// This is to prevent race conditions on startup; Conversation models are created
// but the full ConversationController.load() sequence isn't complete. So, we
// don't cache props on create, but we do later when load() calls generateProps()
// for us.
if (!window.ConversationController.isFetchComplete()) {
return null;
}
const color = this.getColor();
const typingValues = _.values(this.contactTypingTimers || {});
const typingMostRecent = _.first(_.sortBy(typingValues, 'timestamp'));
const typingContact = typingMostRecent
2020-06-12 22:36:32 +00:00
? ConversationController.get(typingMostRecent.senderId)
: null;
2018-11-14 19:10:32 +00:00
2019-08-07 00:40:25 +00:00
const timestamp = this.get('timestamp');
const draftTimestamp = this.get('draftTimestamp');
const draftPreview = this.getDraftPreview();
const draftText = this.get('draft');
const shouldShowDraft =
this.hasDraft() && draftTimestamp && draftTimestamp >= timestamp;
const inboxPosition = this.get('inbox_position');
2020-05-27 21:37:06 +00:00
const messageRequestsEnabled = Signal.RemoteConfig.isEnabled(
'desktop.messageRequests'
);
2019-08-07 00:40:25 +00:00
const result = {
2019-01-14 21:49:58 +00:00
id: this.id,
uuid: this.get('uuid'),
e164: this.get('e164'),
2020-08-05 01:13:19 +00:00
acceptedMessageRequest: this.getAccepted(),
2019-01-14 21:49:58 +00:00
activeAt: this.get('active_at'),
avatarPath: this.getAvatarPath(),
color,
2019-08-07 00:40:25 +00:00
draftPreview,
draftText,
2020-08-05 01:13:19 +00:00
firstName: this.get('profileName'),
inboxPosition,
isAccepted: this.getAccepted(),
isArchived: this.get('isArchived'),
isBlocked: this.isBlocked(),
isMe: this.isMe(),
isVerified: this.isVerified(),
lastMessage: {
status: this.get('lastMessageStatus'),
text: this.get('lastMessage'),
2020-04-29 21:24:12 +00:00
deletedForEveryone: this.get('lastMessageDeletedForEveryone'),
},
2020-08-05 01:13:19 +00:00
lastUpdated: this.get('timestamp'),
membersCount: this.isPrivate()
? undefined
2020-09-09 02:25:05 +00:00
: (this.get('membersV2') || this.get('members') || []).length,
2020-05-27 21:37:06 +00:00
messageRequestsEnabled,
2020-08-27 19:45:08 +00:00
muteExpiresAt: this.get('muteExpiresAt'),
2020-08-05 01:13:19 +00:00
name: this.get('name'),
phoneNumber: this.getNumber(),
profileName: this.getProfileName(),
sharedGroupNames: this.get('sharedGroupNames'),
shouldShowDraft,
timestamp,
title: this.getTitle(),
type: this.isPrivate() ? 'direct' : 'group',
typingContact: typingContact ? typingContact.format() : null,
unreadCount: this.get('unreadCount') || 0,
};
return result;
},
updateE164(e164) {
const oldValue = this.get('e164');
2020-06-12 22:36:32 +00:00
if (e164 && e164 !== oldValue) {
this.set('e164', e164);
window.Signal.Data.updateConversation(this.attributes);
this.trigger('idUpdated', this, 'e164', oldValue);
}
},
updateUuid(uuid) {
const oldValue = this.get('uuid');
2020-06-12 22:36:32 +00:00
if (uuid && uuid !== oldValue) {
this.set('uuid', uuid.toLowerCase());
window.Signal.Data.updateConversation(this.attributes);
this.trigger('idUpdated', this, 'uuid', oldValue);
}
},
updateGroupId(groupId) {
const oldValue = this.get('groupId');
2020-06-12 22:36:32 +00:00
if (groupId && groupId !== oldValue) {
this.set('groupId', groupId);
window.Signal.Data.updateConversation(this.attributes);
this.trigger('idUpdated', this, 'groupId', oldValue);
}
},
2020-05-27 21:37:06 +00:00
incrementMessageCount() {
this.set({
messageCount: (this.get('messageCount') || 0) + 1,
});
window.Signal.Data.updateConversation(this.attributes);
},
decrementMessageCount() {
this.set({
messageCount: Math.max((this.get('messageCount') || 0) - 1, 0),
});
window.Signal.Data.updateConversation(this.attributes);
},
incrementSentMessageCount() {
this.set({
messageCount: (this.get('messageCount') || 0) + 1,
sentMessageCount: (this.get('sentMessageCount') || 0) + 1,
});
window.Signal.Data.updateConversation(this.attributes);
},
decrementSentMessageCount() {
this.set({
messageCount: Math.max((this.get('messageCount') || 0) - 1, 0),
sentMessageCount: Math.max((this.get('sentMessageCount') || 0) - 1, 0),
});
window.Signal.Data.updateConversation(this.attributes);
},
/**
* This function is called when a message request is accepted in order to
* handle sending read receipts and download any pending attachments.
*/
async handleReadAndDownloadAttachments() {
let messages;
do {
2020-08-07 00:50:54 +00:00
const first = messages ? messages.first() : null;
2020-05-27 21:37:06 +00:00
// eslint-disable-next-line no-await-in-loop
messages = await window.Signal.Data.getOlderMessagesByConversation(
this.get('id'),
{
MessageCollection: Whisper.MessageCollection,
limit: 100,
2020-08-07 00:50:54 +00:00
receivedAt: first ? first.get('received_at') : null,
messageId: first ? first.id : null,
2020-05-27 21:37:06 +00:00
}
);
if (!messages.length) {
return;
}
const readMessages = messages.filter(
m => !m.hasErrors() && m.isIncoming()
);
const receiptSpecs = readMessages.map(m => ({
senderE164: m.get('source'),
senderUuid: m.get('sourceUuid'),
2020-07-24 01:35:32 +00:00
senderId: ConversationController.ensureContactIds({
e164: m.get('source'),
uuid: m.get('sourceUuid'),
}),
2020-05-27 21:37:06 +00:00
timestamp: m.get('sent_at'),
hasErrors: m.hasErrors(),
}));
// eslint-disable-next-line no-await-in-loop
await this.sendReadReceiptsFor(receiptSpecs);
// eslint-disable-next-line no-await-in-loop
await Promise.all(readMessages.map(m => m.queueAttachmentDownloads()));
} while (messages.length > 0);
},
2020-09-09 00:56:23 +00:00
async applyMessageRequestResponse(
response,
{ fromSync = false, viaStorageServiceSync = false } = {}
) {
2020-05-27 21:37:06 +00:00
// Apply message request response locally
this.set({
messageRequestResponseType: response,
});
window.Signal.Data.updateConversation(this.attributes);
if (response === this.messageRequestEnum.ACCEPT) {
2020-09-09 00:56:23 +00:00
this.unblock({ viaStorageServiceSync });
this.enableProfileSharing({ viaStorageServiceSync });
2020-05-27 21:37:06 +00:00
if (!fromSync) {
this.sendProfileKeyUpdate();
2020-05-27 21:37:06 +00:00
// Locally accepted
await this.handleReadAndDownloadAttachments();
}
} else if (response === this.messageRequestEnum.BLOCK) {
// Block locally, other devices should block upon receiving the sync message
2020-09-09 00:56:23 +00:00
this.block({ viaStorageServiceSync });
this.disableProfileSharing({ viaStorageServiceSync });
2020-05-27 21:37:06 +00:00
} else if (response === this.messageRequestEnum.DELETE) {
// Delete messages locally, other devices should delete upon receiving
// the sync message
this.destroyMessages();
2020-09-09 00:56:23 +00:00
this.disableProfileSharing({ viaStorageServiceSync });
2020-05-27 21:37:06 +00:00
this.updateLastMessage();
if (!fromSync) {
this.trigger('unload', 'deleted from message request');
}
} else if (response === this.messageRequestEnum.BLOCK_AND_DELETE) {
// Delete messages locally, other devices should delete upon receiving
// the sync message
this.destroyMessages();
2020-09-09 00:56:23 +00:00
this.disableProfileSharing({ viaStorageServiceSync });
2020-05-27 21:37:06 +00:00
this.updateLastMessage();
// Block locally, other devices should block upon receiving the sync message
2020-09-09 00:56:23 +00:00
this.block({ viaStorageServiceSync });
2020-05-27 21:37:06 +00:00
// Leave group if this was a local action
if (!fromSync) {
this.leaveGroup();
this.trigger('unload', 'blocked and deleted from message request');
}
}
},
async syncMessageRequestResponse(response) {
// Let this run, no await
this.applyMessageRequestResponse(response);
const { ourNumber, ourUuid } = this;
const { wrap, sendOptions } = ConversationController.prepareForSend(
ourNumber || ourUuid,
{
syncMessage: true,
}
);
await wrap(
textsecure.messaging.syncMessageRequestResponse(
{
threadE164: this.get('e164'),
threadUuid: this.get('uuid'),
groupId: this.get('groupId'),
type: response,
},
sendOptions
)
);
},
onMessageError() {
this.updateVerified();
},
safeGetVerified() {
const promise = textsecure.storage.protocol.getVerified(this.id);
2018-04-27 21:25:04 +00:00
return promise.catch(
() => textsecure.storage.protocol.VerifiedStatus.DEFAULT
);
},
2018-09-21 01:47:19 +00:00
async updateVerified() {
if (this.isPrivate()) {
2018-09-21 01:47:19 +00:00
await this.initialPromise;
const verified = await this.safeGetVerified();
2019-09-06 19:16:54 +00:00
if (this.get('verified') !== verified) {
this.set({ verified });
window.Signal.Data.updateConversation(this.attributes);
2019-09-06 19:16:54 +00:00
}
2018-09-21 01:47:19 +00:00
return;
}
2020-09-09 02:25:05 +00:00
this.fetchContacts();
2018-09-21 01:47:19 +00:00
await Promise.all(
this.contactCollection.map(async contact => {
if (!contact.isMe()) {
await contact.updateVerified();
}
})
);
this.onMemberVerifiedChange();
},
setVerifiedDefault(options) {
const { DEFAULT } = this.verifiedEnum;
return this.queueJob(() => this._setVerified(DEFAULT, options));
},
setVerified(options) {
const { VERIFIED } = this.verifiedEnum;
return this.queueJob(() => this._setVerified(VERIFIED, options));
},
setUnverified(options) {
const { UNVERIFIED } = this.verifiedEnum;
return this.queueJob(() => this._setVerified(UNVERIFIED, options));
},
2018-09-21 01:47:19 +00:00
async _setVerified(verified, providedOptions) {
const options = providedOptions || {};
2018-04-27 21:25:04 +00:00
_.defaults(options, {
2020-09-09 00:56:23 +00:00
viaStorageServiceSync: false,
2018-04-27 21:25:04 +00:00
viaSyncMessage: false,
viaContactSync: false,
key: null,
});
2018-04-27 21:25:04 +00:00
const { VERIFIED, UNVERIFIED } = this.verifiedEnum;
if (!this.isPrivate()) {
2018-04-27 21:25:04 +00:00
throw new Error(
'You cannot verify a group conversation. ' +
'You must verify individual contacts.'
);
}
const beginningVerified = this.get('verified');
2018-09-21 01:47:19 +00:00
let keyChange;
if (options.viaSyncMessage) {
// handle the incoming key from the sync messages - need different
// behavior if that key doesn't match the current key
2018-09-21 01:47:19 +00:00
keyChange = await textsecure.storage.protocol.processVerifiedMessage(
this.id,
verified,
options.key
);
} else {
2018-09-21 01:47:19 +00:00
keyChange = await textsecure.storage.protocol.setVerified(
this.id,
verified
);
}
2018-09-21 01:47:19 +00:00
this.set({ verified });
window.Signal.Data.updateConversation(this.attributes);
2018-09-21 01:47:19 +00:00
2020-09-09 00:56:23 +00:00
if (
!options.viaStorageServiceSync &&
!keyChange &&
beginningVerified !== verified
) {
this.captureChange();
}
2018-09-21 01:47:19 +00:00
// Three situations result in a verification notice in the conversation:
// 1) The message came from an explicit verification in another client (not
// a contact sync)
// 2) The verification value received by the contact sync is different
// from what we have on record (and it's not a transition to UNVERIFIED)
// 3) Our local verification status is VERIFIED and it hasn't changed,
// but the key did change (Key1/VERIFIED to Key2/VERIFIED - but we don't
// want to show DEFAULT->DEFAULT or UNVERIFIED->UNVERIFIED)
if (
!options.viaContactSync ||
(beginningVerified !== verified && verified !== UNVERIFIED) ||
(keyChange && verified === VERIFIED)
) {
await this.addVerifiedChange(this.id, verified === VERIFIED, {
local: !options.viaSyncMessage,
2018-04-27 21:25:04 +00:00
});
2018-09-21 01:47:19 +00:00
}
if (!options.viaSyncMessage) {
await this.sendVerifySyncMessage(
this.get('e164'),
this.get('uuid'),
verified
);
2018-09-21 01:47:19 +00:00
}
return keyChange;
},
sendVerifySyncMessage(e164, uuid, state) {
// Because syncVerification sends a (null) message to the target of the verify and
// a sync message to our own devices, we need to send the accessKeys down for both
// contacts. So we merge their sendOptions.
const { sendOptions } = ConversationController.prepareForSend(
this.ourNumber || this.ourUuid,
2018-11-07 19:20:43 +00:00
{ syncMessage: true }
);
2018-11-07 19:20:43 +00:00
const contactSendOptions = this.getSendOptions();
2020-09-09 00:46:29 +00:00
const options = { ...sendOptions, ...contactSendOptions };
const promise = textsecure.storage.protocol.loadIdentityKey(e164);
2018-04-27 21:25:04 +00:00
return promise.then(key =>
this.wrapSend(
textsecure.messaging.syncVerification(e164, uuid, state, key, options)
)
2018-04-27 21:25:04 +00:00
);
},
isVerified() {
if (this.isPrivate()) {
return this.get('verified') === this.verifiedEnum.VERIFIED;
}
if (!this.contactCollection.length) {
return false;
}
2018-04-27 21:25:04 +00:00
return this.contactCollection.every(contact => {
if (contact.isMe()) {
return true;
}
return contact.isVerified();
});
},
isUnverified() {
if (this.isPrivate()) {
const verified = this.get('verified');
2018-04-27 21:25:04 +00:00
return (
verified !== this.verifiedEnum.VERIFIED &&
verified !== this.verifiedEnum.DEFAULT
);
}
if (!this.contactCollection.length) {
return true;
}
2018-04-27 21:25:04 +00:00
return this.contactCollection.any(contact => {
if (contact.isMe()) {
return false;
}
return contact.isUnverified();
});
},
getUnverified() {
if (this.isPrivate()) {
return this.isUnverified()
? new Backbone.Collection([this])
: new Backbone.Collection();
}
2018-04-27 21:25:04 +00:00
return new Backbone.Collection(
this.contactCollection.filter(contact => {
if (contact.isMe()) {
return false;
}
return contact.isUnverified();
})
);
},
setApproved() {
if (!this.isPrivate()) {
2018-04-27 21:25:04 +00:00
throw new Error(
'You cannot set a group conversation as trusted. ' +
'You must set individual contacts as trusted.'
);
}
return textsecure.storage.protocol.setApproval(this.id, true);
},
safeIsUntrusted() {
2018-04-27 21:25:04 +00:00
return textsecure.storage.protocol
.isUntrusted(this.id)
.catch(() => false);
},
isUntrusted() {
if (this.isPrivate()) {
return this.safeIsUntrusted();
}
if (!this.contactCollection.length) {
return Promise.resolve(false);
}
2018-04-27 21:25:04 +00:00
return Promise.all(
this.contactCollection.map(contact => {
if (contact.isMe()) {
return false;
}
return contact.safeIsUntrusted();
})
).then(results => _.any(results, result => result));
},
getUntrusted() {
// This is a bit ugly because isUntrusted() is async. Could do the work to cache
// it locally, but we really only need it for this call.
if (this.isPrivate()) {
2018-04-27 21:25:04 +00:00
return this.isUntrusted().then(untrusted => {
if (untrusted) {
return new Backbone.Collection([this]);
}
return new Backbone.Collection();
});
}
2018-04-27 21:25:04 +00:00
return Promise.all(
this.contactCollection.map(contact => {
if (contact.isMe()) {
return [false, contact];
}
return Promise.all([contact.isUntrusted(), contact]);
})
).then(results => {
const filtered = _.filter(results, result => {
const untrusted = result[0];
return untrusted;
});
2018-04-27 21:25:04 +00:00
return new Backbone.Collection(
_.map(filtered, result => {
const contact = result[1];
return contact;
})
);
});
},
2020-05-27 21:37:06 +00:00
getSentMessageCount() {
return this.get('sentMessageCount') || 0;
},
getMessageRequestResponseType() {
return this.get('messageRequestResponseType') || 0;
},
/**
* Determine if this conversation should be considered "accepted" in terms
* of message requests
*/
getAccepted() {
const messageRequestsEnabled = Signal.RemoteConfig.isEnabled(
'desktop.messageRequests'
);
if (!messageRequestsEnabled) {
return true;
}
if (this.isMe()) {
return true;
}
if (
this.getMessageRequestResponseType() === this.messageRequestEnum.ACCEPT
) {
return true;
}
const isFromOrAddedByTrustedContact = this.isFromOrAddedByTrustedContact();
2020-05-27 21:37:06 +00:00
const hasSentMessages = this.getSentMessageCount() > 0;
const hasMessagesBeforeMessageRequests =
(this.get('messageCountBeforeMessageRequests') || 0) > 0;
const hasNoMessages = (this.get('messageCount') || 0) === 0;
2020-08-07 00:50:54 +00:00
const isEmptyPrivateConvo = hasNoMessages && this.isPrivate();
const isEmptyWhitelistedGroup =
hasNoMessages && !this.isPrivate() && this.get('profileSharing');
2020-05-27 21:37:06 +00:00
return (
isFromOrAddedByTrustedContact ||
2020-05-27 21:37:06 +00:00
hasSentMessages ||
hasMessagesBeforeMessageRequests ||
2020-08-07 00:50:54 +00:00
// an empty group is the scenario where we need to rely on
// whether the profile has already been shared or not
2020-08-07 00:50:54 +00:00
isEmptyPrivateConvo ||
isEmptyWhitelistedGroup
2020-05-27 21:37:06 +00:00
);
},
onMemberVerifiedChange() {
// If the verified state of a member changes, our aggregate state changes.
// We trigger both events to replicate the behavior of Backbone.Model.set()
2019-01-14 21:49:58 +00:00
this.trigger('change:verified', this);
this.trigger('change', this);
},
toggleVerified() {
if (this.isVerified()) {
return this.setVerifiedDefault();
}
return this.setVerified();
},
async addKeyChange(keyChangedId) {
window.log.info(
'adding key change advisory for',
this.idForLogging(),
keyChangedId,
this.get('timestamp')
);
const timestamp = Date.now();
const message = {
conversationId: this.id,
type: 'keychange',
sent_at: this.get('timestamp'),
received_at: timestamp,
key_changed: keyChangedId,
unread: 1,
};
const id = await window.Signal.Data.saveMessage(message, {
Message: Whisper.Message,
});
const model = MessageController.register(
id,
new Whisper.Message({
...message,
id,
})
);
this.trigger('newmessage', model);
},
async addVerifiedChange(verifiedChangeId, verified, providedOptions) {
const options = providedOptions || {};
_.defaults(options, { local: true });
if (this.isMe()) {
window.log.info(
2018-04-27 21:25:04 +00:00
'refusing to add verified change advisory for our own number'
);
return;
}
const lastMessage = this.get('timestamp') || Date.now();
window.log.info(
'adding verified change advisory for',
this.idForLogging(),
verifiedChangeId,
lastMessage
);
const timestamp = Date.now();
const message = {
conversationId: this.id,
type: 'verified-change',
sent_at: lastMessage,
received_at: timestamp,
verifiedChanged: verifiedChangeId,
verified,
local: options.local,
unread: 1,
};
const id = await window.Signal.Data.saveMessage(message, {
Message: Whisper.Message,
});
const model = MessageController.register(
id,
new Whisper.Message({
...message,
id,
})
);
2017-09-25 23:41:57 +00:00
this.trigger('newmessage', model);
if (this.isPrivate()) {
2018-09-21 01:47:19 +00:00
ConversationController.getAllGroupsInvolvingId(this.id).then(groups => {
2018-04-27 21:25:04 +00:00
_.forEach(groups, group => {
2018-09-21 01:47:19 +00:00
group.addVerifiedChange(this.id, verified, options);
});
});
}
},
2020-06-04 18:16:19 +00:00
async addCallHistory(callHistoryDetails) {
const { acceptedTime, endedTime, wasDeclined } = callHistoryDetails;
const message = {
conversationId: this.id,
type: 'call-history',
sent_at: endedTime,
received_at: endedTime,
unread: !wasDeclined && !acceptedTime,
callHistoryDetails,
};
const id = await window.Signal.Data.saveMessage(message, {
Message: Whisper.Message,
});
const model = MessageController.register(
id,
new Whisper.Message({
...message,
id,
})
);
this.trigger('newmessage', model);
},
async addProfileChange(profileChange, conversationId) {
const message = {
conversationId: this.id,
type: 'profile-change',
sent_at: Date.now(),
received_at: Date.now(),
unread: true,
changedId: conversationId || this.id,
profileChange,
};
const id = await window.Signal.Data.saveMessage(message, {
Message: Whisper.Message,
});
const model = MessageController.register(
id,
new Whisper.Message({
...message,
id,
})
);
2020-06-04 18:16:19 +00:00
this.trigger('newmessage', model);
if (this.isPrivate()) {
ConversationController.getAllGroupsInvolvingId(this.id).then(groups => {
_.forEach(groups, group => {
group.addProfileChange(profileChange, this.id);
});
});
}
2020-06-04 18:16:19 +00:00
},
async onReadMessage(message, readAt) {
// We mark as read everything older than this message - to clean up old stuff
// still marked unread in the database. If the user generally doesn't read in
// the desktop app, so the desktop app only gets read syncs, we can very
// easily end up with messages never marked as read (our previous early read
// sync handling, read syncs never sent because app was offline)
// We queue it because we often get a whole lot of read syncs at once, and
// their markRead calls could very easily overlap given the async pull from DB.
// Lastly, we don't send read syncs for any message marked read due to a read
// sync. That's a notification explosion we don't need.
2018-04-27 21:25:04 +00:00
return this.queueJob(() =>
this.markRead(message.get('received_at'), {
sendReadReceipts: false,
readAt,
})
2018-04-27 21:25:04 +00:00
);
},
getUnread() {
return window.Signal.Data.getUnreadByConversation(this.id, {
MessageCollection: Whisper.MessageCollection,
});
},
validate(attributes = this.attributes) {
const required = ['type'];
const missing = _.filter(required, attr => !attributes[attr]);
2018-04-27 21:25:04 +00:00
if (missing.length) {
return `Conversation must have ${missing}`;
}
if (attributes.type !== 'private' && attributes.type !== 'group') {
return `Invalid conversation type: ${attributes.type}`;
}
const atLeastOneOf = ['e164', 'uuid', 'groupId'];
const hasAtLeastOneOf =
_.filter(atLeastOneOf, attr => attributes[attr]).length > 0;
if (!hasAtLeastOneOf) {
return 'Missing one of e164, uuid, or groupId';
}
const error = this.validateNumber() || this.validateUuid();
if (error) {
return error;
}
return null;
},
validateNumber() {
if (this.isPrivate() && this.get('e164')) {
const regionCode = storage.get('regionCode');
const number = libphonenumber.util.parseNumber(
this.get('e164'),
regionCode
);
if (number.isValidNumber) {
this.set({ e164: number.e164 });
return null;
2015-02-08 00:24:56 +00:00
}
return number.error || 'Invalid phone number';
}
return null;
},
validateUuid() {
if (this.isPrivate() && this.get('uuid')) {
if (window.isValidGuid(this.get('uuid'))) {
return null;
}
return 'Invalid UUID';
}
return null;
},
queueJob(callback) {
2019-07-09 18:46:48 +00:00
this.jobQueue = this.jobQueue || new window.PQueue({ concurrency: 1 });
const taskWithTimeout = textsecure.createTaskWithTimeout(
callback,
`conversation ${this.idForLogging()}`
);
2019-07-09 18:46:48 +00:00
return this.jobQueue.add(taskWithTimeout);
},
2020-09-09 02:25:05 +00:00
getMembers() {
if (this.isPrivate()) {
2020-09-09 02:25:05 +00:00
return [this];
}
if (this.get('membersV2')) {
return _.compact(
this.get('membersV2').map(member => {
const c = ConversationController.get(member.conversationId);
// In groups we won't sent to contacts we believe are unregistered
if (c && c.isUnregistered()) {
return null;
}
return c;
})
);
}
if (this.get('members')) {
return _.compact(
this.get('members').map(id => {
const c = ConversationController.get(id);
// In groups we won't sent to contacts we believe are unregistered
if (c && c.isUnregistered()) {
return null;
}
return c;
})
);
}
2020-09-09 02:25:05 +00:00
window.log.warn(
'getMembers: Group conversation had neither membersV2 nor members'
);
return [];
},
getMemberIds() {
const members = this.getMembers();
return members.map(member => member.id);
},
getRecipients() {
const members = this.getMembers();
// Eliminate our
return _.compact(
2020-09-09 02:25:05 +00:00
members.map(member => (member.isMe() ? null : member.getSendTarget()))
);
Feature: Blue check marks for read messages if opted in (#1489) * Refactor delivery receipt event handler * Rename the delivery receipt event For less ambiguity with read receipts. * Rename synced read event For less ambiguity with read receipts from other Signal users. * Add support for incoming receipt messages Handle ReceiptMessages, which may include encrypted delivery receipts or read receipts from recipients of our sent messages. // FREEBIE * Rename ReadReceipts to ReadSyncs * Render read messages with blue double checks * Send read receipts to senders of incoming messages // FREEBIE * Move ReadSyncs to their own file // FREEBIE * Fixup old comments on read receipts (now read syncs) And some variable renaming for extra clarity. // FREEBIE * Add global setting for read receipts Don't send read receipt messages unless the setting is enabled. Don't process read receipts if the setting is disabled. // FREEBIE * Sync read receipt setting from mobile Toggling this setting on your mobile device should sync it to Desktop. When linking, use the setting in the provisioning message. // FREEBIE * Send receipt messages silently Avoid generating phantom messages on ios // FREEBIE * Save recipients on the outgoing message models For accurate tracking and display of sent/delivered/read state, even if group membership changes later. // FREEBIE * Fix conversation type in profile key update handling // FREEBIE * Set recipients on synced sent messages * Render saved recipients in message detail if available For older messages, where we did not save the intended set of recipients at the time of sending, fall back to the current group membership. // FREEBIE * Record who has been successfully sent to // FREEBIE * Record who a message has been delivered to * Invert the not-clickable class * Fix readReceipt setting sync when linking * Render per recipient sent/delivered/read status In the message detail view for outgoing messages, render each recipient's individual sent/delivered/read status with respect to this message, as long as there are no errors associated with the recipient (ie, safety number changes, user not registered, etc...) since the error icon is displayed in that case. *Messages sent before this change may not have per-recipient status lists and will simply show no status icon. // FREEBIE * Add configuration sync request Send these requests in a one-off fashion when: 1. We have just setup from a chrome app import 2. We have just upgraded to read-receipt support // FREEBIE * Expose sendRequestConfigurationSyncMessage // FREEBIE * Fix handling of incoming delivery receipts - union with array FREEBIE
2017-10-04 22:28:43 +00:00
},
async getQuoteAttachment(attachments, preview, sticker) {
if (attachments && attachments.length) {
return Promise.all(
attachments
2019-01-16 03:03:56 +00:00
.filter(
attachment =>
attachment &&
attachment.contentType &&
!attachment.pending &&
!attachment.error
2019-01-16 03:03:56 +00:00
)
.slice(0, 1)
2019-01-16 03:03:56 +00:00
.map(async attachment => {
const { fileName, thumbnail, contentType } = attachment;
2019-01-16 03:03:56 +00:00
return {
contentType,
// Our protos library complains about this field being undefined, so we
// force it to null
fileName: fileName || null,
thumbnail: thumbnail
? {
...(await loadAttachmentData(thumbnail)),
objectUrl: getAbsoluteAttachmentPath(thumbnail.path),
}
: null,
};
})
);
}
if (preview && preview.length) {
return Promise.all(
preview
.filter(item => item && item.image)
.slice(0, 1)
.map(async attachment => {
const { image } = attachment;
const { contentType } = image;
return {
contentType,
// Our protos library complains about this field being undefined, so we
// force it to null
fileName: null,
thumbnail: image
? {
...(await loadAttachmentData(image)),
objectUrl: getAbsoluteAttachmentPath(image.path),
}
: null,
};
})
);
}
if (sticker && sticker.data && sticker.data.path) {
const { path, contentType } = sticker.data;
return [
{
contentType,
// Our protos library complains about this field being undefined, so we
// force it to null
fileName: null,
thumbnail: {
...(await loadAttachmentData(sticker.data)),
objectUrl: getAbsoluteAttachmentPath(path),
},
},
];
}
return [];
},
async makeQuote(quotedMessage) {
const { getName } = Contact;
const contact = quotedMessage.getContact();
const attachments = quotedMessage.get('attachments');
const preview = quotedMessage.get('preview');
const sticker = quotedMessage.get('sticker');
const body = quotedMessage.get('body');
const embeddedContact = quotedMessage.get('contact');
const embeddedContactName =
embeddedContact && embeddedContact.length > 0
? getName(embeddedContact[0])
: '';
return {
author: contact.get('e164'),
authorUuid: contact.get('uuid'),
id: quotedMessage.get('sent_at'),
text: body || embeddedContactName,
2019-06-26 19:33:13 +00:00
attachments: quotedMessage.isTapToView()
? [{ contentType: 'image/jpeg', fileName: null }]
: await this.getQuoteAttachment(attachments, preview, sticker),
};
},
async sendStickerMessage(packId, stickerId) {
const packData = window.Signal.Stickers.getStickerPack(packId);
const stickerData = window.Signal.Stickers.getSticker(packId, stickerId);
if (!stickerData || !packData) {
window.log.warn(
`Attempted to send nonexistent (${packId}, ${stickerId}) sticker!`
);
return;
}
const { key } = packData;
const { path, width, height } = stickerData;
const arrayBuffer = await readStickerData(path);
const sticker = {
packId,
stickerId,
packKey: key,
data: {
size: arrayBuffer.byteLength,
data: arrayBuffer,
contentType: 'image/webp',
width,
height,
},
};
this.sendMessage(null, [], null, [], sticker);
window.reduxActions.stickers.useSticker(packId, stickerId);
},
2020-01-23 23:57:37 +00:00
/**
* Sends a reaction message
* @param {object} reaction - The reaction to send
* @param {string} reaction.emoji - The emoji to react with
* @param {boolean} [reaction.remove] - Set to `true` if we are removing a
* reaction with the given emoji
* @param {object} target - The target of the reaction
* @param {string} [target.targetAuthorE164] - The E164 address of the target
* message's author
* @param {string} [target.targetAuthorUuid] - The UUID address of the target
2020-01-23 23:57:37 +00:00
* message's author
* @param {number} target.targetTimestamp - The sent_at timestamp of the
* target message
*/
async sendReactionMessage(reaction, target) {
const timestamp = Date.now();
const outgoingReaction = { ...reaction, ...target };
const expireTimer = this.get('expireTimer');
2020-01-23 23:57:37 +00:00
const reactionModel = Whisper.Reactions.add({
...outgoingReaction,
fromId: ConversationController.getOurConversationId(),
2020-01-23 23:57:37 +00:00
timestamp,
fromSync: true,
});
Whisper.Reactions.onReaction(reactionModel);
2020-09-04 01:25:19 +00:00
const destination = this.getSendTarget();
2020-01-23 23:57:37 +00:00
const recipients = this.getRecipients();
let profileKey;
if (this.get('profileSharing')) {
profileKey = storage.get('profileKey');
}
return this.queueJob(async () => {
window.log.info(
'Sending reaction to conversation',
this.idForLogging(),
'with timestamp',
timestamp
);
const attributes = {
id: window.getGuid(),
type: 'outgoing',
conversationId: this.get('id'),
2020-01-23 23:57:37 +00:00
sent_at: timestamp,
received_at: timestamp,
recipients,
reaction: outgoingReaction,
};
if (this.isPrivate()) {
attributes.destination = destination;
}
// We are only creating this model so we can use its sync message
// sending functionality. It will not be saved to the datbase.
const message = new Whisper.Message(attributes);
// We're offline!
if (!textsecure.messaging) {
throw new Error('Cannot send reaction while offline!');
}
// Special-case the self-send case - we send only a sync message
if (this.isMe()) {
const dataMessage = await textsecure.messaging.getMessageProto(
destination,
2020-09-09 02:25:05 +00:00
null, // body
null, // attachments
null, // quote
null, // preview
null, // sticker
2020-01-23 23:57:37 +00:00
outgoingReaction,
timestamp,
expireTimer,
2020-01-23 23:57:37 +00:00
profileKey
);
return message.sendSyncMessageOnly(dataMessage);
}
const options = this.getSendOptions();
const promise = (() => {
if (this.isPrivate()) {
return textsecure.messaging.sendMessageToIdentifier(
2020-01-23 23:57:37 +00:00
destination,
2020-09-09 02:25:05 +00:00
null, // body
null, // attachments
null, // quote
null, // preview
null, // sticker
2020-01-23 23:57:37 +00:00
outgoingReaction,
timestamp,
expireTimer,
2020-01-23 23:57:37 +00:00
profileKey,
options
);
}
return textsecure.messaging.sendMessageToGroup(
2020-09-09 02:25:05 +00:00
{
groupV1: this.getGroupV1Info(),
groupV2: this.getGroupV2Info(),
reaction: outgoingReaction,
timestamp,
expireTimer,
profileKey,
},
2020-01-23 23:57:37 +00:00
options
);
})();
return message.send(this.wrapSend(promise));
}).catch(error => {
window.log.error('Error sending reaction', reaction, target, error);
const reverseReaction = reactionModel.clone();
reverseReaction.set('remove', !reverseReaction.get('remove'));
Whisper.Reactions.onReaction(reverseReaction);
throw error;
});
},
2020-05-27 21:37:06 +00:00
async sendProfileKeyUpdate() {
const id = this.get('id');
const recipients = this.getRecipients();
2020-05-27 21:37:06 +00:00
if (!this.get('profileSharing')) {
window.log.error(
'Attempted to send profileKeyUpdate to conversation without profileSharing enabled',
id,
recipients
);
return;
}
window.log.info(
'Sending profileKeyUpdate to conversation',
id,
recipients
);
const profileKey = storage.get('profileKey');
await textsecure.messaging.sendProfileKeyUpdate(
profileKey,
recipients,
this.getSendOptions(),
this.get('groupId')
);
},
sendMessage(body, attachments, quote, preview, sticker) {
2018-11-14 19:10:32 +00:00
this.clearTypingTimers();
const { clearUnreadMetrics } = window.reduxActions.conversations;
clearUnreadMetrics(this.id);
const destination = this.getSendTarget();
const expireTimer = this.get('expireTimer');
const recipients = this.getRecipients();
let profileKey;
if (this.get('profileSharing')) {
profileKey = storage.get('profileKey');
}
Auto-orient image attachments based on EXIF metadata As described in #998, images are sometimes displayed with an incorrect orientation. This is because cameras often write files in the native sensor byte order and attach the `Orientation` EXIF metadata to tell end-user devices how to display the images based on the original author’s capture orientation. Electron/Chromium (and therefore Signal Desktop) currently doesn’t support applying this metadata for `<img>` tags, e.g. CSS `image-orientation: from- image`. As a workaround, this change uses the `loadImage` library with the `orientation: true` flag to auto-orient images ~~before display~~ upon receipt and before sending. **Changes** - [x] ~~Auto-orient images during display in message list view~~ - [x] Ensure image is not displayed until loaded (to prevent layout reflow) . - [x] Auto-orient images upon receipt and before storing in IndexedDB (~~or preserve original data until Chromium offers native fix?~~) - [x] Auto-orient images in compose area preview. - [x] ~~Auto-orient images in lightbox view~~ - [x] Auto-orient images before sending / storage. - [x] Add EditorConfig for sharing code styles across editors. - [x] Fix ESLint ignore file. - [x] Change `function-paren-newline` ESLint rule from `consistent` to `multiline`. - [x] Add `operator-linebreak` ESLint rule for consistency. - [x] Added `blob-util` dependency for converting between array buffers, blobs, etc. - [x] Extracted `createMessageHandler` to consolidate logic for `onMessageReceived` and `onSentMessage`. - [x] Introduce `async` / `await` to simplify async coding (restore control flow for branching, loops, and exceptions). - [x] Introduce `window.Signal` namespace for exposing ES2015+ CommonJS modules. - [x] Introduce rudimentary `Message` and `Attachment` types to begin defining a schema and versioning. This will allow us to track which changes, e.g. auto-orient JPEGs, per message / attachment as well as which fields are stored. - [x] Renamed `window.dataURLtoBlob` to `window.dataURLToBlobSync` to both fix the strange `camelCase` as well as to highlight that this operation is synchronous and therefore blocks the user thread. - [x] Normalize all JPEG MIME types to `image/jpeg`, eliminating the invalid `image/jpg`. - [x] Add `npm run test-modules` command for testing non-browser specific CommonJS modules. - **Stretch Goals** - [ ] ~~Restrict `autoOrientImage` to `Blob` to narrow API interface.~~ Do this once we use PureScript. - [ ] ~~Return non-JPEGs as no-op from `autoOrientImage`.~~ Skipping `autoOrientImage` for non-JPEGs altogether. - [ ] Retroactively auto-orient existing JPEG image attachments in the background. --- Fixes #998 --- - **Blog:** EXIF Orientation Handling Is a Ghetto: https://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ - **Chromium Bug:** EXIF orientation is ignored: https://bugs.chromium.org/p/chromium/issues/detail?id=56845 - **Chromium Bug:** Support for the CSS image-orientation CSS property: https://bugs.chromium.org/p/chromium/issues/detail?id=158753 --- commit ce5090b473a2448229dc38e4c3f15d7ad0137714 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 16 10:35:36 2018 -0500 Inline message descriptors commit 329036e59c138c1e950ec7c654eebd7d87076de5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:34:40 2018 -0500 Clarify order of operations Semantically, it makes more sense to do `getFile` before `clearForm` even though it seems to work either way. commit f9d4cfb2ba0d8aa308b0923bbe6066ea34cb97bd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:18:26 2018 -0500 Simplify `operator-linebreak` configuration Enabling `before` caused more code changes and it turns out our previous configuration is already the default. commit db588997acdd90ed2ad829174ecbba744383c78b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:15:59 2018 -0500 Remove obsolete TODO commit 799c8817633f6afa0b731fc3b5434e463bd850e3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:12:18 2018 -0500 Enable ESLint `function-paren-newline` `multiline` Per discussion. commit b660b6bc8ef41df7601a411213d6cda80821df87 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:10:48 2018 -0500 Use `messageDescriptor.id` not `source` commit 5e7309d176f4a7e97d3dc4c738e6b0ccd4792871 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:29:01 2018 -0500 Remove unnecessary `eslint-env` commit 393b3da55eabd7413596c86cc3971b063a0efe31 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:19:17 2018 -0500 Refactor `onSentMessage` and `onMessageReceived` Since they are so similar, we create the handlers using new `createMessageHandler` function. This allows us to ensure both synced and received messages go through schema upgrade pipeline. commit b3db0bf179c9a5bea96480cde28c6fa7193ac117 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:18:21 2018 -0500 Add `Message` descriptor functions commit 8febf125b1b42fe4ae1888dd50fcee2749dc1ff0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 14:46:56 2018 -0500 Fix typo commit 98d951ef77bd578b313a4ff4b496b793e82e88d5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:39 2018 -0500 Remove `promises` reference commit a0e9559ed5bed947dabf28cb672e63d39948d854 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:13 2018 -0500 Fix `AttachmentView::mediaType` fall-through commit 67be916a83951b8a1f9b22efe78a6da6b1825f38 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:03:41 2018 -0500 Remove minor TODOs commit 0af186e118256b62905de38487ffacc41693ff47 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:41 2018 -0500 Enable ESLint for `js/views/attachment_view.js` commit 28a2dc5b8a28e1a087924fdc7275bf7d9a577b92 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:12 2018 -0500 Remove dynamic type checks commit f4ce36fcfc2737de32d911cd6103f889097813f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:27:56 2018 -0500 Rename `process` to `upgradeSchema` - `Message.process` -> `Message.upgradeSchema` - `Attachment.process` -> `Attachment.upgradeSchema` - `Attachment::processVersion` -> `Attachment::schemaVersion` Document version history. commit 41b92c0a31050ba05ddb1c43171d651f3568b9ac Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:11:50 2018 -0500 Add `operator-linebreak` ESLint rule Based on the following discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r168029106 commit 462defbe55879060fe25bc69103d4429bae2b2f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:01:30 2018 -0500 Add missing `await` for `ConversationController.getOrCreateAndWait` Tested this by setting `if` condition to `true` and confirming it works. It turns rotating a profile key is more involved and might require registering a new account according to Matthew. commit c08058ee4b883b3e23a40683de802ac81ed74874 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 16:32:24 2018 -0500 Convert `FileList` to `Array` commit 70a6c4201925f57be1f94d9da3547fdefc7bbb53 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:46:34 2018 -0500 :art: Fix lint errors commit 2ca7cdbc31d4120d6c6a838a6dcf43bc209d9788 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:07:09 2018 -0500 Skip `autoOrientImage` for non-JPEG images commit 58eac383013c16ca363a4ed33dca5c7ba61284e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:55:35 2018 -0500 Move new-style modules to `window.Signal` namespace commit 02c9328877dce289d6116a18b1c223891bd3cd0b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:35:23 2018 -0500 Extract `npm run test-modules` command commit 2c708eb94fba468b81ea9427734896114f5a7807 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:51 2018 -0500 Extract `Message.process` commit 4a2e52f68a77536a0fa04aa3c29ad3e541a8fa7e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:12 2018 -0500 Fix EditorConfig commit a346bab5db082720f5d47363f06301380e870425 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:13:02 2018 -0500 Remove `vim` directives on ESLint-ed files commit 7ec885c6359e495b407d5bc3eac9431d47c37fc6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:08:24 2018 -0500 Remove CSP whitelisting of `blob:` We no longer use `autoOrientImage` using blob URLs. Bring this back if we decide to auto-orient legacy attachments. commit 879b6f58f4a3f4a9ed6915af6b1be46c1e90e0ca Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:57:05 2018 -0500 Use `Message` type to determine send function Throws on invalid message type. commit 5203d945c98fd2562ae4e22c5c9838d27dec305b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:48 2018 -0500 Whitelist `Whisper` global commit 8ad0b066a3690d3382b86bf6ac00c03df7d1e20b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:32 2018 -0500 Add `Whisper.Types` namespace This avoids namespace collision for `Whisper.Message`. commit 785a949fce2656ca7dcaf0869d6b9e0648114e80 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:55:43 2018 -0500 Add `Message` type commit 674a7357abf0dcc365455695d56c0479998ebf27 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:35:23 2018 -0500 Run ESLint on `Conversation::sendMessage` commit cd985aa700caa80946245b17ea1b856449f152a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:34:38 2018 -0500 Document type signature of `FileInputView::readFile` commit d70d70e52c49588a1dc9833dfe5dd7128e13607f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:31:16 2018 -0500 Move attachment processing closer to sending This helps ensure processing happens uniformly, regardless of which code paths are taken to send an attachment. commit 532ac3e273a26b97f831247f9ee3412621b5c112 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:22:29 2018 -0500 Process attachment before it’s sent Picked this place since it already had various async steps, similar to `onMessageReceived` for the incoming `Attachment.process`. Could we try have this live closer to where we store it in IndexedDB, e.g. `Conversation::sendMessage`? commit a4582ae2fb6e1d3487131ba1f8fa6a00170cb32c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:21:42 2018 -0500 Refactor `getFile` and `getFiles` Lint them using ESLint. commit 07e9114e65046d791fc4f6ed90d6e2e938ad559d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:37:31 2018 -0500 Document incoming and outgoing attachments fields Note how outgoing message attachments only have 4 fields. This presumably means the others are not used in our code and could be discarded for simplicity. commit fdc3ef289d6ec1be344a12d496839d5ba747bb6a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:36:21 2018 -0500 Highlight that `dataURLToBlob` is synchronous commit b9c6bf600fcecedfd649ef2ae3c8629cced4e45a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:35:49 2018 -0500 Add EditorConfig configuration commit e56101e229d56810c8e31ad7289043a152c6c449 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:34:23 2018 -0500 Replace custom with `blob-util` functions IMPORTANT: All of them are async so we need to use `await`, otherwise we get strange or silent errors. commit f95150f6a9569fabcb31f3acd9f6b7bf50b5d145 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:17:30 2018 -0500 Revert "Replace custom functions with `blob-util`" This reverts commit 8a81e9c01bfe80c0e1bf76737092206c06949512. commit 33860d93f3d30ec55c32f3f4a58729df2eb43f0d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:13:02 2018 -0500 Revert "Replace `blueimp-canvas-to-blob` with `blob-util`" This reverts commit 31b3e853e4afc78fe80995921aa4152d9f6e4783. commit 7a0ba6fed622d76a3c39c7f03de541a7edb5b8dd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:12:58 2018 -0500 Replace `blueimp-canvas-to-blob` with `blob-util` commit 47a5f2bfd8b3f546e27e8d2b7e1969755d825a66 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:55:34 2018 -0500 Replace custom functions with `blob-util` commit 1cfa0efdb4fb1265369e2bf243c21f04f044fa01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:47:02 2018 -0500 Add `blob-util` dependency commit 9ac26be1bd783cd5070d886de107dd3ad9c91ad1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:44 2018 -0500 Document why we drop original image data during auto-orient commit 4136d6c382b99f41760a4da519d0db537fa7de8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:27 2018 -0500 Extract `DEFAULT_JPEG_QUALITY` commit 4a7156327eb5f94dba80cb300b344ac591226b0e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:37:11 2018 -0500 Drop support for invalid `image/jpg` MIME type commit 69fe96581f25413194032232f1bf704312e4754c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:30 2018 -0500 Document `window.onInvalidStateError` global commit a48ba1c77458da38583ee9cd488f70a59f6ee0fd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:04 2018 -0500 Selectively run ESLint on `js/background.js` Enabling ESLint on a per function basis allows us to incrementally improve the codebase without requiring large and potentially risky refactorings. commit e6d1cf826befc17ad4ec72fda8e761701665635e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:16:23 2018 -0500 Move async attachment processing to `onMessageReceived` We previously processed attachments in `handleDataMessage` which is mostly a synchronous function, except for the saving of the model. Moving the processing into the already async `onMessageReceived` improves code clarity. commit be6ca2a9aae5b59c360817deb1e18d39d705755e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:49 2018 -0500 Document import of ES2015+ modules commit eaaf7c41608fb988b8f4bbaa933cff110115610e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:29 2018 -0500 :art: Fix lint error commit a25b0e2e3d0f72c6a7bf0a15683f02450d5209ee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:13:57 2018 -0500 :art: Organize `require`s commit e0cc3d8fab6529d01b388acddf8605908c3d236b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:07:17 2018 -0500 Implement attachment process version Instead of keeping track of last normalization (processing) date, we now keep track of an internal processing version that will help us understand what kind of processing has already been completed for a given attachment. This will let us retroactively upgrade existing attachments. As we add more processing steps, we can build a processing pipeline that can convert any attachment processing version into a higher one, e.g. 4 -> 5 -> 6 -> 7. commit ad9083d0fdb880bc518e02251e51a39f7e1c585f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:50:31 2018 -0500 Ignore ES2015+ files during JSCS linting commit 96641205f734927aaebc2342d977c555799c3e3b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:48:07 2018 -0500 Improve ESLint ignore rules Apparently, using unqualified `/**` patterns prevents `!` include patterns. Using qualified glob patterns, e.g. `js/models/**/*.js`, lets us work around this. commit 255e0ab15bd1a0ca8ca5746e42d23977c8765d01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:44:59 2018 -0500 :abc: ESLint ignored files commit ebcb70258a26f234bd602072ac7c0a1913128132 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:47 2018 -0500 Whitelist `browser` environment for ESLint commit 3eaace6f3a21421c5aaaaf01592408c7ed83ecd3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:05 2018 -0500 Use `MIME` module commit ba2cf7770e614415733414a2dcc48f110b929892 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:32:54 2018 -0500 :art: Fix lint errors commit 65acc86e8580e88f7a6611eb4b8fa5d7291f7a3f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:30:42 2018 -0500 Add ES2015+ files to JSHint ignored list commit 8b6494ae6c9247acdfa059a9b361ec5ffcdb39f0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:29:20 2018 -0500 Document potentially unexpected `autoScale` behavior commit 8b4c69b2002d1777d3621be10f92cbf432f9d4d6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:26:47 2018 -0500 Test CommonJS modules separately Not sure how to test them as part of Grunt `unit-tests` task as `test/index.html` doesn’t allow for inclusion of CommonJS modules that use `require`. The tests are silently skipped. commit 213400e4b2bba3efee856a25b40e269221c3c39d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:24:27 2018 -0500 Add `MIME` type module commit 37a726e4fb4b3ed65914463122a5662847b5adee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:18:05 2018 -0500 Return proper `Error` from `blobArrayToBuffer` commit 164752db5612220e4dcf58d57bcd682cb489a399 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:15:41 2018 -0500 :art: Fix ESLint errors commit d498dd79a067c75098dd3179814c914780e5cb4f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:14:33 2018 -0500 Update `Attachment` type field definitions commit 141155a1533ff8fb616b70ea313432781bbebffd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:12:50 2018 -0500 Move `blueimp-canvas-to-blob` from Bower to npm commit 7ccb833e5d286ddd6235d3e491c62ac1e4544510 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:33:50 2018 -0500 :art: Clarify data flow commit e7da41591fde5a830467bebf1b6f51c1f7293e74 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:31:21 2018 -0500 Use `blobUrl` for consistency commit 523a80eefe0e2858aa1fb2bb9539ec44da502963 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:28:06 2018 -0500 Remove just-in-time image auto-orient for lightbox We can bring this back if our users would like auto-orient for old attachments. commit 0739feae9c47dd523c10740d6cdf746d539f270c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:27:21 2018 -0500 Remove just-in-time auto-orient of message attachments We can bring this back if our users would like auto-orient for old attachments. But better yet, we might implement this as database migration. commit ed43c66f92830ee233d5a94d0545eea4da43894d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:26:24 2018 -0500 Auto-orient JPEG attachments upon receipt commit e2eb8e36b017b048d57602fca14e45d657e0e1a1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:25:26 2018 -0500 Expose `Attachment` type through `Whisper.Attachment` commit 9638fbc987b84f143ca34211dc4666d96248ea2f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:39 2018 -0500 Use `contentType` from `model` commit 032c0ced46c3876cb9474b26f9d53d6f1c6b16a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:04 2018 -0500 Return `Error` object for `autoOrientImage` failures commit ff04bad8510c4b21aef350bed2b1887d0e055b98 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:22:32 2018 -0500 Add `options` for `autoOrientImage` output type / quality commit 87745b5586d1e182b51c9f9bc5e4eaf6dbc16722 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:18:46 2018 -0500 Add `Attachment` type Defines various functions on attachments, e.g. normalization (auto-orient JPEGs, etc.) commit de27fdc10a53bc8882a9c978e82265db9ac6d6f5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:16:34 2018 -0500 Add `yarn grunt` shortcut This allows us to use local `grunt-cli` for `grunt dev`. commit 59974db5a5da0d8f4cdc8ce5c4e3c974ecd5e754 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:10:11 2018 -0500 Improve readability commit b5ba96f1e6f40f2e1fa77490c583217768e1f412 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:08:12 2018 -0500 Use `snake_case` for module names Prevents problems across case-sensitive and case-insensitive file systems. We can work around this in the future using a lint rule such as `eslint-plugin-require-path-exists`. See discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r167365931 commit 48c5d3155c96ef628b00d99b52975e580d1d5501 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:05:44 2018 -0500 :art: Use destructuring commit 4822f49f22382a99ebf142b337375f7c25251d76 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:41:40 2018 -0500 Auto-orient images in lightbox view commit 7317110809677dddbbef3fadbf912cdba1c010bf Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:40:14 2018 -0500 Document magic number for escape key commit c790d07389a7d0bbf5298de83dbcfa8be1e7696b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:38:35 2018 -0500 Make second `View` argument an `options` object commit fbe010bb63d0088af9dfe11f153437fab34247e0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:27:40 2018 -0500 Allow `loadImage` to fetch `blob://` URLs commit ec35710d002b019a273eeb48f94dcaf2babe5d96 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:48 2018 -0500 :art: Shorten `autoOrientImage` import commit d07433e3cf316c6a143a0c9393ba26df9e3af17b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:19 2018 -0500 Make `autoOrientImage` module standalone commit c285bf5e33cdf10e0ef71e72cd6f55aef0df96ef Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:55:44 2018 -0500 Replace `loadImage` with `autoOrientImage` commit 44318549235af01fd061c25f557c93fd21cebb7a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:53:23 2018 -0500 Add `autoOrientImage` module This module exposes `loadImage` with a `Promise` based interface and pre- populates `orientation: true` option to auto-orient input. Returns data URL as string. The module uses a named export as refactoring references of modules with `default` (`module.exports`) export references can be error-prone. See: https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html commit c77063afc6366fe49615052796fe46f9b369de39 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:44:30 2018 -0500 Auto-orient preview images See: #998 commit 06dba5eb8f662c11af3a9ba8395bb453ab2e5f8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:43:23 2018 -0500 TODO: Use native `Canvas::toBlob` One challenge is that `Canvas::toBlob` is async whereas `dataURLtoBlob` is sync. commit b15c304a3125dd023fd90990e6225a7303f3596f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:42:45 2018 -0500 Make `null` check strict Appeases JSHint. ESLint has a nice `smart` option for `eqeqeq` rule: https://eslint.org/docs/rules/eqeqeq#smart commit ea70b92d9b18201758e11fdc25b09afc97b50055 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 15:23:58 2018 -0500 Use `Canvas::toDataURL` to preserve `ImageView` logic This way, all the other code paths remain untouched in case we want to remove the auto-orient code once Chrome supports the `image-orientation` CSS property. See: - #998 - https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation commit 62fd744f9f27d951573a68d2cdfe7ba2a3784b41 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:38:04 2018 -0500 Use CSS to constrain auto-oriented images commit f4d3392687168c237441b29140c7968b49dbef9e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:35:02 2018 -0500 Replace `ImageView` `el` with auto-oriented `canvas` See: #998 commit 1602d7f610e4993ad1291f88197f9ead1e25e776 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:48 2018 -0500 Pass `Blob` to `View` (for `ImageView`) This allows us to do JPEG auto-orientation based on EXIF metadata. commit e6a414f2b2a80da1137b839b348a38510efd04bb Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:12 2018 -0500 :hocho: Remove newline commit 5f0d9570d7862fc428ff89c2ecfd332a744537e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:17:02 2018 -0500 Expose `blueimp-load-image` as `window.loadImage` commit 1e1c62fe2f6a76dbcf1998dd468c26187c9871dc Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:16:46 2018 -0500 Add `blueimp-load-image` npm dependency commit ad17fa8a68a21ca5ddec336801b8568009bef3d4 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:14:40 2018 -0500 Remove `blueimp-load-image` Bower dependency
2018-02-21 15:26:59 +00:00
this.queueJob(async () => {
const now = Date.now();
2017-09-25 23:41:57 +00:00
window.log.info(
Auto-orient image attachments based on EXIF metadata As described in #998, images are sometimes displayed with an incorrect orientation. This is because cameras often write files in the native sensor byte order and attach the `Orientation` EXIF metadata to tell end-user devices how to display the images based on the original author’s capture orientation. Electron/Chromium (and therefore Signal Desktop) currently doesn’t support applying this metadata for `<img>` tags, e.g. CSS `image-orientation: from- image`. As a workaround, this change uses the `loadImage` library with the `orientation: true` flag to auto-orient images ~~before display~~ upon receipt and before sending. **Changes** - [x] ~~Auto-orient images during display in message list view~~ - [x] Ensure image is not displayed until loaded (to prevent layout reflow) . - [x] Auto-orient images upon receipt and before storing in IndexedDB (~~or preserve original data until Chromium offers native fix?~~) - [x] Auto-orient images in compose area preview. - [x] ~~Auto-orient images in lightbox view~~ - [x] Auto-orient images before sending / storage. - [x] Add EditorConfig for sharing code styles across editors. - [x] Fix ESLint ignore file. - [x] Change `function-paren-newline` ESLint rule from `consistent` to `multiline`. - [x] Add `operator-linebreak` ESLint rule for consistency. - [x] Added `blob-util` dependency for converting between array buffers, blobs, etc. - [x] Extracted `createMessageHandler` to consolidate logic for `onMessageReceived` and `onSentMessage`. - [x] Introduce `async` / `await` to simplify async coding (restore control flow for branching, loops, and exceptions). - [x] Introduce `window.Signal` namespace for exposing ES2015+ CommonJS modules. - [x] Introduce rudimentary `Message` and `Attachment` types to begin defining a schema and versioning. This will allow us to track which changes, e.g. auto-orient JPEGs, per message / attachment as well as which fields are stored. - [x] Renamed `window.dataURLtoBlob` to `window.dataURLToBlobSync` to both fix the strange `camelCase` as well as to highlight that this operation is synchronous and therefore blocks the user thread. - [x] Normalize all JPEG MIME types to `image/jpeg`, eliminating the invalid `image/jpg`. - [x] Add `npm run test-modules` command for testing non-browser specific CommonJS modules. - **Stretch Goals** - [ ] ~~Restrict `autoOrientImage` to `Blob` to narrow API interface.~~ Do this once we use PureScript. - [ ] ~~Return non-JPEGs as no-op from `autoOrientImage`.~~ Skipping `autoOrientImage` for non-JPEGs altogether. - [ ] Retroactively auto-orient existing JPEG image attachments in the background. --- Fixes #998 --- - **Blog:** EXIF Orientation Handling Is a Ghetto: https://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ - **Chromium Bug:** EXIF orientation is ignored: https://bugs.chromium.org/p/chromium/issues/detail?id=56845 - **Chromium Bug:** Support for the CSS image-orientation CSS property: https://bugs.chromium.org/p/chromium/issues/detail?id=158753 --- commit ce5090b473a2448229dc38e4c3f15d7ad0137714 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 16 10:35:36 2018 -0500 Inline message descriptors commit 329036e59c138c1e950ec7c654eebd7d87076de5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:34:40 2018 -0500 Clarify order of operations Semantically, it makes more sense to do `getFile` before `clearForm` even though it seems to work either way. commit f9d4cfb2ba0d8aa308b0923bbe6066ea34cb97bd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:18:26 2018 -0500 Simplify `operator-linebreak` configuration Enabling `before` caused more code changes and it turns out our previous configuration is already the default. commit db588997acdd90ed2ad829174ecbba744383c78b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:15:59 2018 -0500 Remove obsolete TODO commit 799c8817633f6afa0b731fc3b5434e463bd850e3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:12:18 2018 -0500 Enable ESLint `function-paren-newline` `multiline` Per discussion. commit b660b6bc8ef41df7601a411213d6cda80821df87 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:10:48 2018 -0500 Use `messageDescriptor.id` not `source` commit 5e7309d176f4a7e97d3dc4c738e6b0ccd4792871 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:29:01 2018 -0500 Remove unnecessary `eslint-env` commit 393b3da55eabd7413596c86cc3971b063a0efe31 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:19:17 2018 -0500 Refactor `onSentMessage` and `onMessageReceived` Since they are so similar, we create the handlers using new `createMessageHandler` function. This allows us to ensure both synced and received messages go through schema upgrade pipeline. commit b3db0bf179c9a5bea96480cde28c6fa7193ac117 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:18:21 2018 -0500 Add `Message` descriptor functions commit 8febf125b1b42fe4ae1888dd50fcee2749dc1ff0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 14:46:56 2018 -0500 Fix typo commit 98d951ef77bd578b313a4ff4b496b793e82e88d5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:39 2018 -0500 Remove `promises` reference commit a0e9559ed5bed947dabf28cb672e63d39948d854 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:13 2018 -0500 Fix `AttachmentView::mediaType` fall-through commit 67be916a83951b8a1f9b22efe78a6da6b1825f38 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:03:41 2018 -0500 Remove minor TODOs commit 0af186e118256b62905de38487ffacc41693ff47 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:41 2018 -0500 Enable ESLint for `js/views/attachment_view.js` commit 28a2dc5b8a28e1a087924fdc7275bf7d9a577b92 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:12 2018 -0500 Remove dynamic type checks commit f4ce36fcfc2737de32d911cd6103f889097813f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:27:56 2018 -0500 Rename `process` to `upgradeSchema` - `Message.process` -> `Message.upgradeSchema` - `Attachment.process` -> `Attachment.upgradeSchema` - `Attachment::processVersion` -> `Attachment::schemaVersion` Document version history. commit 41b92c0a31050ba05ddb1c43171d651f3568b9ac Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:11:50 2018 -0500 Add `operator-linebreak` ESLint rule Based on the following discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r168029106 commit 462defbe55879060fe25bc69103d4429bae2b2f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:01:30 2018 -0500 Add missing `await` for `ConversationController.getOrCreateAndWait` Tested this by setting `if` condition to `true` and confirming it works. It turns rotating a profile key is more involved and might require registering a new account according to Matthew. commit c08058ee4b883b3e23a40683de802ac81ed74874 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 16:32:24 2018 -0500 Convert `FileList` to `Array` commit 70a6c4201925f57be1f94d9da3547fdefc7bbb53 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:46:34 2018 -0500 :art: Fix lint errors commit 2ca7cdbc31d4120d6c6a838a6dcf43bc209d9788 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:07:09 2018 -0500 Skip `autoOrientImage` for non-JPEG images commit 58eac383013c16ca363a4ed33dca5c7ba61284e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:55:35 2018 -0500 Move new-style modules to `window.Signal` namespace commit 02c9328877dce289d6116a18b1c223891bd3cd0b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:35:23 2018 -0500 Extract `npm run test-modules` command commit 2c708eb94fba468b81ea9427734896114f5a7807 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:51 2018 -0500 Extract `Message.process` commit 4a2e52f68a77536a0fa04aa3c29ad3e541a8fa7e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:12 2018 -0500 Fix EditorConfig commit a346bab5db082720f5d47363f06301380e870425 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:13:02 2018 -0500 Remove `vim` directives on ESLint-ed files commit 7ec885c6359e495b407d5bc3eac9431d47c37fc6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:08:24 2018 -0500 Remove CSP whitelisting of `blob:` We no longer use `autoOrientImage` using blob URLs. Bring this back if we decide to auto-orient legacy attachments. commit 879b6f58f4a3f4a9ed6915af6b1be46c1e90e0ca Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:57:05 2018 -0500 Use `Message` type to determine send function Throws on invalid message type. commit 5203d945c98fd2562ae4e22c5c9838d27dec305b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:48 2018 -0500 Whitelist `Whisper` global commit 8ad0b066a3690d3382b86bf6ac00c03df7d1e20b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:32 2018 -0500 Add `Whisper.Types` namespace This avoids namespace collision for `Whisper.Message`. commit 785a949fce2656ca7dcaf0869d6b9e0648114e80 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:55:43 2018 -0500 Add `Message` type commit 674a7357abf0dcc365455695d56c0479998ebf27 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:35:23 2018 -0500 Run ESLint on `Conversation::sendMessage` commit cd985aa700caa80946245b17ea1b856449f152a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:34:38 2018 -0500 Document type signature of `FileInputView::readFile` commit d70d70e52c49588a1dc9833dfe5dd7128e13607f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:31:16 2018 -0500 Move attachment processing closer to sending This helps ensure processing happens uniformly, regardless of which code paths are taken to send an attachment. commit 532ac3e273a26b97f831247f9ee3412621b5c112 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:22:29 2018 -0500 Process attachment before it’s sent Picked this place since it already had various async steps, similar to `onMessageReceived` for the incoming `Attachment.process`. Could we try have this live closer to where we store it in IndexedDB, e.g. `Conversation::sendMessage`? commit a4582ae2fb6e1d3487131ba1f8fa6a00170cb32c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:21:42 2018 -0500 Refactor `getFile` and `getFiles` Lint them using ESLint. commit 07e9114e65046d791fc4f6ed90d6e2e938ad559d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:37:31 2018 -0500 Document incoming and outgoing attachments fields Note how outgoing message attachments only have 4 fields. This presumably means the others are not used in our code and could be discarded for simplicity. commit fdc3ef289d6ec1be344a12d496839d5ba747bb6a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:36:21 2018 -0500 Highlight that `dataURLToBlob` is synchronous commit b9c6bf600fcecedfd649ef2ae3c8629cced4e45a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:35:49 2018 -0500 Add EditorConfig configuration commit e56101e229d56810c8e31ad7289043a152c6c449 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:34:23 2018 -0500 Replace custom with `blob-util` functions IMPORTANT: All of them are async so we need to use `await`, otherwise we get strange or silent errors. commit f95150f6a9569fabcb31f3acd9f6b7bf50b5d145 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:17:30 2018 -0500 Revert "Replace custom functions with `blob-util`" This reverts commit 8a81e9c01bfe80c0e1bf76737092206c06949512. commit 33860d93f3d30ec55c32f3f4a58729df2eb43f0d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:13:02 2018 -0500 Revert "Replace `blueimp-canvas-to-blob` with `blob-util`" This reverts commit 31b3e853e4afc78fe80995921aa4152d9f6e4783. commit 7a0ba6fed622d76a3c39c7f03de541a7edb5b8dd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:12:58 2018 -0500 Replace `blueimp-canvas-to-blob` with `blob-util` commit 47a5f2bfd8b3f546e27e8d2b7e1969755d825a66 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:55:34 2018 -0500 Replace custom functions with `blob-util` commit 1cfa0efdb4fb1265369e2bf243c21f04f044fa01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:47:02 2018 -0500 Add `blob-util` dependency commit 9ac26be1bd783cd5070d886de107dd3ad9c91ad1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:44 2018 -0500 Document why we drop original image data during auto-orient commit 4136d6c382b99f41760a4da519d0db537fa7de8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:27 2018 -0500 Extract `DEFAULT_JPEG_QUALITY` commit 4a7156327eb5f94dba80cb300b344ac591226b0e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:37:11 2018 -0500 Drop support for invalid `image/jpg` MIME type commit 69fe96581f25413194032232f1bf704312e4754c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:30 2018 -0500 Document `window.onInvalidStateError` global commit a48ba1c77458da38583ee9cd488f70a59f6ee0fd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:04 2018 -0500 Selectively run ESLint on `js/background.js` Enabling ESLint on a per function basis allows us to incrementally improve the codebase without requiring large and potentially risky refactorings. commit e6d1cf826befc17ad4ec72fda8e761701665635e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:16:23 2018 -0500 Move async attachment processing to `onMessageReceived` We previously processed attachments in `handleDataMessage` which is mostly a synchronous function, except for the saving of the model. Moving the processing into the already async `onMessageReceived` improves code clarity. commit be6ca2a9aae5b59c360817deb1e18d39d705755e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:49 2018 -0500 Document import of ES2015+ modules commit eaaf7c41608fb988b8f4bbaa933cff110115610e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:29 2018 -0500 :art: Fix lint error commit a25b0e2e3d0f72c6a7bf0a15683f02450d5209ee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:13:57 2018 -0500 :art: Organize `require`s commit e0cc3d8fab6529d01b388acddf8605908c3d236b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:07:17 2018 -0500 Implement attachment process version Instead of keeping track of last normalization (processing) date, we now keep track of an internal processing version that will help us understand what kind of processing has already been completed for a given attachment. This will let us retroactively upgrade existing attachments. As we add more processing steps, we can build a processing pipeline that can convert any attachment processing version into a higher one, e.g. 4 -> 5 -> 6 -> 7. commit ad9083d0fdb880bc518e02251e51a39f7e1c585f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:50:31 2018 -0500 Ignore ES2015+ files during JSCS linting commit 96641205f734927aaebc2342d977c555799c3e3b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:48:07 2018 -0500 Improve ESLint ignore rules Apparently, using unqualified `/**` patterns prevents `!` include patterns. Using qualified glob patterns, e.g. `js/models/**/*.js`, lets us work around this. commit 255e0ab15bd1a0ca8ca5746e42d23977c8765d01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:44:59 2018 -0500 :abc: ESLint ignored files commit ebcb70258a26f234bd602072ac7c0a1913128132 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:47 2018 -0500 Whitelist `browser` environment for ESLint commit 3eaace6f3a21421c5aaaaf01592408c7ed83ecd3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:05 2018 -0500 Use `MIME` module commit ba2cf7770e614415733414a2dcc48f110b929892 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:32:54 2018 -0500 :art: Fix lint errors commit 65acc86e8580e88f7a6611eb4b8fa5d7291f7a3f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:30:42 2018 -0500 Add ES2015+ files to JSHint ignored list commit 8b6494ae6c9247acdfa059a9b361ec5ffcdb39f0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:29:20 2018 -0500 Document potentially unexpected `autoScale` behavior commit 8b4c69b2002d1777d3621be10f92cbf432f9d4d6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:26:47 2018 -0500 Test CommonJS modules separately Not sure how to test them as part of Grunt `unit-tests` task as `test/index.html` doesn’t allow for inclusion of CommonJS modules that use `require`. The tests are silently skipped. commit 213400e4b2bba3efee856a25b40e269221c3c39d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:24:27 2018 -0500 Add `MIME` type module commit 37a726e4fb4b3ed65914463122a5662847b5adee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:18:05 2018 -0500 Return proper `Error` from `blobArrayToBuffer` commit 164752db5612220e4dcf58d57bcd682cb489a399 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:15:41 2018 -0500 :art: Fix ESLint errors commit d498dd79a067c75098dd3179814c914780e5cb4f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:14:33 2018 -0500 Update `Attachment` type field definitions commit 141155a1533ff8fb616b70ea313432781bbebffd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:12:50 2018 -0500 Move `blueimp-canvas-to-blob` from Bower to npm commit 7ccb833e5d286ddd6235d3e491c62ac1e4544510 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:33:50 2018 -0500 :art: Clarify data flow commit e7da41591fde5a830467bebf1b6f51c1f7293e74 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:31:21 2018 -0500 Use `blobUrl` for consistency commit 523a80eefe0e2858aa1fb2bb9539ec44da502963 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:28:06 2018 -0500 Remove just-in-time image auto-orient for lightbox We can bring this back if our users would like auto-orient for old attachments. commit 0739feae9c47dd523c10740d6cdf746d539f270c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:27:21 2018 -0500 Remove just-in-time auto-orient of message attachments We can bring this back if our users would like auto-orient for old attachments. But better yet, we might implement this as database migration. commit ed43c66f92830ee233d5a94d0545eea4da43894d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:26:24 2018 -0500 Auto-orient JPEG attachments upon receipt commit e2eb8e36b017b048d57602fca14e45d657e0e1a1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:25:26 2018 -0500 Expose `Attachment` type through `Whisper.Attachment` commit 9638fbc987b84f143ca34211dc4666d96248ea2f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:39 2018 -0500 Use `contentType` from `model` commit 032c0ced46c3876cb9474b26f9d53d6f1c6b16a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:04 2018 -0500 Return `Error` object for `autoOrientImage` failures commit ff04bad8510c4b21aef350bed2b1887d0e055b98 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:22:32 2018 -0500 Add `options` for `autoOrientImage` output type / quality commit 87745b5586d1e182b51c9f9bc5e4eaf6dbc16722 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:18:46 2018 -0500 Add `Attachment` type Defines various functions on attachments, e.g. normalization (auto-orient JPEGs, etc.) commit de27fdc10a53bc8882a9c978e82265db9ac6d6f5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:16:34 2018 -0500 Add `yarn grunt` shortcut This allows us to use local `grunt-cli` for `grunt dev`. commit 59974db5a5da0d8f4cdc8ce5c4e3c974ecd5e754 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:10:11 2018 -0500 Improve readability commit b5ba96f1e6f40f2e1fa77490c583217768e1f412 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:08:12 2018 -0500 Use `snake_case` for module names Prevents problems across case-sensitive and case-insensitive file systems. We can work around this in the future using a lint rule such as `eslint-plugin-require-path-exists`. See discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r167365931 commit 48c5d3155c96ef628b00d99b52975e580d1d5501 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:05:44 2018 -0500 :art: Use destructuring commit 4822f49f22382a99ebf142b337375f7c25251d76 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:41:40 2018 -0500 Auto-orient images in lightbox view commit 7317110809677dddbbef3fadbf912cdba1c010bf Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:40:14 2018 -0500 Document magic number for escape key commit c790d07389a7d0bbf5298de83dbcfa8be1e7696b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:38:35 2018 -0500 Make second `View` argument an `options` object commit fbe010bb63d0088af9dfe11f153437fab34247e0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:27:40 2018 -0500 Allow `loadImage` to fetch `blob://` URLs commit ec35710d002b019a273eeb48f94dcaf2babe5d96 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:48 2018 -0500 :art: Shorten `autoOrientImage` import commit d07433e3cf316c6a143a0c9393ba26df9e3af17b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:19 2018 -0500 Make `autoOrientImage` module standalone commit c285bf5e33cdf10e0ef71e72cd6f55aef0df96ef Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:55:44 2018 -0500 Replace `loadImage` with `autoOrientImage` commit 44318549235af01fd061c25f557c93fd21cebb7a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:53:23 2018 -0500 Add `autoOrientImage` module This module exposes `loadImage` with a `Promise` based interface and pre- populates `orientation: true` option to auto-orient input. Returns data URL as string. The module uses a named export as refactoring references of modules with `default` (`module.exports`) export references can be error-prone. See: https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html commit c77063afc6366fe49615052796fe46f9b369de39 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:44:30 2018 -0500 Auto-orient preview images See: #998 commit 06dba5eb8f662c11af3a9ba8395bb453ab2e5f8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:43:23 2018 -0500 TODO: Use native `Canvas::toBlob` One challenge is that `Canvas::toBlob` is async whereas `dataURLtoBlob` is sync. commit b15c304a3125dd023fd90990e6225a7303f3596f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:42:45 2018 -0500 Make `null` check strict Appeases JSHint. ESLint has a nice `smart` option for `eqeqeq` rule: https://eslint.org/docs/rules/eqeqeq#smart commit ea70b92d9b18201758e11fdc25b09afc97b50055 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 15:23:58 2018 -0500 Use `Canvas::toDataURL` to preserve `ImageView` logic This way, all the other code paths remain untouched in case we want to remove the auto-orient code once Chrome supports the `image-orientation` CSS property. See: - #998 - https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation commit 62fd744f9f27d951573a68d2cdfe7ba2a3784b41 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:38:04 2018 -0500 Use CSS to constrain auto-oriented images commit f4d3392687168c237441b29140c7968b49dbef9e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:35:02 2018 -0500 Replace `ImageView` `el` with auto-oriented `canvas` See: #998 commit 1602d7f610e4993ad1291f88197f9ead1e25e776 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:48 2018 -0500 Pass `Blob` to `View` (for `ImageView`) This allows us to do JPEG auto-orientation based on EXIF metadata. commit e6a414f2b2a80da1137b839b348a38510efd04bb Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:12 2018 -0500 :hocho: Remove newline commit 5f0d9570d7862fc428ff89c2ecfd332a744537e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:17:02 2018 -0500 Expose `blueimp-load-image` as `window.loadImage` commit 1e1c62fe2f6a76dbcf1998dd468c26187c9871dc Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:16:46 2018 -0500 Add `blueimp-load-image` npm dependency commit ad17fa8a68a21ca5ddec336801b8568009bef3d4 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:14:40 2018 -0500 Remove `blueimp-load-image` Bower dependency
2018-02-21 15:26:59 +00:00
'Sending message to conversation',
this.idForLogging(),
'with timestamp',
now
);
2014-10-26 07:29:01 +00:00
// Here we move attachments to disk
const messageWithSchema = await upgradeMessageSchema({
2018-03-14 20:00:12 +00:00
type: 'outgoing',
Auto-orient image attachments based on EXIF metadata As described in #998, images are sometimes displayed with an incorrect orientation. This is because cameras often write files in the native sensor byte order and attach the `Orientation` EXIF metadata to tell end-user devices how to display the images based on the original author’s capture orientation. Electron/Chromium (and therefore Signal Desktop) currently doesn’t support applying this metadata for `<img>` tags, e.g. CSS `image-orientation: from- image`. As a workaround, this change uses the `loadImage` library with the `orientation: true` flag to auto-orient images ~~before display~~ upon receipt and before sending. **Changes** - [x] ~~Auto-orient images during display in message list view~~ - [x] Ensure image is not displayed until loaded (to prevent layout reflow) . - [x] Auto-orient images upon receipt and before storing in IndexedDB (~~or preserve original data until Chromium offers native fix?~~) - [x] Auto-orient images in compose area preview. - [x] ~~Auto-orient images in lightbox view~~ - [x] Auto-orient images before sending / storage. - [x] Add EditorConfig for sharing code styles across editors. - [x] Fix ESLint ignore file. - [x] Change `function-paren-newline` ESLint rule from `consistent` to `multiline`. - [x] Add `operator-linebreak` ESLint rule for consistency. - [x] Added `blob-util` dependency for converting between array buffers, blobs, etc. - [x] Extracted `createMessageHandler` to consolidate logic for `onMessageReceived` and `onSentMessage`. - [x] Introduce `async` / `await` to simplify async coding (restore control flow for branching, loops, and exceptions). - [x] Introduce `window.Signal` namespace for exposing ES2015+ CommonJS modules. - [x] Introduce rudimentary `Message` and `Attachment` types to begin defining a schema and versioning. This will allow us to track which changes, e.g. auto-orient JPEGs, per message / attachment as well as which fields are stored. - [x] Renamed `window.dataURLtoBlob` to `window.dataURLToBlobSync` to both fix the strange `camelCase` as well as to highlight that this operation is synchronous and therefore blocks the user thread. - [x] Normalize all JPEG MIME types to `image/jpeg`, eliminating the invalid `image/jpg`. - [x] Add `npm run test-modules` command for testing non-browser specific CommonJS modules. - **Stretch Goals** - [ ] ~~Restrict `autoOrientImage` to `Blob` to narrow API interface.~~ Do this once we use PureScript. - [ ] ~~Return non-JPEGs as no-op from `autoOrientImage`.~~ Skipping `autoOrientImage` for non-JPEGs altogether. - [ ] Retroactively auto-orient existing JPEG image attachments in the background. --- Fixes #998 --- - **Blog:** EXIF Orientation Handling Is a Ghetto: https://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ - **Chromium Bug:** EXIF orientation is ignored: https://bugs.chromium.org/p/chromium/issues/detail?id=56845 - **Chromium Bug:** Support for the CSS image-orientation CSS property: https://bugs.chromium.org/p/chromium/issues/detail?id=158753 --- commit ce5090b473a2448229dc38e4c3f15d7ad0137714 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 16 10:35:36 2018 -0500 Inline message descriptors commit 329036e59c138c1e950ec7c654eebd7d87076de5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:34:40 2018 -0500 Clarify order of operations Semantically, it makes more sense to do `getFile` before `clearForm` even though it seems to work either way. commit f9d4cfb2ba0d8aa308b0923bbe6066ea34cb97bd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:18:26 2018 -0500 Simplify `operator-linebreak` configuration Enabling `before` caused more code changes and it turns out our previous configuration is already the default. commit db588997acdd90ed2ad829174ecbba744383c78b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:15:59 2018 -0500 Remove obsolete TODO commit 799c8817633f6afa0b731fc3b5434e463bd850e3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:12:18 2018 -0500 Enable ESLint `function-paren-newline` `multiline` Per discussion. commit b660b6bc8ef41df7601a411213d6cda80821df87 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:10:48 2018 -0500 Use `messageDescriptor.id` not `source` commit 5e7309d176f4a7e97d3dc4c738e6b0ccd4792871 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:29:01 2018 -0500 Remove unnecessary `eslint-env` commit 393b3da55eabd7413596c86cc3971b063a0efe31 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:19:17 2018 -0500 Refactor `onSentMessage` and `onMessageReceived` Since they are so similar, we create the handlers using new `createMessageHandler` function. This allows us to ensure both synced and received messages go through schema upgrade pipeline. commit b3db0bf179c9a5bea96480cde28c6fa7193ac117 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:18:21 2018 -0500 Add `Message` descriptor functions commit 8febf125b1b42fe4ae1888dd50fcee2749dc1ff0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 14:46:56 2018 -0500 Fix typo commit 98d951ef77bd578b313a4ff4b496b793e82e88d5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:39 2018 -0500 Remove `promises` reference commit a0e9559ed5bed947dabf28cb672e63d39948d854 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:13 2018 -0500 Fix `AttachmentView::mediaType` fall-through commit 67be916a83951b8a1f9b22efe78a6da6b1825f38 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:03:41 2018 -0500 Remove minor TODOs commit 0af186e118256b62905de38487ffacc41693ff47 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:41 2018 -0500 Enable ESLint for `js/views/attachment_view.js` commit 28a2dc5b8a28e1a087924fdc7275bf7d9a577b92 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:12 2018 -0500 Remove dynamic type checks commit f4ce36fcfc2737de32d911cd6103f889097813f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:27:56 2018 -0500 Rename `process` to `upgradeSchema` - `Message.process` -> `Message.upgradeSchema` - `Attachment.process` -> `Attachment.upgradeSchema` - `Attachment::processVersion` -> `Attachment::schemaVersion` Document version history. commit 41b92c0a31050ba05ddb1c43171d651f3568b9ac Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:11:50 2018 -0500 Add `operator-linebreak` ESLint rule Based on the following discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r168029106 commit 462defbe55879060fe25bc69103d4429bae2b2f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:01:30 2018 -0500 Add missing `await` for `ConversationController.getOrCreateAndWait` Tested this by setting `if` condition to `true` and confirming it works. It turns rotating a profile key is more involved and might require registering a new account according to Matthew. commit c08058ee4b883b3e23a40683de802ac81ed74874 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 16:32:24 2018 -0500 Convert `FileList` to `Array` commit 70a6c4201925f57be1f94d9da3547fdefc7bbb53 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:46:34 2018 -0500 :art: Fix lint errors commit 2ca7cdbc31d4120d6c6a838a6dcf43bc209d9788 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:07:09 2018 -0500 Skip `autoOrientImage` for non-JPEG images commit 58eac383013c16ca363a4ed33dca5c7ba61284e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:55:35 2018 -0500 Move new-style modules to `window.Signal` namespace commit 02c9328877dce289d6116a18b1c223891bd3cd0b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:35:23 2018 -0500 Extract `npm run test-modules` command commit 2c708eb94fba468b81ea9427734896114f5a7807 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:51 2018 -0500 Extract `Message.process` commit 4a2e52f68a77536a0fa04aa3c29ad3e541a8fa7e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:12 2018 -0500 Fix EditorConfig commit a346bab5db082720f5d47363f06301380e870425 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:13:02 2018 -0500 Remove `vim` directives on ESLint-ed files commit 7ec885c6359e495b407d5bc3eac9431d47c37fc6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:08:24 2018 -0500 Remove CSP whitelisting of `blob:` We no longer use `autoOrientImage` using blob URLs. Bring this back if we decide to auto-orient legacy attachments. commit 879b6f58f4a3f4a9ed6915af6b1be46c1e90e0ca Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:57:05 2018 -0500 Use `Message` type to determine send function Throws on invalid message type. commit 5203d945c98fd2562ae4e22c5c9838d27dec305b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:48 2018 -0500 Whitelist `Whisper` global commit 8ad0b066a3690d3382b86bf6ac00c03df7d1e20b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:32 2018 -0500 Add `Whisper.Types` namespace This avoids namespace collision for `Whisper.Message`. commit 785a949fce2656ca7dcaf0869d6b9e0648114e80 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:55:43 2018 -0500 Add `Message` type commit 674a7357abf0dcc365455695d56c0479998ebf27 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:35:23 2018 -0500 Run ESLint on `Conversation::sendMessage` commit cd985aa700caa80946245b17ea1b856449f152a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:34:38 2018 -0500 Document type signature of `FileInputView::readFile` commit d70d70e52c49588a1dc9833dfe5dd7128e13607f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:31:16 2018 -0500 Move attachment processing closer to sending This helps ensure processing happens uniformly, regardless of which code paths are taken to send an attachment. commit 532ac3e273a26b97f831247f9ee3412621b5c112 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:22:29 2018 -0500 Process attachment before it’s sent Picked this place since it already had various async steps, similar to `onMessageReceived` for the incoming `Attachment.process`. Could we try have this live closer to where we store it in IndexedDB, e.g. `Conversation::sendMessage`? commit a4582ae2fb6e1d3487131ba1f8fa6a00170cb32c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:21:42 2018 -0500 Refactor `getFile` and `getFiles` Lint them using ESLint. commit 07e9114e65046d791fc4f6ed90d6e2e938ad559d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:37:31 2018 -0500 Document incoming and outgoing attachments fields Note how outgoing message attachments only have 4 fields. This presumably means the others are not used in our code and could be discarded for simplicity. commit fdc3ef289d6ec1be344a12d496839d5ba747bb6a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:36:21 2018 -0500 Highlight that `dataURLToBlob` is synchronous commit b9c6bf600fcecedfd649ef2ae3c8629cced4e45a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:35:49 2018 -0500 Add EditorConfig configuration commit e56101e229d56810c8e31ad7289043a152c6c449 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:34:23 2018 -0500 Replace custom with `blob-util` functions IMPORTANT: All of them are async so we need to use `await`, otherwise we get strange or silent errors. commit f95150f6a9569fabcb31f3acd9f6b7bf50b5d145 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:17:30 2018 -0500 Revert "Replace custom functions with `blob-util`" This reverts commit 8a81e9c01bfe80c0e1bf76737092206c06949512. commit 33860d93f3d30ec55c32f3f4a58729df2eb43f0d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:13:02 2018 -0500 Revert "Replace `blueimp-canvas-to-blob` with `blob-util`" This reverts commit 31b3e853e4afc78fe80995921aa4152d9f6e4783. commit 7a0ba6fed622d76a3c39c7f03de541a7edb5b8dd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:12:58 2018 -0500 Replace `blueimp-canvas-to-blob` with `blob-util` commit 47a5f2bfd8b3f546e27e8d2b7e1969755d825a66 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:55:34 2018 -0500 Replace custom functions with `blob-util` commit 1cfa0efdb4fb1265369e2bf243c21f04f044fa01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:47:02 2018 -0500 Add `blob-util` dependency commit 9ac26be1bd783cd5070d886de107dd3ad9c91ad1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:44 2018 -0500 Document why we drop original image data during auto-orient commit 4136d6c382b99f41760a4da519d0db537fa7de8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:27 2018 -0500 Extract `DEFAULT_JPEG_QUALITY` commit 4a7156327eb5f94dba80cb300b344ac591226b0e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:37:11 2018 -0500 Drop support for invalid `image/jpg` MIME type commit 69fe96581f25413194032232f1bf704312e4754c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:30 2018 -0500 Document `window.onInvalidStateError` global commit a48ba1c77458da38583ee9cd488f70a59f6ee0fd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:04 2018 -0500 Selectively run ESLint on `js/background.js` Enabling ESLint on a per function basis allows us to incrementally improve the codebase without requiring large and potentially risky refactorings. commit e6d1cf826befc17ad4ec72fda8e761701665635e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:16:23 2018 -0500 Move async attachment processing to `onMessageReceived` We previously processed attachments in `handleDataMessage` which is mostly a synchronous function, except for the saving of the model. Moving the processing into the already async `onMessageReceived` improves code clarity. commit be6ca2a9aae5b59c360817deb1e18d39d705755e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:49 2018 -0500 Document import of ES2015+ modules commit eaaf7c41608fb988b8f4bbaa933cff110115610e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:29 2018 -0500 :art: Fix lint error commit a25b0e2e3d0f72c6a7bf0a15683f02450d5209ee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:13:57 2018 -0500 :art: Organize `require`s commit e0cc3d8fab6529d01b388acddf8605908c3d236b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:07:17 2018 -0500 Implement attachment process version Instead of keeping track of last normalization (processing) date, we now keep track of an internal processing version that will help us understand what kind of processing has already been completed for a given attachment. This will let us retroactively upgrade existing attachments. As we add more processing steps, we can build a processing pipeline that can convert any attachment processing version into a higher one, e.g. 4 -> 5 -> 6 -> 7. commit ad9083d0fdb880bc518e02251e51a39f7e1c585f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:50:31 2018 -0500 Ignore ES2015+ files during JSCS linting commit 96641205f734927aaebc2342d977c555799c3e3b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:48:07 2018 -0500 Improve ESLint ignore rules Apparently, using unqualified `/**` patterns prevents `!` include patterns. Using qualified glob patterns, e.g. `js/models/**/*.js`, lets us work around this. commit 255e0ab15bd1a0ca8ca5746e42d23977c8765d01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:44:59 2018 -0500 :abc: ESLint ignored files commit ebcb70258a26f234bd602072ac7c0a1913128132 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:47 2018 -0500 Whitelist `browser` environment for ESLint commit 3eaace6f3a21421c5aaaaf01592408c7ed83ecd3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:05 2018 -0500 Use `MIME` module commit ba2cf7770e614415733414a2dcc48f110b929892 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:32:54 2018 -0500 :art: Fix lint errors commit 65acc86e8580e88f7a6611eb4b8fa5d7291f7a3f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:30:42 2018 -0500 Add ES2015+ files to JSHint ignored list commit 8b6494ae6c9247acdfa059a9b361ec5ffcdb39f0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:29:20 2018 -0500 Document potentially unexpected `autoScale` behavior commit 8b4c69b2002d1777d3621be10f92cbf432f9d4d6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:26:47 2018 -0500 Test CommonJS modules separately Not sure how to test them as part of Grunt `unit-tests` task as `test/index.html` doesn’t allow for inclusion of CommonJS modules that use `require`. The tests are silently skipped. commit 213400e4b2bba3efee856a25b40e269221c3c39d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:24:27 2018 -0500 Add `MIME` type module commit 37a726e4fb4b3ed65914463122a5662847b5adee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:18:05 2018 -0500 Return proper `Error` from `blobArrayToBuffer` commit 164752db5612220e4dcf58d57bcd682cb489a399 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:15:41 2018 -0500 :art: Fix ESLint errors commit d498dd79a067c75098dd3179814c914780e5cb4f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:14:33 2018 -0500 Update `Attachment` type field definitions commit 141155a1533ff8fb616b70ea313432781bbebffd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:12:50 2018 -0500 Move `blueimp-canvas-to-blob` from Bower to npm commit 7ccb833e5d286ddd6235d3e491c62ac1e4544510 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:33:50 2018 -0500 :art: Clarify data flow commit e7da41591fde5a830467bebf1b6f51c1f7293e74 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:31:21 2018 -0500 Use `blobUrl` for consistency commit 523a80eefe0e2858aa1fb2bb9539ec44da502963 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:28:06 2018 -0500 Remove just-in-time image auto-orient for lightbox We can bring this back if our users would like auto-orient for old attachments. commit 0739feae9c47dd523c10740d6cdf746d539f270c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:27:21 2018 -0500 Remove just-in-time auto-orient of message attachments We can bring this back if our users would like auto-orient for old attachments. But better yet, we might implement this as database migration. commit ed43c66f92830ee233d5a94d0545eea4da43894d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:26:24 2018 -0500 Auto-orient JPEG attachments upon receipt commit e2eb8e36b017b048d57602fca14e45d657e0e1a1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:25:26 2018 -0500 Expose `Attachment` type through `Whisper.Attachment` commit 9638fbc987b84f143ca34211dc4666d96248ea2f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:39 2018 -0500 Use `contentType` from `model` commit 032c0ced46c3876cb9474b26f9d53d6f1c6b16a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:04 2018 -0500 Return `Error` object for `autoOrientImage` failures commit ff04bad8510c4b21aef350bed2b1887d0e055b98 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:22:32 2018 -0500 Add `options` for `autoOrientImage` output type / quality commit 87745b5586d1e182b51c9f9bc5e4eaf6dbc16722 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:18:46 2018 -0500 Add `Attachment` type Defines various functions on attachments, e.g. normalization (auto-orient JPEGs, etc.) commit de27fdc10a53bc8882a9c978e82265db9ac6d6f5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:16:34 2018 -0500 Add `yarn grunt` shortcut This allows us to use local `grunt-cli` for `grunt dev`. commit 59974db5a5da0d8f4cdc8ce5c4e3c974ecd5e754 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:10:11 2018 -0500 Improve readability commit b5ba96f1e6f40f2e1fa77490c583217768e1f412 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:08:12 2018 -0500 Use `snake_case` for module names Prevents problems across case-sensitive and case-insensitive file systems. We can work around this in the future using a lint rule such as `eslint-plugin-require-path-exists`. See discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r167365931 commit 48c5d3155c96ef628b00d99b52975e580d1d5501 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:05:44 2018 -0500 :art: Use destructuring commit 4822f49f22382a99ebf142b337375f7c25251d76 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:41:40 2018 -0500 Auto-orient images in lightbox view commit 7317110809677dddbbef3fadbf912cdba1c010bf Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:40:14 2018 -0500 Document magic number for escape key commit c790d07389a7d0bbf5298de83dbcfa8be1e7696b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:38:35 2018 -0500 Make second `View` argument an `options` object commit fbe010bb63d0088af9dfe11f153437fab34247e0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:27:40 2018 -0500 Allow `loadImage` to fetch `blob://` URLs commit ec35710d002b019a273eeb48f94dcaf2babe5d96 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:48 2018 -0500 :art: Shorten `autoOrientImage` import commit d07433e3cf316c6a143a0c9393ba26df9e3af17b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:19 2018 -0500 Make `autoOrientImage` module standalone commit c285bf5e33cdf10e0ef71e72cd6f55aef0df96ef Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:55:44 2018 -0500 Replace `loadImage` with `autoOrientImage` commit 44318549235af01fd061c25f557c93fd21cebb7a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:53:23 2018 -0500 Add `autoOrientImage` module This module exposes `loadImage` with a `Promise` based interface and pre- populates `orientation: true` option to auto-orient input. Returns data URL as string. The module uses a named export as refactoring references of modules with `default` (`module.exports`) export references can be error-prone. See: https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html commit c77063afc6366fe49615052796fe46f9b369de39 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:44:30 2018 -0500 Auto-orient preview images See: #998 commit 06dba5eb8f662c11af3a9ba8395bb453ab2e5f8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:43:23 2018 -0500 TODO: Use native `Canvas::toBlob` One challenge is that `Canvas::toBlob` is async whereas `dataURLtoBlob` is sync. commit b15c304a3125dd023fd90990e6225a7303f3596f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:42:45 2018 -0500 Make `null` check strict Appeases JSHint. ESLint has a nice `smart` option for `eqeqeq` rule: https://eslint.org/docs/rules/eqeqeq#smart commit ea70b92d9b18201758e11fdc25b09afc97b50055 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 15:23:58 2018 -0500 Use `Canvas::toDataURL` to preserve `ImageView` logic This way, all the other code paths remain untouched in case we want to remove the auto-orient code once Chrome supports the `image-orientation` CSS property. See: - #998 - https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation commit 62fd744f9f27d951573a68d2cdfe7ba2a3784b41 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:38:04 2018 -0500 Use CSS to constrain auto-oriented images commit f4d3392687168c237441b29140c7968b49dbef9e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:35:02 2018 -0500 Replace `ImageView` `el` with auto-oriented `canvas` See: #998 commit 1602d7f610e4993ad1291f88197f9ead1e25e776 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:48 2018 -0500 Pass `Blob` to `View` (for `ImageView`) This allows us to do JPEG auto-orientation based on EXIF metadata. commit e6a414f2b2a80da1137b839b348a38510efd04bb Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:12 2018 -0500 :hocho: Remove newline commit 5f0d9570d7862fc428ff89c2ecfd332a744537e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:17:02 2018 -0500 Expose `blueimp-load-image` as `window.loadImage` commit 1e1c62fe2f6a76dbcf1998dd468c26187c9871dc Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:16:46 2018 -0500 Add `blueimp-load-image` npm dependency commit ad17fa8a68a21ca5ddec336801b8568009bef3d4 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:14:40 2018 -0500 Remove `blueimp-load-image` Bower dependency
2018-02-21 15:26:59 +00:00
body,
conversationId: this.id,
quote,
2019-01-16 03:03:56 +00:00
preview,
2018-03-14 20:00:12 +00:00
attachments,
Auto-orient image attachments based on EXIF metadata As described in #998, images are sometimes displayed with an incorrect orientation. This is because cameras often write files in the native sensor byte order and attach the `Orientation` EXIF metadata to tell end-user devices how to display the images based on the original author’s capture orientation. Electron/Chromium (and therefore Signal Desktop) currently doesn’t support applying this metadata for `<img>` tags, e.g. CSS `image-orientation: from- image`. As a workaround, this change uses the `loadImage` library with the `orientation: true` flag to auto-orient images ~~before display~~ upon receipt and before sending. **Changes** - [x] ~~Auto-orient images during display in message list view~~ - [x] Ensure image is not displayed until loaded (to prevent layout reflow) . - [x] Auto-orient images upon receipt and before storing in IndexedDB (~~or preserve original data until Chromium offers native fix?~~) - [x] Auto-orient images in compose area preview. - [x] ~~Auto-orient images in lightbox view~~ - [x] Auto-orient images before sending / storage. - [x] Add EditorConfig for sharing code styles across editors. - [x] Fix ESLint ignore file. - [x] Change `function-paren-newline` ESLint rule from `consistent` to `multiline`. - [x] Add `operator-linebreak` ESLint rule for consistency. - [x] Added `blob-util` dependency for converting between array buffers, blobs, etc. - [x] Extracted `createMessageHandler` to consolidate logic for `onMessageReceived` and `onSentMessage`. - [x] Introduce `async` / `await` to simplify async coding (restore control flow for branching, loops, and exceptions). - [x] Introduce `window.Signal` namespace for exposing ES2015+ CommonJS modules. - [x] Introduce rudimentary `Message` and `Attachment` types to begin defining a schema and versioning. This will allow us to track which changes, e.g. auto-orient JPEGs, per message / attachment as well as which fields are stored. - [x] Renamed `window.dataURLtoBlob` to `window.dataURLToBlobSync` to both fix the strange `camelCase` as well as to highlight that this operation is synchronous and therefore blocks the user thread. - [x] Normalize all JPEG MIME types to `image/jpeg`, eliminating the invalid `image/jpg`. - [x] Add `npm run test-modules` command for testing non-browser specific CommonJS modules. - **Stretch Goals** - [ ] ~~Restrict `autoOrientImage` to `Blob` to narrow API interface.~~ Do this once we use PureScript. - [ ] ~~Return non-JPEGs as no-op from `autoOrientImage`.~~ Skipping `autoOrientImage` for non-JPEGs altogether. - [ ] Retroactively auto-orient existing JPEG image attachments in the background. --- Fixes #998 --- - **Blog:** EXIF Orientation Handling Is a Ghetto: https://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ - **Chromium Bug:** EXIF orientation is ignored: https://bugs.chromium.org/p/chromium/issues/detail?id=56845 - **Chromium Bug:** Support for the CSS image-orientation CSS property: https://bugs.chromium.org/p/chromium/issues/detail?id=158753 --- commit ce5090b473a2448229dc38e4c3f15d7ad0137714 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 16 10:35:36 2018 -0500 Inline message descriptors commit 329036e59c138c1e950ec7c654eebd7d87076de5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:34:40 2018 -0500 Clarify order of operations Semantically, it makes more sense to do `getFile` before `clearForm` even though it seems to work either way. commit f9d4cfb2ba0d8aa308b0923bbe6066ea34cb97bd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:18:26 2018 -0500 Simplify `operator-linebreak` configuration Enabling `before` caused more code changes and it turns out our previous configuration is already the default. commit db588997acdd90ed2ad829174ecbba744383c78b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:15:59 2018 -0500 Remove obsolete TODO commit 799c8817633f6afa0b731fc3b5434e463bd850e3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:12:18 2018 -0500 Enable ESLint `function-paren-newline` `multiline` Per discussion. commit b660b6bc8ef41df7601a411213d6cda80821df87 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:10:48 2018 -0500 Use `messageDescriptor.id` not `source` commit 5e7309d176f4a7e97d3dc4c738e6b0ccd4792871 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:29:01 2018 -0500 Remove unnecessary `eslint-env` commit 393b3da55eabd7413596c86cc3971b063a0efe31 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:19:17 2018 -0500 Refactor `onSentMessage` and `onMessageReceived` Since they are so similar, we create the handlers using new `createMessageHandler` function. This allows us to ensure both synced and received messages go through schema upgrade pipeline. commit b3db0bf179c9a5bea96480cde28c6fa7193ac117 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:18:21 2018 -0500 Add `Message` descriptor functions commit 8febf125b1b42fe4ae1888dd50fcee2749dc1ff0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 14:46:56 2018 -0500 Fix typo commit 98d951ef77bd578b313a4ff4b496b793e82e88d5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:39 2018 -0500 Remove `promises` reference commit a0e9559ed5bed947dabf28cb672e63d39948d854 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:13 2018 -0500 Fix `AttachmentView::mediaType` fall-through commit 67be916a83951b8a1f9b22efe78a6da6b1825f38 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:03:41 2018 -0500 Remove minor TODOs commit 0af186e118256b62905de38487ffacc41693ff47 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:41 2018 -0500 Enable ESLint for `js/views/attachment_view.js` commit 28a2dc5b8a28e1a087924fdc7275bf7d9a577b92 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:12 2018 -0500 Remove dynamic type checks commit f4ce36fcfc2737de32d911cd6103f889097813f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:27:56 2018 -0500 Rename `process` to `upgradeSchema` - `Message.process` -> `Message.upgradeSchema` - `Attachment.process` -> `Attachment.upgradeSchema` - `Attachment::processVersion` -> `Attachment::schemaVersion` Document version history. commit 41b92c0a31050ba05ddb1c43171d651f3568b9ac Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:11:50 2018 -0500 Add `operator-linebreak` ESLint rule Based on the following discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r168029106 commit 462defbe55879060fe25bc69103d4429bae2b2f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:01:30 2018 -0500 Add missing `await` for `ConversationController.getOrCreateAndWait` Tested this by setting `if` condition to `true` and confirming it works. It turns rotating a profile key is more involved and might require registering a new account according to Matthew. commit c08058ee4b883b3e23a40683de802ac81ed74874 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 16:32:24 2018 -0500 Convert `FileList` to `Array` commit 70a6c4201925f57be1f94d9da3547fdefc7bbb53 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:46:34 2018 -0500 :art: Fix lint errors commit 2ca7cdbc31d4120d6c6a838a6dcf43bc209d9788 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:07:09 2018 -0500 Skip `autoOrientImage` for non-JPEG images commit 58eac383013c16ca363a4ed33dca5c7ba61284e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:55:35 2018 -0500 Move new-style modules to `window.Signal` namespace commit 02c9328877dce289d6116a18b1c223891bd3cd0b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:35:23 2018 -0500 Extract `npm run test-modules` command commit 2c708eb94fba468b81ea9427734896114f5a7807 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:51 2018 -0500 Extract `Message.process` commit 4a2e52f68a77536a0fa04aa3c29ad3e541a8fa7e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:12 2018 -0500 Fix EditorConfig commit a346bab5db082720f5d47363f06301380e870425 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:13:02 2018 -0500 Remove `vim` directives on ESLint-ed files commit 7ec885c6359e495b407d5bc3eac9431d47c37fc6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:08:24 2018 -0500 Remove CSP whitelisting of `blob:` We no longer use `autoOrientImage` using blob URLs. Bring this back if we decide to auto-orient legacy attachments. commit 879b6f58f4a3f4a9ed6915af6b1be46c1e90e0ca Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:57:05 2018 -0500 Use `Message` type to determine send function Throws on invalid message type. commit 5203d945c98fd2562ae4e22c5c9838d27dec305b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:48 2018 -0500 Whitelist `Whisper` global commit 8ad0b066a3690d3382b86bf6ac00c03df7d1e20b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:32 2018 -0500 Add `Whisper.Types` namespace This avoids namespace collision for `Whisper.Message`. commit 785a949fce2656ca7dcaf0869d6b9e0648114e80 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:55:43 2018 -0500 Add `Message` type commit 674a7357abf0dcc365455695d56c0479998ebf27 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:35:23 2018 -0500 Run ESLint on `Conversation::sendMessage` commit cd985aa700caa80946245b17ea1b856449f152a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:34:38 2018 -0500 Document type signature of `FileInputView::readFile` commit d70d70e52c49588a1dc9833dfe5dd7128e13607f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:31:16 2018 -0500 Move attachment processing closer to sending This helps ensure processing happens uniformly, regardless of which code paths are taken to send an attachment. commit 532ac3e273a26b97f831247f9ee3412621b5c112 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:22:29 2018 -0500 Process attachment before it’s sent Picked this place since it already had various async steps, similar to `onMessageReceived` for the incoming `Attachment.process`. Could we try have this live closer to where we store it in IndexedDB, e.g. `Conversation::sendMessage`? commit a4582ae2fb6e1d3487131ba1f8fa6a00170cb32c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:21:42 2018 -0500 Refactor `getFile` and `getFiles` Lint them using ESLint. commit 07e9114e65046d791fc4f6ed90d6e2e938ad559d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:37:31 2018 -0500 Document incoming and outgoing attachments fields Note how outgoing message attachments only have 4 fields. This presumably means the others are not used in our code and could be discarded for simplicity. commit fdc3ef289d6ec1be344a12d496839d5ba747bb6a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:36:21 2018 -0500 Highlight that `dataURLToBlob` is synchronous commit b9c6bf600fcecedfd649ef2ae3c8629cced4e45a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:35:49 2018 -0500 Add EditorConfig configuration commit e56101e229d56810c8e31ad7289043a152c6c449 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:34:23 2018 -0500 Replace custom with `blob-util` functions IMPORTANT: All of them are async so we need to use `await`, otherwise we get strange or silent errors. commit f95150f6a9569fabcb31f3acd9f6b7bf50b5d145 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:17:30 2018 -0500 Revert "Replace custom functions with `blob-util`" This reverts commit 8a81e9c01bfe80c0e1bf76737092206c06949512. commit 33860d93f3d30ec55c32f3f4a58729df2eb43f0d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:13:02 2018 -0500 Revert "Replace `blueimp-canvas-to-blob` with `blob-util`" This reverts commit 31b3e853e4afc78fe80995921aa4152d9f6e4783. commit 7a0ba6fed622d76a3c39c7f03de541a7edb5b8dd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:12:58 2018 -0500 Replace `blueimp-canvas-to-blob` with `blob-util` commit 47a5f2bfd8b3f546e27e8d2b7e1969755d825a66 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:55:34 2018 -0500 Replace custom functions with `blob-util` commit 1cfa0efdb4fb1265369e2bf243c21f04f044fa01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:47:02 2018 -0500 Add `blob-util` dependency commit 9ac26be1bd783cd5070d886de107dd3ad9c91ad1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:44 2018 -0500 Document why we drop original image data during auto-orient commit 4136d6c382b99f41760a4da519d0db537fa7de8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:27 2018 -0500 Extract `DEFAULT_JPEG_QUALITY` commit 4a7156327eb5f94dba80cb300b344ac591226b0e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:37:11 2018 -0500 Drop support for invalid `image/jpg` MIME type commit 69fe96581f25413194032232f1bf704312e4754c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:30 2018 -0500 Document `window.onInvalidStateError` global commit a48ba1c77458da38583ee9cd488f70a59f6ee0fd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:04 2018 -0500 Selectively run ESLint on `js/background.js` Enabling ESLint on a per function basis allows us to incrementally improve the codebase without requiring large and potentially risky refactorings. commit e6d1cf826befc17ad4ec72fda8e761701665635e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:16:23 2018 -0500 Move async attachment processing to `onMessageReceived` We previously processed attachments in `handleDataMessage` which is mostly a synchronous function, except for the saving of the model. Moving the processing into the already async `onMessageReceived` improves code clarity. commit be6ca2a9aae5b59c360817deb1e18d39d705755e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:49 2018 -0500 Document import of ES2015+ modules commit eaaf7c41608fb988b8f4bbaa933cff110115610e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:29 2018 -0500 :art: Fix lint error commit a25b0e2e3d0f72c6a7bf0a15683f02450d5209ee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:13:57 2018 -0500 :art: Organize `require`s commit e0cc3d8fab6529d01b388acddf8605908c3d236b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:07:17 2018 -0500 Implement attachment process version Instead of keeping track of last normalization (processing) date, we now keep track of an internal processing version that will help us understand what kind of processing has already been completed for a given attachment. This will let us retroactively upgrade existing attachments. As we add more processing steps, we can build a processing pipeline that can convert any attachment processing version into a higher one, e.g. 4 -> 5 -> 6 -> 7. commit ad9083d0fdb880bc518e02251e51a39f7e1c585f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:50:31 2018 -0500 Ignore ES2015+ files during JSCS linting commit 96641205f734927aaebc2342d977c555799c3e3b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:48:07 2018 -0500 Improve ESLint ignore rules Apparently, using unqualified `/**` patterns prevents `!` include patterns. Using qualified glob patterns, e.g. `js/models/**/*.js`, lets us work around this. commit 255e0ab15bd1a0ca8ca5746e42d23977c8765d01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:44:59 2018 -0500 :abc: ESLint ignored files commit ebcb70258a26f234bd602072ac7c0a1913128132 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:47 2018 -0500 Whitelist `browser` environment for ESLint commit 3eaace6f3a21421c5aaaaf01592408c7ed83ecd3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:05 2018 -0500 Use `MIME` module commit ba2cf7770e614415733414a2dcc48f110b929892 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:32:54 2018 -0500 :art: Fix lint errors commit 65acc86e8580e88f7a6611eb4b8fa5d7291f7a3f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:30:42 2018 -0500 Add ES2015+ files to JSHint ignored list commit 8b6494ae6c9247acdfa059a9b361ec5ffcdb39f0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:29:20 2018 -0500 Document potentially unexpected `autoScale` behavior commit 8b4c69b2002d1777d3621be10f92cbf432f9d4d6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:26:47 2018 -0500 Test CommonJS modules separately Not sure how to test them as part of Grunt `unit-tests` task as `test/index.html` doesn’t allow for inclusion of CommonJS modules that use `require`. The tests are silently skipped. commit 213400e4b2bba3efee856a25b40e269221c3c39d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:24:27 2018 -0500 Add `MIME` type module commit 37a726e4fb4b3ed65914463122a5662847b5adee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:18:05 2018 -0500 Return proper `Error` from `blobArrayToBuffer` commit 164752db5612220e4dcf58d57bcd682cb489a399 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:15:41 2018 -0500 :art: Fix ESLint errors commit d498dd79a067c75098dd3179814c914780e5cb4f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:14:33 2018 -0500 Update `Attachment` type field definitions commit 141155a1533ff8fb616b70ea313432781bbebffd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:12:50 2018 -0500 Move `blueimp-canvas-to-blob` from Bower to npm commit 7ccb833e5d286ddd6235d3e491c62ac1e4544510 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:33:50 2018 -0500 :art: Clarify data flow commit e7da41591fde5a830467bebf1b6f51c1f7293e74 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:31:21 2018 -0500 Use `blobUrl` for consistency commit 523a80eefe0e2858aa1fb2bb9539ec44da502963 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:28:06 2018 -0500 Remove just-in-time image auto-orient for lightbox We can bring this back if our users would like auto-orient for old attachments. commit 0739feae9c47dd523c10740d6cdf746d539f270c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:27:21 2018 -0500 Remove just-in-time auto-orient of message attachments We can bring this back if our users would like auto-orient for old attachments. But better yet, we might implement this as database migration. commit ed43c66f92830ee233d5a94d0545eea4da43894d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:26:24 2018 -0500 Auto-orient JPEG attachments upon receipt commit e2eb8e36b017b048d57602fca14e45d657e0e1a1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:25:26 2018 -0500 Expose `Attachment` type through `Whisper.Attachment` commit 9638fbc987b84f143ca34211dc4666d96248ea2f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:39 2018 -0500 Use `contentType` from `model` commit 032c0ced46c3876cb9474b26f9d53d6f1c6b16a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:04 2018 -0500 Return `Error` object for `autoOrientImage` failures commit ff04bad8510c4b21aef350bed2b1887d0e055b98 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:22:32 2018 -0500 Add `options` for `autoOrientImage` output type / quality commit 87745b5586d1e182b51c9f9bc5e4eaf6dbc16722 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:18:46 2018 -0500 Add `Attachment` type Defines various functions on attachments, e.g. normalization (auto-orient JPEGs, etc.) commit de27fdc10a53bc8882a9c978e82265db9ac6d6f5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:16:34 2018 -0500 Add `yarn grunt` shortcut This allows us to use local `grunt-cli` for `grunt dev`. commit 59974db5a5da0d8f4cdc8ce5c4e3c974ecd5e754 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:10:11 2018 -0500 Improve readability commit b5ba96f1e6f40f2e1fa77490c583217768e1f412 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:08:12 2018 -0500 Use `snake_case` for module names Prevents problems across case-sensitive and case-insensitive file systems. We can work around this in the future using a lint rule such as `eslint-plugin-require-path-exists`. See discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r167365931 commit 48c5d3155c96ef628b00d99b52975e580d1d5501 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:05:44 2018 -0500 :art: Use destructuring commit 4822f49f22382a99ebf142b337375f7c25251d76 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:41:40 2018 -0500 Auto-orient images in lightbox view commit 7317110809677dddbbef3fadbf912cdba1c010bf Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:40:14 2018 -0500 Document magic number for escape key commit c790d07389a7d0bbf5298de83dbcfa8be1e7696b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:38:35 2018 -0500 Make second `View` argument an `options` object commit fbe010bb63d0088af9dfe11f153437fab34247e0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:27:40 2018 -0500 Allow `loadImage` to fetch `blob://` URLs commit ec35710d002b019a273eeb48f94dcaf2babe5d96 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:48 2018 -0500 :art: Shorten `autoOrientImage` import commit d07433e3cf316c6a143a0c9393ba26df9e3af17b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:19 2018 -0500 Make `autoOrientImage` module standalone commit c285bf5e33cdf10e0ef71e72cd6f55aef0df96ef Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:55:44 2018 -0500 Replace `loadImage` with `autoOrientImage` commit 44318549235af01fd061c25f557c93fd21cebb7a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:53:23 2018 -0500 Add `autoOrientImage` module This module exposes `loadImage` with a `Promise` based interface and pre- populates `orientation: true` option to auto-orient input. Returns data URL as string. The module uses a named export as refactoring references of modules with `default` (`module.exports`) export references can be error-prone. See: https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html commit c77063afc6366fe49615052796fe46f9b369de39 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:44:30 2018 -0500 Auto-orient preview images See: #998 commit 06dba5eb8f662c11af3a9ba8395bb453ab2e5f8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:43:23 2018 -0500 TODO: Use native `Canvas::toBlob` One challenge is that `Canvas::toBlob` is async whereas `dataURLtoBlob` is sync. commit b15c304a3125dd023fd90990e6225a7303f3596f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:42:45 2018 -0500 Make `null` check strict Appeases JSHint. ESLint has a nice `smart` option for `eqeqeq` rule: https://eslint.org/docs/rules/eqeqeq#smart commit ea70b92d9b18201758e11fdc25b09afc97b50055 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 15:23:58 2018 -0500 Use `Canvas::toDataURL` to preserve `ImageView` logic This way, all the other code paths remain untouched in case we want to remove the auto-orient code once Chrome supports the `image-orientation` CSS property. See: - #998 - https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation commit 62fd744f9f27d951573a68d2cdfe7ba2a3784b41 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:38:04 2018 -0500 Use CSS to constrain auto-oriented images commit f4d3392687168c237441b29140c7968b49dbef9e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:35:02 2018 -0500 Replace `ImageView` `el` with auto-oriented `canvas` See: #998 commit 1602d7f610e4993ad1291f88197f9ead1e25e776 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:48 2018 -0500 Pass `Blob` to `View` (for `ImageView`) This allows us to do JPEG auto-orientation based on EXIF metadata. commit e6a414f2b2a80da1137b839b348a38510efd04bb Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:12 2018 -0500 :hocho: Remove newline commit 5f0d9570d7862fc428ff89c2ecfd332a744537e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:17:02 2018 -0500 Expose `blueimp-load-image` as `window.loadImage` commit 1e1c62fe2f6a76dbcf1998dd468c26187c9871dc Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:16:46 2018 -0500 Add `blueimp-load-image` npm dependency commit ad17fa8a68a21ca5ddec336801b8568009bef3d4 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:14:40 2018 -0500 Remove `blueimp-load-image` Bower dependency
2018-02-21 15:26:59 +00:00
sent_at: now,
received_at: now,
expireTimer,
recipients,
sticker,
});
if (this.isPrivate()) {
messageWithSchema.destination = destination;
}
const attributes = {
...messageWithSchema,
id: window.getGuid(),
};
const model = this.addSingleMessage(attributes);
if (sticker) {
await addStickerPackReference(model.id, sticker.packId);
}
const message = MessageController.register(model.id, model);
await window.Signal.Data.saveMessage(message.attributes, {
forceSave: true,
Message: Whisper.Message,
});
2018-09-21 01:47:19 +00:00
this.set({
lastMessage: model.getNotificationText(),
lastMessageStatus: 'sending',
Auto-orient image attachments based on EXIF metadata As described in #998, images are sometimes displayed with an incorrect orientation. This is because cameras often write files in the native sensor byte order and attach the `Orientation` EXIF metadata to tell end-user devices how to display the images based on the original author’s capture orientation. Electron/Chromium (and therefore Signal Desktop) currently doesn’t support applying this metadata for `<img>` tags, e.g. CSS `image-orientation: from- image`. As a workaround, this change uses the `loadImage` library with the `orientation: true` flag to auto-orient images ~~before display~~ upon receipt and before sending. **Changes** - [x] ~~Auto-orient images during display in message list view~~ - [x] Ensure image is not displayed until loaded (to prevent layout reflow) . - [x] Auto-orient images upon receipt and before storing in IndexedDB (~~or preserve original data until Chromium offers native fix?~~) - [x] Auto-orient images in compose area preview. - [x] ~~Auto-orient images in lightbox view~~ - [x] Auto-orient images before sending / storage. - [x] Add EditorConfig for sharing code styles across editors. - [x] Fix ESLint ignore file. - [x] Change `function-paren-newline` ESLint rule from `consistent` to `multiline`. - [x] Add `operator-linebreak` ESLint rule for consistency. - [x] Added `blob-util` dependency for converting between array buffers, blobs, etc. - [x] Extracted `createMessageHandler` to consolidate logic for `onMessageReceived` and `onSentMessage`. - [x] Introduce `async` / `await` to simplify async coding (restore control flow for branching, loops, and exceptions). - [x] Introduce `window.Signal` namespace for exposing ES2015+ CommonJS modules. - [x] Introduce rudimentary `Message` and `Attachment` types to begin defining a schema and versioning. This will allow us to track which changes, e.g. auto-orient JPEGs, per message / attachment as well as which fields are stored. - [x] Renamed `window.dataURLtoBlob` to `window.dataURLToBlobSync` to both fix the strange `camelCase` as well as to highlight that this operation is synchronous and therefore blocks the user thread. - [x] Normalize all JPEG MIME types to `image/jpeg`, eliminating the invalid `image/jpg`. - [x] Add `npm run test-modules` command for testing non-browser specific CommonJS modules. - **Stretch Goals** - [ ] ~~Restrict `autoOrientImage` to `Blob` to narrow API interface.~~ Do this once we use PureScript. - [ ] ~~Return non-JPEGs as no-op from `autoOrientImage`.~~ Skipping `autoOrientImage` for non-JPEGs altogether. - [ ] Retroactively auto-orient existing JPEG image attachments in the background. --- Fixes #998 --- - **Blog:** EXIF Orientation Handling Is a Ghetto: https://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ - **Chromium Bug:** EXIF orientation is ignored: https://bugs.chromium.org/p/chromium/issues/detail?id=56845 - **Chromium Bug:** Support for the CSS image-orientation CSS property: https://bugs.chromium.org/p/chromium/issues/detail?id=158753 --- commit ce5090b473a2448229dc38e4c3f15d7ad0137714 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 16 10:35:36 2018 -0500 Inline message descriptors commit 329036e59c138c1e950ec7c654eebd7d87076de5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:34:40 2018 -0500 Clarify order of operations Semantically, it makes more sense to do `getFile` before `clearForm` even though it seems to work either way. commit f9d4cfb2ba0d8aa308b0923bbe6066ea34cb97bd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:18:26 2018 -0500 Simplify `operator-linebreak` configuration Enabling `before` caused more code changes and it turns out our previous configuration is already the default. commit db588997acdd90ed2ad829174ecbba744383c78b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:15:59 2018 -0500 Remove obsolete TODO commit 799c8817633f6afa0b731fc3b5434e463bd850e3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:12:18 2018 -0500 Enable ESLint `function-paren-newline` `multiline` Per discussion. commit b660b6bc8ef41df7601a411213d6cda80821df87 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:10:48 2018 -0500 Use `messageDescriptor.id` not `source` commit 5e7309d176f4a7e97d3dc4c738e6b0ccd4792871 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:29:01 2018 -0500 Remove unnecessary `eslint-env` commit 393b3da55eabd7413596c86cc3971b063a0efe31 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:19:17 2018 -0500 Refactor `onSentMessage` and `onMessageReceived` Since they are so similar, we create the handlers using new `createMessageHandler` function. This allows us to ensure both synced and received messages go through schema upgrade pipeline. commit b3db0bf179c9a5bea96480cde28c6fa7193ac117 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:18:21 2018 -0500 Add `Message` descriptor functions commit 8febf125b1b42fe4ae1888dd50fcee2749dc1ff0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 14:46:56 2018 -0500 Fix typo commit 98d951ef77bd578b313a4ff4b496b793e82e88d5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:39 2018 -0500 Remove `promises` reference commit a0e9559ed5bed947dabf28cb672e63d39948d854 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:13 2018 -0500 Fix `AttachmentView::mediaType` fall-through commit 67be916a83951b8a1f9b22efe78a6da6b1825f38 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:03:41 2018 -0500 Remove minor TODOs commit 0af186e118256b62905de38487ffacc41693ff47 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:41 2018 -0500 Enable ESLint for `js/views/attachment_view.js` commit 28a2dc5b8a28e1a087924fdc7275bf7d9a577b92 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:12 2018 -0500 Remove dynamic type checks commit f4ce36fcfc2737de32d911cd6103f889097813f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:27:56 2018 -0500 Rename `process` to `upgradeSchema` - `Message.process` -> `Message.upgradeSchema` - `Attachment.process` -> `Attachment.upgradeSchema` - `Attachment::processVersion` -> `Attachment::schemaVersion` Document version history. commit 41b92c0a31050ba05ddb1c43171d651f3568b9ac Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:11:50 2018 -0500 Add `operator-linebreak` ESLint rule Based on the following discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r168029106 commit 462defbe55879060fe25bc69103d4429bae2b2f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:01:30 2018 -0500 Add missing `await` for `ConversationController.getOrCreateAndWait` Tested this by setting `if` condition to `true` and confirming it works. It turns rotating a profile key is more involved and might require registering a new account according to Matthew. commit c08058ee4b883b3e23a40683de802ac81ed74874 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 16:32:24 2018 -0500 Convert `FileList` to `Array` commit 70a6c4201925f57be1f94d9da3547fdefc7bbb53 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:46:34 2018 -0500 :art: Fix lint errors commit 2ca7cdbc31d4120d6c6a838a6dcf43bc209d9788 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:07:09 2018 -0500 Skip `autoOrientImage` for non-JPEG images commit 58eac383013c16ca363a4ed33dca5c7ba61284e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:55:35 2018 -0500 Move new-style modules to `window.Signal` namespace commit 02c9328877dce289d6116a18b1c223891bd3cd0b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:35:23 2018 -0500 Extract `npm run test-modules` command commit 2c708eb94fba468b81ea9427734896114f5a7807 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:51 2018 -0500 Extract `Message.process` commit 4a2e52f68a77536a0fa04aa3c29ad3e541a8fa7e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:12 2018 -0500 Fix EditorConfig commit a346bab5db082720f5d47363f06301380e870425 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:13:02 2018 -0500 Remove `vim` directives on ESLint-ed files commit 7ec885c6359e495b407d5bc3eac9431d47c37fc6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:08:24 2018 -0500 Remove CSP whitelisting of `blob:` We no longer use `autoOrientImage` using blob URLs. Bring this back if we decide to auto-orient legacy attachments. commit 879b6f58f4a3f4a9ed6915af6b1be46c1e90e0ca Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:57:05 2018 -0500 Use `Message` type to determine send function Throws on invalid message type. commit 5203d945c98fd2562ae4e22c5c9838d27dec305b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:48 2018 -0500 Whitelist `Whisper` global commit 8ad0b066a3690d3382b86bf6ac00c03df7d1e20b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:32 2018 -0500 Add `Whisper.Types` namespace This avoids namespace collision for `Whisper.Message`. commit 785a949fce2656ca7dcaf0869d6b9e0648114e80 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:55:43 2018 -0500 Add `Message` type commit 674a7357abf0dcc365455695d56c0479998ebf27 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:35:23 2018 -0500 Run ESLint on `Conversation::sendMessage` commit cd985aa700caa80946245b17ea1b856449f152a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:34:38 2018 -0500 Document type signature of `FileInputView::readFile` commit d70d70e52c49588a1dc9833dfe5dd7128e13607f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:31:16 2018 -0500 Move attachment processing closer to sending This helps ensure processing happens uniformly, regardless of which code paths are taken to send an attachment. commit 532ac3e273a26b97f831247f9ee3412621b5c112 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:22:29 2018 -0500 Process attachment before it’s sent Picked this place since it already had various async steps, similar to `onMessageReceived` for the incoming `Attachment.process`. Could we try have this live closer to where we store it in IndexedDB, e.g. `Conversation::sendMessage`? commit a4582ae2fb6e1d3487131ba1f8fa6a00170cb32c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:21:42 2018 -0500 Refactor `getFile` and `getFiles` Lint them using ESLint. commit 07e9114e65046d791fc4f6ed90d6e2e938ad559d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:37:31 2018 -0500 Document incoming and outgoing attachments fields Note how outgoing message attachments only have 4 fields. This presumably means the others are not used in our code and could be discarded for simplicity. commit fdc3ef289d6ec1be344a12d496839d5ba747bb6a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:36:21 2018 -0500 Highlight that `dataURLToBlob` is synchronous commit b9c6bf600fcecedfd649ef2ae3c8629cced4e45a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:35:49 2018 -0500 Add EditorConfig configuration commit e56101e229d56810c8e31ad7289043a152c6c449 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:34:23 2018 -0500 Replace custom with `blob-util` functions IMPORTANT: All of them are async so we need to use `await`, otherwise we get strange or silent errors. commit f95150f6a9569fabcb31f3acd9f6b7bf50b5d145 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:17:30 2018 -0500 Revert "Replace custom functions with `blob-util`" This reverts commit 8a81e9c01bfe80c0e1bf76737092206c06949512. commit 33860d93f3d30ec55c32f3f4a58729df2eb43f0d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:13:02 2018 -0500 Revert "Replace `blueimp-canvas-to-blob` with `blob-util`" This reverts commit 31b3e853e4afc78fe80995921aa4152d9f6e4783. commit 7a0ba6fed622d76a3c39c7f03de541a7edb5b8dd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:12:58 2018 -0500 Replace `blueimp-canvas-to-blob` with `blob-util` commit 47a5f2bfd8b3f546e27e8d2b7e1969755d825a66 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:55:34 2018 -0500 Replace custom functions with `blob-util` commit 1cfa0efdb4fb1265369e2bf243c21f04f044fa01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:47:02 2018 -0500 Add `blob-util` dependency commit 9ac26be1bd783cd5070d886de107dd3ad9c91ad1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:44 2018 -0500 Document why we drop original image data during auto-orient commit 4136d6c382b99f41760a4da519d0db537fa7de8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:27 2018 -0500 Extract `DEFAULT_JPEG_QUALITY` commit 4a7156327eb5f94dba80cb300b344ac591226b0e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:37:11 2018 -0500 Drop support for invalid `image/jpg` MIME type commit 69fe96581f25413194032232f1bf704312e4754c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:30 2018 -0500 Document `window.onInvalidStateError` global commit a48ba1c77458da38583ee9cd488f70a59f6ee0fd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:04 2018 -0500 Selectively run ESLint on `js/background.js` Enabling ESLint on a per function basis allows us to incrementally improve the codebase without requiring large and potentially risky refactorings. commit e6d1cf826befc17ad4ec72fda8e761701665635e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:16:23 2018 -0500 Move async attachment processing to `onMessageReceived` We previously processed attachments in `handleDataMessage` which is mostly a synchronous function, except for the saving of the model. Moving the processing into the already async `onMessageReceived` improves code clarity. commit be6ca2a9aae5b59c360817deb1e18d39d705755e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:49 2018 -0500 Document import of ES2015+ modules commit eaaf7c41608fb988b8f4bbaa933cff110115610e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:29 2018 -0500 :art: Fix lint error commit a25b0e2e3d0f72c6a7bf0a15683f02450d5209ee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:13:57 2018 -0500 :art: Organize `require`s commit e0cc3d8fab6529d01b388acddf8605908c3d236b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:07:17 2018 -0500 Implement attachment process version Instead of keeping track of last normalization (processing) date, we now keep track of an internal processing version that will help us understand what kind of processing has already been completed for a given attachment. This will let us retroactively upgrade existing attachments. As we add more processing steps, we can build a processing pipeline that can convert any attachment processing version into a higher one, e.g. 4 -> 5 -> 6 -> 7. commit ad9083d0fdb880bc518e02251e51a39f7e1c585f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:50:31 2018 -0500 Ignore ES2015+ files during JSCS linting commit 96641205f734927aaebc2342d977c555799c3e3b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:48:07 2018 -0500 Improve ESLint ignore rules Apparently, using unqualified `/**` patterns prevents `!` include patterns. Using qualified glob patterns, e.g. `js/models/**/*.js`, lets us work around this. commit 255e0ab15bd1a0ca8ca5746e42d23977c8765d01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:44:59 2018 -0500 :abc: ESLint ignored files commit ebcb70258a26f234bd602072ac7c0a1913128132 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:47 2018 -0500 Whitelist `browser` environment for ESLint commit 3eaace6f3a21421c5aaaaf01592408c7ed83ecd3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:05 2018 -0500 Use `MIME` module commit ba2cf7770e614415733414a2dcc48f110b929892 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:32:54 2018 -0500 :art: Fix lint errors commit 65acc86e8580e88f7a6611eb4b8fa5d7291f7a3f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:30:42 2018 -0500 Add ES2015+ files to JSHint ignored list commit 8b6494ae6c9247acdfa059a9b361ec5ffcdb39f0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:29:20 2018 -0500 Document potentially unexpected `autoScale` behavior commit 8b4c69b2002d1777d3621be10f92cbf432f9d4d6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:26:47 2018 -0500 Test CommonJS modules separately Not sure how to test them as part of Grunt `unit-tests` task as `test/index.html` doesn’t allow for inclusion of CommonJS modules that use `require`. The tests are silently skipped. commit 213400e4b2bba3efee856a25b40e269221c3c39d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:24:27 2018 -0500 Add `MIME` type module commit 37a726e4fb4b3ed65914463122a5662847b5adee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:18:05 2018 -0500 Return proper `Error` from `blobArrayToBuffer` commit 164752db5612220e4dcf58d57bcd682cb489a399 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:15:41 2018 -0500 :art: Fix ESLint errors commit d498dd79a067c75098dd3179814c914780e5cb4f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:14:33 2018 -0500 Update `Attachment` type field definitions commit 141155a1533ff8fb616b70ea313432781bbebffd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:12:50 2018 -0500 Move `blueimp-canvas-to-blob` from Bower to npm commit 7ccb833e5d286ddd6235d3e491c62ac1e4544510 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:33:50 2018 -0500 :art: Clarify data flow commit e7da41591fde5a830467bebf1b6f51c1f7293e74 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:31:21 2018 -0500 Use `blobUrl` for consistency commit 523a80eefe0e2858aa1fb2bb9539ec44da502963 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:28:06 2018 -0500 Remove just-in-time image auto-orient for lightbox We can bring this back if our users would like auto-orient for old attachments. commit 0739feae9c47dd523c10740d6cdf746d539f270c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:27:21 2018 -0500 Remove just-in-time auto-orient of message attachments We can bring this back if our users would like auto-orient for old attachments. But better yet, we might implement this as database migration. commit ed43c66f92830ee233d5a94d0545eea4da43894d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:26:24 2018 -0500 Auto-orient JPEG attachments upon receipt commit e2eb8e36b017b048d57602fca14e45d657e0e1a1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:25:26 2018 -0500 Expose `Attachment` type through `Whisper.Attachment` commit 9638fbc987b84f143ca34211dc4666d96248ea2f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:39 2018 -0500 Use `contentType` from `model` commit 032c0ced46c3876cb9474b26f9d53d6f1c6b16a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:04 2018 -0500 Return `Error` object for `autoOrientImage` failures commit ff04bad8510c4b21aef350bed2b1887d0e055b98 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:22:32 2018 -0500 Add `options` for `autoOrientImage` output type / quality commit 87745b5586d1e182b51c9f9bc5e4eaf6dbc16722 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:18:46 2018 -0500 Add `Attachment` type Defines various functions on attachments, e.g. normalization (auto-orient JPEGs, etc.) commit de27fdc10a53bc8882a9c978e82265db9ac6d6f5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:16:34 2018 -0500 Add `yarn grunt` shortcut This allows us to use local `grunt-cli` for `grunt dev`. commit 59974db5a5da0d8f4cdc8ce5c4e3c974ecd5e754 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:10:11 2018 -0500 Improve readability commit b5ba96f1e6f40f2e1fa77490c583217768e1f412 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:08:12 2018 -0500 Use `snake_case` for module names Prevents problems across case-sensitive and case-insensitive file systems. We can work around this in the future using a lint rule such as `eslint-plugin-require-path-exists`. See discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r167365931 commit 48c5d3155c96ef628b00d99b52975e580d1d5501 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:05:44 2018 -0500 :art: Use destructuring commit 4822f49f22382a99ebf142b337375f7c25251d76 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:41:40 2018 -0500 Auto-orient images in lightbox view commit 7317110809677dddbbef3fadbf912cdba1c010bf Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:40:14 2018 -0500 Document magic number for escape key commit c790d07389a7d0bbf5298de83dbcfa8be1e7696b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:38:35 2018 -0500 Make second `View` argument an `options` object commit fbe010bb63d0088af9dfe11f153437fab34247e0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:27:40 2018 -0500 Allow `loadImage` to fetch `blob://` URLs commit ec35710d002b019a273eeb48f94dcaf2babe5d96 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:48 2018 -0500 :art: Shorten `autoOrientImage` import commit d07433e3cf316c6a143a0c9393ba26df9e3af17b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:19 2018 -0500 Make `autoOrientImage` module standalone commit c285bf5e33cdf10e0ef71e72cd6f55aef0df96ef Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:55:44 2018 -0500 Replace `loadImage` with `autoOrientImage` commit 44318549235af01fd061c25f557c93fd21cebb7a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:53:23 2018 -0500 Add `autoOrientImage` module This module exposes `loadImage` with a `Promise` based interface and pre- populates `orientation: true` option to auto-orient input. Returns data URL as string. The module uses a named export as refactoring references of modules with `default` (`module.exports`) export references can be error-prone. See: https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html commit c77063afc6366fe49615052796fe46f9b369de39 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:44:30 2018 -0500 Auto-orient preview images See: #998 commit 06dba5eb8f662c11af3a9ba8395bb453ab2e5f8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:43:23 2018 -0500 TODO: Use native `Canvas::toBlob` One challenge is that `Canvas::toBlob` is async whereas `dataURLtoBlob` is sync. commit b15c304a3125dd023fd90990e6225a7303f3596f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:42:45 2018 -0500 Make `null` check strict Appeases JSHint. ESLint has a nice `smart` option for `eqeqeq` rule: https://eslint.org/docs/rules/eqeqeq#smart commit ea70b92d9b18201758e11fdc25b09afc97b50055 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 15:23:58 2018 -0500 Use `Canvas::toDataURL` to preserve `ImageView` logic This way, all the other code paths remain untouched in case we want to remove the auto-orient code once Chrome supports the `image-orientation` CSS property. See: - #998 - https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation commit 62fd744f9f27d951573a68d2cdfe7ba2a3784b41 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:38:04 2018 -0500 Use CSS to constrain auto-oriented images commit f4d3392687168c237441b29140c7968b49dbef9e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:35:02 2018 -0500 Replace `ImageView` `el` with auto-oriented `canvas` See: #998 commit 1602d7f610e4993ad1291f88197f9ead1e25e776 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:48 2018 -0500 Pass `Blob` to `View` (for `ImageView`) This allows us to do JPEG auto-orientation based on EXIF metadata. commit e6a414f2b2a80da1137b839b348a38510efd04bb Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:12 2018 -0500 :hocho: Remove newline commit 5f0d9570d7862fc428ff89c2ecfd332a744537e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:17:02 2018 -0500 Expose `blueimp-load-image` as `window.loadImage` commit 1e1c62fe2f6a76dbcf1998dd468c26187c9871dc Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:16:46 2018 -0500 Add `blueimp-load-image` npm dependency commit ad17fa8a68a21ca5ddec336801b8568009bef3d4 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:14:40 2018 -0500 Remove `blueimp-load-image` Bower dependency
2018-02-21 15:26:59 +00:00
active_at: now,
timestamp: now,
2019-03-12 00:20:16 +00:00
isArchived: false,
2019-08-07 00:40:25 +00:00
draft: null,
draftTimestamp: null,
Auto-orient image attachments based on EXIF metadata As described in #998, images are sometimes displayed with an incorrect orientation. This is because cameras often write files in the native sensor byte order and attach the `Orientation` EXIF metadata to tell end-user devices how to display the images based on the original author’s capture orientation. Electron/Chromium (and therefore Signal Desktop) currently doesn’t support applying this metadata for `<img>` tags, e.g. CSS `image-orientation: from- image`. As a workaround, this change uses the `loadImage` library with the `orientation: true` flag to auto-orient images ~~before display~~ upon receipt and before sending. **Changes** - [x] ~~Auto-orient images during display in message list view~~ - [x] Ensure image is not displayed until loaded (to prevent layout reflow) . - [x] Auto-orient images upon receipt and before storing in IndexedDB (~~or preserve original data until Chromium offers native fix?~~) - [x] Auto-orient images in compose area preview. - [x] ~~Auto-orient images in lightbox view~~ - [x] Auto-orient images before sending / storage. - [x] Add EditorConfig for sharing code styles across editors. - [x] Fix ESLint ignore file. - [x] Change `function-paren-newline` ESLint rule from `consistent` to `multiline`. - [x] Add `operator-linebreak` ESLint rule for consistency. - [x] Added `blob-util` dependency for converting between array buffers, blobs, etc. - [x] Extracted `createMessageHandler` to consolidate logic for `onMessageReceived` and `onSentMessage`. - [x] Introduce `async` / `await` to simplify async coding (restore control flow for branching, loops, and exceptions). - [x] Introduce `window.Signal` namespace for exposing ES2015+ CommonJS modules. - [x] Introduce rudimentary `Message` and `Attachment` types to begin defining a schema and versioning. This will allow us to track which changes, e.g. auto-orient JPEGs, per message / attachment as well as which fields are stored. - [x] Renamed `window.dataURLtoBlob` to `window.dataURLToBlobSync` to both fix the strange `camelCase` as well as to highlight that this operation is synchronous and therefore blocks the user thread. - [x] Normalize all JPEG MIME types to `image/jpeg`, eliminating the invalid `image/jpg`. - [x] Add `npm run test-modules` command for testing non-browser specific CommonJS modules. - **Stretch Goals** - [ ] ~~Restrict `autoOrientImage` to `Blob` to narrow API interface.~~ Do this once we use PureScript. - [ ] ~~Return non-JPEGs as no-op from `autoOrientImage`.~~ Skipping `autoOrientImage` for non-JPEGs altogether. - [ ] Retroactively auto-orient existing JPEG image attachments in the background. --- Fixes #998 --- - **Blog:** EXIF Orientation Handling Is a Ghetto: https://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ - **Chromium Bug:** EXIF orientation is ignored: https://bugs.chromium.org/p/chromium/issues/detail?id=56845 - **Chromium Bug:** Support for the CSS image-orientation CSS property: https://bugs.chromium.org/p/chromium/issues/detail?id=158753 --- commit ce5090b473a2448229dc38e4c3f15d7ad0137714 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 16 10:35:36 2018 -0500 Inline message descriptors commit 329036e59c138c1e950ec7c654eebd7d87076de5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:34:40 2018 -0500 Clarify order of operations Semantically, it makes more sense to do `getFile` before `clearForm` even though it seems to work either way. commit f9d4cfb2ba0d8aa308b0923bbe6066ea34cb97bd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:18:26 2018 -0500 Simplify `operator-linebreak` configuration Enabling `before` caused more code changes and it turns out our previous configuration is already the default. commit db588997acdd90ed2ad829174ecbba744383c78b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:15:59 2018 -0500 Remove obsolete TODO commit 799c8817633f6afa0b731fc3b5434e463bd850e3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:12:18 2018 -0500 Enable ESLint `function-paren-newline` `multiline` Per discussion. commit b660b6bc8ef41df7601a411213d6cda80821df87 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:10:48 2018 -0500 Use `messageDescriptor.id` not `source` commit 5e7309d176f4a7e97d3dc4c738e6b0ccd4792871 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:29:01 2018 -0500 Remove unnecessary `eslint-env` commit 393b3da55eabd7413596c86cc3971b063a0efe31 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:19:17 2018 -0500 Refactor `onSentMessage` and `onMessageReceived` Since they are so similar, we create the handlers using new `createMessageHandler` function. This allows us to ensure both synced and received messages go through schema upgrade pipeline. commit b3db0bf179c9a5bea96480cde28c6fa7193ac117 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:18:21 2018 -0500 Add `Message` descriptor functions commit 8febf125b1b42fe4ae1888dd50fcee2749dc1ff0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 14:46:56 2018 -0500 Fix typo commit 98d951ef77bd578b313a4ff4b496b793e82e88d5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:39 2018 -0500 Remove `promises` reference commit a0e9559ed5bed947dabf28cb672e63d39948d854 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:13 2018 -0500 Fix `AttachmentView::mediaType` fall-through commit 67be916a83951b8a1f9b22efe78a6da6b1825f38 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:03:41 2018 -0500 Remove minor TODOs commit 0af186e118256b62905de38487ffacc41693ff47 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:41 2018 -0500 Enable ESLint for `js/views/attachment_view.js` commit 28a2dc5b8a28e1a087924fdc7275bf7d9a577b92 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:12 2018 -0500 Remove dynamic type checks commit f4ce36fcfc2737de32d911cd6103f889097813f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:27:56 2018 -0500 Rename `process` to `upgradeSchema` - `Message.process` -> `Message.upgradeSchema` - `Attachment.process` -> `Attachment.upgradeSchema` - `Attachment::processVersion` -> `Attachment::schemaVersion` Document version history. commit 41b92c0a31050ba05ddb1c43171d651f3568b9ac Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:11:50 2018 -0500 Add `operator-linebreak` ESLint rule Based on the following discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r168029106 commit 462defbe55879060fe25bc69103d4429bae2b2f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:01:30 2018 -0500 Add missing `await` for `ConversationController.getOrCreateAndWait` Tested this by setting `if` condition to `true` and confirming it works. It turns rotating a profile key is more involved and might require registering a new account according to Matthew. commit c08058ee4b883b3e23a40683de802ac81ed74874 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 16:32:24 2018 -0500 Convert `FileList` to `Array` commit 70a6c4201925f57be1f94d9da3547fdefc7bbb53 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:46:34 2018 -0500 :art: Fix lint errors commit 2ca7cdbc31d4120d6c6a838a6dcf43bc209d9788 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:07:09 2018 -0500 Skip `autoOrientImage` for non-JPEG images commit 58eac383013c16ca363a4ed33dca5c7ba61284e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:55:35 2018 -0500 Move new-style modules to `window.Signal` namespace commit 02c9328877dce289d6116a18b1c223891bd3cd0b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:35:23 2018 -0500 Extract `npm run test-modules` command commit 2c708eb94fba468b81ea9427734896114f5a7807 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:51 2018 -0500 Extract `Message.process` commit 4a2e52f68a77536a0fa04aa3c29ad3e541a8fa7e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:12 2018 -0500 Fix EditorConfig commit a346bab5db082720f5d47363f06301380e870425 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:13:02 2018 -0500 Remove `vim` directives on ESLint-ed files commit 7ec885c6359e495b407d5bc3eac9431d47c37fc6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:08:24 2018 -0500 Remove CSP whitelisting of `blob:` We no longer use `autoOrientImage` using blob URLs. Bring this back if we decide to auto-orient legacy attachments. commit 879b6f58f4a3f4a9ed6915af6b1be46c1e90e0ca Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:57:05 2018 -0500 Use `Message` type to determine send function Throws on invalid message type. commit 5203d945c98fd2562ae4e22c5c9838d27dec305b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:48 2018 -0500 Whitelist `Whisper` global commit 8ad0b066a3690d3382b86bf6ac00c03df7d1e20b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:32 2018 -0500 Add `Whisper.Types` namespace This avoids namespace collision for `Whisper.Message`. commit 785a949fce2656ca7dcaf0869d6b9e0648114e80 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:55:43 2018 -0500 Add `Message` type commit 674a7357abf0dcc365455695d56c0479998ebf27 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:35:23 2018 -0500 Run ESLint on `Conversation::sendMessage` commit cd985aa700caa80946245b17ea1b856449f152a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:34:38 2018 -0500 Document type signature of `FileInputView::readFile` commit d70d70e52c49588a1dc9833dfe5dd7128e13607f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:31:16 2018 -0500 Move attachment processing closer to sending This helps ensure processing happens uniformly, regardless of which code paths are taken to send an attachment. commit 532ac3e273a26b97f831247f9ee3412621b5c112 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:22:29 2018 -0500 Process attachment before it’s sent Picked this place since it already had various async steps, similar to `onMessageReceived` for the incoming `Attachment.process`. Could we try have this live closer to where we store it in IndexedDB, e.g. `Conversation::sendMessage`? commit a4582ae2fb6e1d3487131ba1f8fa6a00170cb32c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:21:42 2018 -0500 Refactor `getFile` and `getFiles` Lint them using ESLint. commit 07e9114e65046d791fc4f6ed90d6e2e938ad559d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:37:31 2018 -0500 Document incoming and outgoing attachments fields Note how outgoing message attachments only have 4 fields. This presumably means the others are not used in our code and could be discarded for simplicity. commit fdc3ef289d6ec1be344a12d496839d5ba747bb6a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:36:21 2018 -0500 Highlight that `dataURLToBlob` is synchronous commit b9c6bf600fcecedfd649ef2ae3c8629cced4e45a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:35:49 2018 -0500 Add EditorConfig configuration commit e56101e229d56810c8e31ad7289043a152c6c449 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:34:23 2018 -0500 Replace custom with `blob-util` functions IMPORTANT: All of them are async so we need to use `await`, otherwise we get strange or silent errors. commit f95150f6a9569fabcb31f3acd9f6b7bf50b5d145 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:17:30 2018 -0500 Revert "Replace custom functions with `blob-util`" This reverts commit 8a81e9c01bfe80c0e1bf76737092206c06949512. commit 33860d93f3d30ec55c32f3f4a58729df2eb43f0d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:13:02 2018 -0500 Revert "Replace `blueimp-canvas-to-blob` with `blob-util`" This reverts commit 31b3e853e4afc78fe80995921aa4152d9f6e4783. commit 7a0ba6fed622d76a3c39c7f03de541a7edb5b8dd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:12:58 2018 -0500 Replace `blueimp-canvas-to-blob` with `blob-util` commit 47a5f2bfd8b3f546e27e8d2b7e1969755d825a66 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:55:34 2018 -0500 Replace custom functions with `blob-util` commit 1cfa0efdb4fb1265369e2bf243c21f04f044fa01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:47:02 2018 -0500 Add `blob-util` dependency commit 9ac26be1bd783cd5070d886de107dd3ad9c91ad1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:44 2018 -0500 Document why we drop original image data during auto-orient commit 4136d6c382b99f41760a4da519d0db537fa7de8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:27 2018 -0500 Extract `DEFAULT_JPEG_QUALITY` commit 4a7156327eb5f94dba80cb300b344ac591226b0e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:37:11 2018 -0500 Drop support for invalid `image/jpg` MIME type commit 69fe96581f25413194032232f1bf704312e4754c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:30 2018 -0500 Document `window.onInvalidStateError` global commit a48ba1c77458da38583ee9cd488f70a59f6ee0fd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:04 2018 -0500 Selectively run ESLint on `js/background.js` Enabling ESLint on a per function basis allows us to incrementally improve the codebase without requiring large and potentially risky refactorings. commit e6d1cf826befc17ad4ec72fda8e761701665635e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:16:23 2018 -0500 Move async attachment processing to `onMessageReceived` We previously processed attachments in `handleDataMessage` which is mostly a synchronous function, except for the saving of the model. Moving the processing into the already async `onMessageReceived` improves code clarity. commit be6ca2a9aae5b59c360817deb1e18d39d705755e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:49 2018 -0500 Document import of ES2015+ modules commit eaaf7c41608fb988b8f4bbaa933cff110115610e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:29 2018 -0500 :art: Fix lint error commit a25b0e2e3d0f72c6a7bf0a15683f02450d5209ee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:13:57 2018 -0500 :art: Organize `require`s commit e0cc3d8fab6529d01b388acddf8605908c3d236b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:07:17 2018 -0500 Implement attachment process version Instead of keeping track of last normalization (processing) date, we now keep track of an internal processing version that will help us understand what kind of processing has already been completed for a given attachment. This will let us retroactively upgrade existing attachments. As we add more processing steps, we can build a processing pipeline that can convert any attachment processing version into a higher one, e.g. 4 -> 5 -> 6 -> 7. commit ad9083d0fdb880bc518e02251e51a39f7e1c585f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:50:31 2018 -0500 Ignore ES2015+ files during JSCS linting commit 96641205f734927aaebc2342d977c555799c3e3b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:48:07 2018 -0500 Improve ESLint ignore rules Apparently, using unqualified `/**` patterns prevents `!` include patterns. Using qualified glob patterns, e.g. `js/models/**/*.js`, lets us work around this. commit 255e0ab15bd1a0ca8ca5746e42d23977c8765d01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:44:59 2018 -0500 :abc: ESLint ignored files commit ebcb70258a26f234bd602072ac7c0a1913128132 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:47 2018 -0500 Whitelist `browser` environment for ESLint commit 3eaace6f3a21421c5aaaaf01592408c7ed83ecd3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:05 2018 -0500 Use `MIME` module commit ba2cf7770e614415733414a2dcc48f110b929892 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:32:54 2018 -0500 :art: Fix lint errors commit 65acc86e8580e88f7a6611eb4b8fa5d7291f7a3f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:30:42 2018 -0500 Add ES2015+ files to JSHint ignored list commit 8b6494ae6c9247acdfa059a9b361ec5ffcdb39f0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:29:20 2018 -0500 Document potentially unexpected `autoScale` behavior commit 8b4c69b2002d1777d3621be10f92cbf432f9d4d6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:26:47 2018 -0500 Test CommonJS modules separately Not sure how to test them as part of Grunt `unit-tests` task as `test/index.html` doesn’t allow for inclusion of CommonJS modules that use `require`. The tests are silently skipped. commit 213400e4b2bba3efee856a25b40e269221c3c39d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:24:27 2018 -0500 Add `MIME` type module commit 37a726e4fb4b3ed65914463122a5662847b5adee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:18:05 2018 -0500 Return proper `Error` from `blobArrayToBuffer` commit 164752db5612220e4dcf58d57bcd682cb489a399 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:15:41 2018 -0500 :art: Fix ESLint errors commit d498dd79a067c75098dd3179814c914780e5cb4f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:14:33 2018 -0500 Update `Attachment` type field definitions commit 141155a1533ff8fb616b70ea313432781bbebffd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:12:50 2018 -0500 Move `blueimp-canvas-to-blob` from Bower to npm commit 7ccb833e5d286ddd6235d3e491c62ac1e4544510 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:33:50 2018 -0500 :art: Clarify data flow commit e7da41591fde5a830467bebf1b6f51c1f7293e74 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:31:21 2018 -0500 Use `blobUrl` for consistency commit 523a80eefe0e2858aa1fb2bb9539ec44da502963 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:28:06 2018 -0500 Remove just-in-time image auto-orient for lightbox We can bring this back if our users would like auto-orient for old attachments. commit 0739feae9c47dd523c10740d6cdf746d539f270c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:27:21 2018 -0500 Remove just-in-time auto-orient of message attachments We can bring this back if our users would like auto-orient for old attachments. But better yet, we might implement this as database migration. commit ed43c66f92830ee233d5a94d0545eea4da43894d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:26:24 2018 -0500 Auto-orient JPEG attachments upon receipt commit e2eb8e36b017b048d57602fca14e45d657e0e1a1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:25:26 2018 -0500 Expose `Attachment` type through `Whisper.Attachment` commit 9638fbc987b84f143ca34211dc4666d96248ea2f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:39 2018 -0500 Use `contentType` from `model` commit 032c0ced46c3876cb9474b26f9d53d6f1c6b16a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:04 2018 -0500 Return `Error` object for `autoOrientImage` failures commit ff04bad8510c4b21aef350bed2b1887d0e055b98 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:22:32 2018 -0500 Add `options` for `autoOrientImage` output type / quality commit 87745b5586d1e182b51c9f9bc5e4eaf6dbc16722 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:18:46 2018 -0500 Add `Attachment` type Defines various functions on attachments, e.g. normalization (auto-orient JPEGs, etc.) commit de27fdc10a53bc8882a9c978e82265db9ac6d6f5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:16:34 2018 -0500 Add `yarn grunt` shortcut This allows us to use local `grunt-cli` for `grunt dev`. commit 59974db5a5da0d8f4cdc8ce5c4e3c974ecd5e754 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:10:11 2018 -0500 Improve readability commit b5ba96f1e6f40f2e1fa77490c583217768e1f412 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:08:12 2018 -0500 Use `snake_case` for module names Prevents problems across case-sensitive and case-insensitive file systems. We can work around this in the future using a lint rule such as `eslint-plugin-require-path-exists`. See discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r167365931 commit 48c5d3155c96ef628b00d99b52975e580d1d5501 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:05:44 2018 -0500 :art: Use destructuring commit 4822f49f22382a99ebf142b337375f7c25251d76 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:41:40 2018 -0500 Auto-orient images in lightbox view commit 7317110809677dddbbef3fadbf912cdba1c010bf Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:40:14 2018 -0500 Document magic number for escape key commit c790d07389a7d0bbf5298de83dbcfa8be1e7696b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:38:35 2018 -0500 Make second `View` argument an `options` object commit fbe010bb63d0088af9dfe11f153437fab34247e0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:27:40 2018 -0500 Allow `loadImage` to fetch `blob://` URLs commit ec35710d002b019a273eeb48f94dcaf2babe5d96 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:48 2018 -0500 :art: Shorten `autoOrientImage` import commit d07433e3cf316c6a143a0c9393ba26df9e3af17b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:19 2018 -0500 Make `autoOrientImage` module standalone commit c285bf5e33cdf10e0ef71e72cd6f55aef0df96ef Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:55:44 2018 -0500 Replace `loadImage` with `autoOrientImage` commit 44318549235af01fd061c25f557c93fd21cebb7a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:53:23 2018 -0500 Add `autoOrientImage` module This module exposes `loadImage` with a `Promise` based interface and pre- populates `orientation: true` option to auto-orient input. Returns data URL as string. The module uses a named export as refactoring references of modules with `default` (`module.exports`) export references can be error-prone. See: https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html commit c77063afc6366fe49615052796fe46f9b369de39 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:44:30 2018 -0500 Auto-orient preview images See: #998 commit 06dba5eb8f662c11af3a9ba8395bb453ab2e5f8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:43:23 2018 -0500 TODO: Use native `Canvas::toBlob` One challenge is that `Canvas::toBlob` is async whereas `dataURLtoBlob` is sync. commit b15c304a3125dd023fd90990e6225a7303f3596f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:42:45 2018 -0500 Make `null` check strict Appeases JSHint. ESLint has a nice `smart` option for `eqeqeq` rule: https://eslint.org/docs/rules/eqeqeq#smart commit ea70b92d9b18201758e11fdc25b09afc97b50055 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 15:23:58 2018 -0500 Use `Canvas::toDataURL` to preserve `ImageView` logic This way, all the other code paths remain untouched in case we want to remove the auto-orient code once Chrome supports the `image-orientation` CSS property. See: - #998 - https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation commit 62fd744f9f27d951573a68d2cdfe7ba2a3784b41 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:38:04 2018 -0500 Use CSS to constrain auto-oriented images commit f4d3392687168c237441b29140c7968b49dbef9e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:35:02 2018 -0500 Replace `ImageView` `el` with auto-oriented `canvas` See: #998 commit 1602d7f610e4993ad1291f88197f9ead1e25e776 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:48 2018 -0500 Pass `Blob` to `View` (for `ImageView`) This allows us to do JPEG auto-orientation based on EXIF metadata. commit e6a414f2b2a80da1137b839b348a38510efd04bb Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:12 2018 -0500 :hocho: Remove newline commit 5f0d9570d7862fc428ff89c2ecfd332a744537e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:17:02 2018 -0500 Expose `blueimp-load-image` as `window.loadImage` commit 1e1c62fe2f6a76dbcf1998dd468c26187c9871dc Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:16:46 2018 -0500 Add `blueimp-load-image` npm dependency commit ad17fa8a68a21ca5ddec336801b8568009bef3d4 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:14:40 2018 -0500 Remove `blueimp-load-image` Bower dependency
2018-02-21 15:26:59 +00:00
});
2020-05-27 21:37:06 +00:00
this.incrementSentMessageCount();
window.Signal.Data.updateConversation(this.attributes);
Profiles (#1453) * Add AES-GCM encryption for profiles With tests. * Add profileKey to DataMessage protobuf // FREEBIE * Decrypt and save profile names // FREEBIE * Save incoming profile keys * Move pad/unpad to crypto module // FREEBIE * Support fetching avatars from the cdn // FREEBIE * Translate failed authentication errors When AES-GCM authentication fails, webcrypto returns a very generic error. The same error is thrown for invalid length inputs, but our earlier checks in decryptProfile should rule out those failure modes and leave us safe to assume that we either had bad ciphertext or the wrong key. // FREEBIE * Handle profile avatars (wip) and log decrypt errors // FREEBIE * Display profile avatars Synced contact avatars will still override profile avatars. * Display profile names in convo list Only if we don't have a synced contact name. // FREEBIE * Make cdn url an environment config Use different ones for staging and production // FREEBIE * Display profile name in conversation header * Display profile name in group messages * Update conversation header if profile avatar changes // FREEBIE * Style profile names small with ~ * Save profileKeys from contact sync messages // FREEBIE * Save profile keys from provisioning messages For standalone accounts, generate a random profile key. // FREEBIE * Special case for one-time sync of our profile key Android will use a contact sync message to sync a profile key from Android clients who have just upgraded and generated their profile key. Normally we should receive this data in a provisioning message. // FREEBIE * Infer profile sharing from synced data messages * Populate profile keys on outgoing messages Requires that `profileSharing` be set on the conversation. // FREEBIE * Support for the profile key update flag When receiving a message with this flag, don't init a message record, just process the profile key and move on. // FREEBIE * Display profile names in group member list * Refresh contact's profile on profile key changes // FREEBIE * Catch errors on profile save // FREEBIE * Save our own synced contact info Don't return early if we get a contact sync for our own number // FREEBIE
2017-09-11 16:50:35 +00:00
// We're offline!
if (!textsecure.messaging) {
const errors = (this.contactCollection.length
? this.contactCollection
: [this]
).map(contact => {
const error = new Error('Network is not available');
error.name = 'SendMessageNetworkError';
error.identifier = contact.get('id');
return error;
});
await message.saveErrors(errors);
2018-09-21 01:47:19 +00:00
return null;
}
2019-01-31 01:45:58 +00:00
const attachmentsWithData = await Promise.all(
messageWithSchema.attachments.map(loadAttachmentData)
);
const {
body: messageBody,
attachments: finalAttachments,
} = Whisper.Message.getLongMessageAttachment({
body,
attachments: attachmentsWithData,
now,
});
2019-01-31 01:45:58 +00:00
// Special-case the self-send case - we send only a sync message
if (this.isMe()) {
const dataMessage = await textsecure.messaging.getMessageProto(
destination,
messageBody,
finalAttachments,
2019-01-31 01:45:58 +00:00
quote,
preview,
sticker,
2020-09-09 02:25:05 +00:00
null, // reaction
2019-01-31 01:45:58 +00:00
now,
expireTimer,
profileKey
);
return message.sendSyncMessageOnly(dataMessage);
}
Auto-orient image attachments based on EXIF metadata As described in #998, images are sometimes displayed with an incorrect orientation. This is because cameras often write files in the native sensor byte order and attach the `Orientation` EXIF metadata to tell end-user devices how to display the images based on the original author’s capture orientation. Electron/Chromium (and therefore Signal Desktop) currently doesn’t support applying this metadata for `<img>` tags, e.g. CSS `image-orientation: from- image`. As a workaround, this change uses the `loadImage` library with the `orientation: true` flag to auto-orient images ~~before display~~ upon receipt and before sending. **Changes** - [x] ~~Auto-orient images during display in message list view~~ - [x] Ensure image is not displayed until loaded (to prevent layout reflow) . - [x] Auto-orient images upon receipt and before storing in IndexedDB (~~or preserve original data until Chromium offers native fix?~~) - [x] Auto-orient images in compose area preview. - [x] ~~Auto-orient images in lightbox view~~ - [x] Auto-orient images before sending / storage. - [x] Add EditorConfig for sharing code styles across editors. - [x] Fix ESLint ignore file. - [x] Change `function-paren-newline` ESLint rule from `consistent` to `multiline`. - [x] Add `operator-linebreak` ESLint rule for consistency. - [x] Added `blob-util` dependency for converting between array buffers, blobs, etc. - [x] Extracted `createMessageHandler` to consolidate logic for `onMessageReceived` and `onSentMessage`. - [x] Introduce `async` / `await` to simplify async coding (restore control flow for branching, loops, and exceptions). - [x] Introduce `window.Signal` namespace for exposing ES2015+ CommonJS modules. - [x] Introduce rudimentary `Message` and `Attachment` types to begin defining a schema and versioning. This will allow us to track which changes, e.g. auto-orient JPEGs, per message / attachment as well as which fields are stored. - [x] Renamed `window.dataURLtoBlob` to `window.dataURLToBlobSync` to both fix the strange `camelCase` as well as to highlight that this operation is synchronous and therefore blocks the user thread. - [x] Normalize all JPEG MIME types to `image/jpeg`, eliminating the invalid `image/jpg`. - [x] Add `npm run test-modules` command for testing non-browser specific CommonJS modules. - **Stretch Goals** - [ ] ~~Restrict `autoOrientImage` to `Blob` to narrow API interface.~~ Do this once we use PureScript. - [ ] ~~Return non-JPEGs as no-op from `autoOrientImage`.~~ Skipping `autoOrientImage` for non-JPEGs altogether. - [ ] Retroactively auto-orient existing JPEG image attachments in the background. --- Fixes #998 --- - **Blog:** EXIF Orientation Handling Is a Ghetto: https://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ - **Chromium Bug:** EXIF orientation is ignored: https://bugs.chromium.org/p/chromium/issues/detail?id=56845 - **Chromium Bug:** Support for the CSS image-orientation CSS property: https://bugs.chromium.org/p/chromium/issues/detail?id=158753 --- commit ce5090b473a2448229dc38e4c3f15d7ad0137714 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 16 10:35:36 2018 -0500 Inline message descriptors commit 329036e59c138c1e950ec7c654eebd7d87076de5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:34:40 2018 -0500 Clarify order of operations Semantically, it makes more sense to do `getFile` before `clearForm` even though it seems to work either way. commit f9d4cfb2ba0d8aa308b0923bbe6066ea34cb97bd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:18:26 2018 -0500 Simplify `operator-linebreak` configuration Enabling `before` caused more code changes and it turns out our previous configuration is already the default. commit db588997acdd90ed2ad829174ecbba744383c78b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:15:59 2018 -0500 Remove obsolete TODO commit 799c8817633f6afa0b731fc3b5434e463bd850e3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:12:18 2018 -0500 Enable ESLint `function-paren-newline` `multiline` Per discussion. commit b660b6bc8ef41df7601a411213d6cda80821df87 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:10:48 2018 -0500 Use `messageDescriptor.id` not `source` commit 5e7309d176f4a7e97d3dc4c738e6b0ccd4792871 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:29:01 2018 -0500 Remove unnecessary `eslint-env` commit 393b3da55eabd7413596c86cc3971b063a0efe31 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:19:17 2018 -0500 Refactor `onSentMessage` and `onMessageReceived` Since they are so similar, we create the handlers using new `createMessageHandler` function. This allows us to ensure both synced and received messages go through schema upgrade pipeline. commit b3db0bf179c9a5bea96480cde28c6fa7193ac117 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:18:21 2018 -0500 Add `Message` descriptor functions commit 8febf125b1b42fe4ae1888dd50fcee2749dc1ff0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 14:46:56 2018 -0500 Fix typo commit 98d951ef77bd578b313a4ff4b496b793e82e88d5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:39 2018 -0500 Remove `promises` reference commit a0e9559ed5bed947dabf28cb672e63d39948d854 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:13 2018 -0500 Fix `AttachmentView::mediaType` fall-through commit 67be916a83951b8a1f9b22efe78a6da6b1825f38 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:03:41 2018 -0500 Remove minor TODOs commit 0af186e118256b62905de38487ffacc41693ff47 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:41 2018 -0500 Enable ESLint for `js/views/attachment_view.js` commit 28a2dc5b8a28e1a087924fdc7275bf7d9a577b92 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:12 2018 -0500 Remove dynamic type checks commit f4ce36fcfc2737de32d911cd6103f889097813f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:27:56 2018 -0500 Rename `process` to `upgradeSchema` - `Message.process` -> `Message.upgradeSchema` - `Attachment.process` -> `Attachment.upgradeSchema` - `Attachment::processVersion` -> `Attachment::schemaVersion` Document version history. commit 41b92c0a31050ba05ddb1c43171d651f3568b9ac Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:11:50 2018 -0500 Add `operator-linebreak` ESLint rule Based on the following discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r168029106 commit 462defbe55879060fe25bc69103d4429bae2b2f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:01:30 2018 -0500 Add missing `await` for `ConversationController.getOrCreateAndWait` Tested this by setting `if` condition to `true` and confirming it works. It turns rotating a profile key is more involved and might require registering a new account according to Matthew. commit c08058ee4b883b3e23a40683de802ac81ed74874 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 16:32:24 2018 -0500 Convert `FileList` to `Array` commit 70a6c4201925f57be1f94d9da3547fdefc7bbb53 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:46:34 2018 -0500 :art: Fix lint errors commit 2ca7cdbc31d4120d6c6a838a6dcf43bc209d9788 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:07:09 2018 -0500 Skip `autoOrientImage` for non-JPEG images commit 58eac383013c16ca363a4ed33dca5c7ba61284e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:55:35 2018 -0500 Move new-style modules to `window.Signal` namespace commit 02c9328877dce289d6116a18b1c223891bd3cd0b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:35:23 2018 -0500 Extract `npm run test-modules` command commit 2c708eb94fba468b81ea9427734896114f5a7807 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:51 2018 -0500 Extract `Message.process` commit 4a2e52f68a77536a0fa04aa3c29ad3e541a8fa7e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:12 2018 -0500 Fix EditorConfig commit a346bab5db082720f5d47363f06301380e870425 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:13:02 2018 -0500 Remove `vim` directives on ESLint-ed files commit 7ec885c6359e495b407d5bc3eac9431d47c37fc6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:08:24 2018 -0500 Remove CSP whitelisting of `blob:` We no longer use `autoOrientImage` using blob URLs. Bring this back if we decide to auto-orient legacy attachments. commit 879b6f58f4a3f4a9ed6915af6b1be46c1e90e0ca Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:57:05 2018 -0500 Use `Message` type to determine send function Throws on invalid message type. commit 5203d945c98fd2562ae4e22c5c9838d27dec305b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:48 2018 -0500 Whitelist `Whisper` global commit 8ad0b066a3690d3382b86bf6ac00c03df7d1e20b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:32 2018 -0500 Add `Whisper.Types` namespace This avoids namespace collision for `Whisper.Message`. commit 785a949fce2656ca7dcaf0869d6b9e0648114e80 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:55:43 2018 -0500 Add `Message` type commit 674a7357abf0dcc365455695d56c0479998ebf27 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:35:23 2018 -0500 Run ESLint on `Conversation::sendMessage` commit cd985aa700caa80946245b17ea1b856449f152a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:34:38 2018 -0500 Document type signature of `FileInputView::readFile` commit d70d70e52c49588a1dc9833dfe5dd7128e13607f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:31:16 2018 -0500 Move attachment processing closer to sending This helps ensure processing happens uniformly, regardless of which code paths are taken to send an attachment. commit 532ac3e273a26b97f831247f9ee3412621b5c112 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:22:29 2018 -0500 Process attachment before it’s sent Picked this place since it already had various async steps, similar to `onMessageReceived` for the incoming `Attachment.process`. Could we try have this live closer to where we store it in IndexedDB, e.g. `Conversation::sendMessage`? commit a4582ae2fb6e1d3487131ba1f8fa6a00170cb32c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:21:42 2018 -0500 Refactor `getFile` and `getFiles` Lint them using ESLint. commit 07e9114e65046d791fc4f6ed90d6e2e938ad559d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:37:31 2018 -0500 Document incoming and outgoing attachments fields Note how outgoing message attachments only have 4 fields. This presumably means the others are not used in our code and could be discarded for simplicity. commit fdc3ef289d6ec1be344a12d496839d5ba747bb6a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:36:21 2018 -0500 Highlight that `dataURLToBlob` is synchronous commit b9c6bf600fcecedfd649ef2ae3c8629cced4e45a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:35:49 2018 -0500 Add EditorConfig configuration commit e56101e229d56810c8e31ad7289043a152c6c449 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:34:23 2018 -0500 Replace custom with `blob-util` functions IMPORTANT: All of them are async so we need to use `await`, otherwise we get strange or silent errors. commit f95150f6a9569fabcb31f3acd9f6b7bf50b5d145 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:17:30 2018 -0500 Revert "Replace custom functions with `blob-util`" This reverts commit 8a81e9c01bfe80c0e1bf76737092206c06949512. commit 33860d93f3d30ec55c32f3f4a58729df2eb43f0d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:13:02 2018 -0500 Revert "Replace `blueimp-canvas-to-blob` with `blob-util`" This reverts commit 31b3e853e4afc78fe80995921aa4152d9f6e4783. commit 7a0ba6fed622d76a3c39c7f03de541a7edb5b8dd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:12:58 2018 -0500 Replace `blueimp-canvas-to-blob` with `blob-util` commit 47a5f2bfd8b3f546e27e8d2b7e1969755d825a66 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:55:34 2018 -0500 Replace custom functions with `blob-util` commit 1cfa0efdb4fb1265369e2bf243c21f04f044fa01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:47:02 2018 -0500 Add `blob-util` dependency commit 9ac26be1bd783cd5070d886de107dd3ad9c91ad1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:44 2018 -0500 Document why we drop original image data during auto-orient commit 4136d6c382b99f41760a4da519d0db537fa7de8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:27 2018 -0500 Extract `DEFAULT_JPEG_QUALITY` commit 4a7156327eb5f94dba80cb300b344ac591226b0e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:37:11 2018 -0500 Drop support for invalid `image/jpg` MIME type commit 69fe96581f25413194032232f1bf704312e4754c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:30 2018 -0500 Document `window.onInvalidStateError` global commit a48ba1c77458da38583ee9cd488f70a59f6ee0fd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:04 2018 -0500 Selectively run ESLint on `js/background.js` Enabling ESLint on a per function basis allows us to incrementally improve the codebase without requiring large and potentially risky refactorings. commit e6d1cf826befc17ad4ec72fda8e761701665635e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:16:23 2018 -0500 Move async attachment processing to `onMessageReceived` We previously processed attachments in `handleDataMessage` which is mostly a synchronous function, except for the saving of the model. Moving the processing into the already async `onMessageReceived` improves code clarity. commit be6ca2a9aae5b59c360817deb1e18d39d705755e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:49 2018 -0500 Document import of ES2015+ modules commit eaaf7c41608fb988b8f4bbaa933cff110115610e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:29 2018 -0500 :art: Fix lint error commit a25b0e2e3d0f72c6a7bf0a15683f02450d5209ee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:13:57 2018 -0500 :art: Organize `require`s commit e0cc3d8fab6529d01b388acddf8605908c3d236b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:07:17 2018 -0500 Implement attachment process version Instead of keeping track of last normalization (processing) date, we now keep track of an internal processing version that will help us understand what kind of processing has already been completed for a given attachment. This will let us retroactively upgrade existing attachments. As we add more processing steps, we can build a processing pipeline that can convert any attachment processing version into a higher one, e.g. 4 -> 5 -> 6 -> 7. commit ad9083d0fdb880bc518e02251e51a39f7e1c585f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:50:31 2018 -0500 Ignore ES2015+ files during JSCS linting commit 96641205f734927aaebc2342d977c555799c3e3b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:48:07 2018 -0500 Improve ESLint ignore rules Apparently, using unqualified `/**` patterns prevents `!` include patterns. Using qualified glob patterns, e.g. `js/models/**/*.js`, lets us work around this. commit 255e0ab15bd1a0ca8ca5746e42d23977c8765d01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:44:59 2018 -0500 :abc: ESLint ignored files commit ebcb70258a26f234bd602072ac7c0a1913128132 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:47 2018 -0500 Whitelist `browser` environment for ESLint commit 3eaace6f3a21421c5aaaaf01592408c7ed83ecd3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:05 2018 -0500 Use `MIME` module commit ba2cf7770e614415733414a2dcc48f110b929892 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:32:54 2018 -0500 :art: Fix lint errors commit 65acc86e8580e88f7a6611eb4b8fa5d7291f7a3f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:30:42 2018 -0500 Add ES2015+ files to JSHint ignored list commit 8b6494ae6c9247acdfa059a9b361ec5ffcdb39f0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:29:20 2018 -0500 Document potentially unexpected `autoScale` behavior commit 8b4c69b2002d1777d3621be10f92cbf432f9d4d6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:26:47 2018 -0500 Test CommonJS modules separately Not sure how to test them as part of Grunt `unit-tests` task as `test/index.html` doesn’t allow for inclusion of CommonJS modules that use `require`. The tests are silently skipped. commit 213400e4b2bba3efee856a25b40e269221c3c39d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:24:27 2018 -0500 Add `MIME` type module commit 37a726e4fb4b3ed65914463122a5662847b5adee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:18:05 2018 -0500 Return proper `Error` from `blobArrayToBuffer` commit 164752db5612220e4dcf58d57bcd682cb489a399 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:15:41 2018 -0500 :art: Fix ESLint errors commit d498dd79a067c75098dd3179814c914780e5cb4f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:14:33 2018 -0500 Update `Attachment` type field definitions commit 141155a1533ff8fb616b70ea313432781bbebffd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:12:50 2018 -0500 Move `blueimp-canvas-to-blob` from Bower to npm commit 7ccb833e5d286ddd6235d3e491c62ac1e4544510 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:33:50 2018 -0500 :art: Clarify data flow commit e7da41591fde5a830467bebf1b6f51c1f7293e74 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:31:21 2018 -0500 Use `blobUrl` for consistency commit 523a80eefe0e2858aa1fb2bb9539ec44da502963 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:28:06 2018 -0500 Remove just-in-time image auto-orient for lightbox We can bring this back if our users would like auto-orient for old attachments. commit 0739feae9c47dd523c10740d6cdf746d539f270c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:27:21 2018 -0500 Remove just-in-time auto-orient of message attachments We can bring this back if our users would like auto-orient for old attachments. But better yet, we might implement this as database migration. commit ed43c66f92830ee233d5a94d0545eea4da43894d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:26:24 2018 -0500 Auto-orient JPEG attachments upon receipt commit e2eb8e36b017b048d57602fca14e45d657e0e1a1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:25:26 2018 -0500 Expose `Attachment` type through `Whisper.Attachment` commit 9638fbc987b84f143ca34211dc4666d96248ea2f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:39 2018 -0500 Use `contentType` from `model` commit 032c0ced46c3876cb9474b26f9d53d6f1c6b16a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:04 2018 -0500 Return `Error` object for `autoOrientImage` failures commit ff04bad8510c4b21aef350bed2b1887d0e055b98 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:22:32 2018 -0500 Add `options` for `autoOrientImage` output type / quality commit 87745b5586d1e182b51c9f9bc5e4eaf6dbc16722 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:18:46 2018 -0500 Add `Attachment` type Defines various functions on attachments, e.g. normalization (auto-orient JPEGs, etc.) commit de27fdc10a53bc8882a9c978e82265db9ac6d6f5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:16:34 2018 -0500 Add `yarn grunt` shortcut This allows us to use local `grunt-cli` for `grunt dev`. commit 59974db5a5da0d8f4cdc8ce5c4e3c974ecd5e754 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:10:11 2018 -0500 Improve readability commit b5ba96f1e6f40f2e1fa77490c583217768e1f412 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:08:12 2018 -0500 Use `snake_case` for module names Prevents problems across case-sensitive and case-insensitive file systems. We can work around this in the future using a lint rule such as `eslint-plugin-require-path-exists`. See discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r167365931 commit 48c5d3155c96ef628b00d99b52975e580d1d5501 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:05:44 2018 -0500 :art: Use destructuring commit 4822f49f22382a99ebf142b337375f7c25251d76 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:41:40 2018 -0500 Auto-orient images in lightbox view commit 7317110809677dddbbef3fadbf912cdba1c010bf Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:40:14 2018 -0500 Document magic number for escape key commit c790d07389a7d0bbf5298de83dbcfa8be1e7696b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:38:35 2018 -0500 Make second `View` argument an `options` object commit fbe010bb63d0088af9dfe11f153437fab34247e0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:27:40 2018 -0500 Allow `loadImage` to fetch `blob://` URLs commit ec35710d002b019a273eeb48f94dcaf2babe5d96 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:48 2018 -0500 :art: Shorten `autoOrientImage` import commit d07433e3cf316c6a143a0c9393ba26df9e3af17b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:19 2018 -0500 Make `autoOrientImage` module standalone commit c285bf5e33cdf10e0ef71e72cd6f55aef0df96ef Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:55:44 2018 -0500 Replace `loadImage` with `autoOrientImage` commit 44318549235af01fd061c25f557c93fd21cebb7a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:53:23 2018 -0500 Add `autoOrientImage` module This module exposes `loadImage` with a `Promise` based interface and pre- populates `orientation: true` option to auto-orient input. Returns data URL as string. The module uses a named export as refactoring references of modules with `default` (`module.exports`) export references can be error-prone. See: https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html commit c77063afc6366fe49615052796fe46f9b369de39 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:44:30 2018 -0500 Auto-orient preview images See: #998 commit 06dba5eb8f662c11af3a9ba8395bb453ab2e5f8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:43:23 2018 -0500 TODO: Use native `Canvas::toBlob` One challenge is that `Canvas::toBlob` is async whereas `dataURLtoBlob` is sync. commit b15c304a3125dd023fd90990e6225a7303f3596f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:42:45 2018 -0500 Make `null` check strict Appeases JSHint. ESLint has a nice `smart` option for `eqeqeq` rule: https://eslint.org/docs/rules/eqeqeq#smart commit ea70b92d9b18201758e11fdc25b09afc97b50055 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 15:23:58 2018 -0500 Use `Canvas::toDataURL` to preserve `ImageView` logic This way, all the other code paths remain untouched in case we want to remove the auto-orient code once Chrome supports the `image-orientation` CSS property. See: - #998 - https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation commit 62fd744f9f27d951573a68d2cdfe7ba2a3784b41 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:38:04 2018 -0500 Use CSS to constrain auto-oriented images commit f4d3392687168c237441b29140c7968b49dbef9e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:35:02 2018 -0500 Replace `ImageView` `el` with auto-oriented `canvas` See: #998 commit 1602d7f610e4993ad1291f88197f9ead1e25e776 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:48 2018 -0500 Pass `Blob` to `View` (for `ImageView`) This allows us to do JPEG auto-orientation based on EXIF metadata. commit e6a414f2b2a80da1137b839b348a38510efd04bb Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:12 2018 -0500 :hocho: Remove newline commit 5f0d9570d7862fc428ff89c2ecfd332a744537e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:17:02 2018 -0500 Expose `blueimp-load-image` as `window.loadImage` commit 1e1c62fe2f6a76dbcf1998dd468c26187c9871dc Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:16:46 2018 -0500 Add `blueimp-load-image` npm dependency commit ad17fa8a68a21ca5ddec336801b8568009bef3d4 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:14:40 2018 -0500 Remove `blueimp-load-image` Bower dependency
2018-02-21 15:26:59 +00:00
const conversationType = this.get('type');
const options = this.getSendOptions();
2020-09-09 02:25:05 +00:00
let promise;
if (conversationType === Message.GROUP) {
promise = textsecure.messaging.sendMessageToGroup(
{
attachments: finalAttachments,
expireTimer,
groupV1: this.getGroupV1Info(),
groupV2: this.getGroupV2Info(),
messageText: messageBody,
preview,
profileKey,
quote,
sticker,
timestamp: now,
},
options
);
} else {
promise = textsecure.messaging.sendMessageToIdentifier(
destination,
messageBody,
finalAttachments,
quote,
preview,
sticker,
null, // reaction
now,
expireTimer,
profileKey,
options
);
}
Auto-orient image attachments based on EXIF metadata As described in #998, images are sometimes displayed with an incorrect orientation. This is because cameras often write files in the native sensor byte order and attach the `Orientation` EXIF metadata to tell end-user devices how to display the images based on the original author’s capture orientation. Electron/Chromium (and therefore Signal Desktop) currently doesn’t support applying this metadata for `<img>` tags, e.g. CSS `image-orientation: from- image`. As a workaround, this change uses the `loadImage` library with the `orientation: true` flag to auto-orient images ~~before display~~ upon receipt and before sending. **Changes** - [x] ~~Auto-orient images during display in message list view~~ - [x] Ensure image is not displayed until loaded (to prevent layout reflow) . - [x] Auto-orient images upon receipt and before storing in IndexedDB (~~or preserve original data until Chromium offers native fix?~~) - [x] Auto-orient images in compose area preview. - [x] ~~Auto-orient images in lightbox view~~ - [x] Auto-orient images before sending / storage. - [x] Add EditorConfig for sharing code styles across editors. - [x] Fix ESLint ignore file. - [x] Change `function-paren-newline` ESLint rule from `consistent` to `multiline`. - [x] Add `operator-linebreak` ESLint rule for consistency. - [x] Added `blob-util` dependency for converting between array buffers, blobs, etc. - [x] Extracted `createMessageHandler` to consolidate logic for `onMessageReceived` and `onSentMessage`. - [x] Introduce `async` / `await` to simplify async coding (restore control flow for branching, loops, and exceptions). - [x] Introduce `window.Signal` namespace for exposing ES2015+ CommonJS modules. - [x] Introduce rudimentary `Message` and `Attachment` types to begin defining a schema and versioning. This will allow us to track which changes, e.g. auto-orient JPEGs, per message / attachment as well as which fields are stored. - [x] Renamed `window.dataURLtoBlob` to `window.dataURLToBlobSync` to both fix the strange `camelCase` as well as to highlight that this operation is synchronous and therefore blocks the user thread. - [x] Normalize all JPEG MIME types to `image/jpeg`, eliminating the invalid `image/jpg`. - [x] Add `npm run test-modules` command for testing non-browser specific CommonJS modules. - **Stretch Goals** - [ ] ~~Restrict `autoOrientImage` to `Blob` to narrow API interface.~~ Do this once we use PureScript. - [ ] ~~Return non-JPEGs as no-op from `autoOrientImage`.~~ Skipping `autoOrientImage` for non-JPEGs altogether. - [ ] Retroactively auto-orient existing JPEG image attachments in the background. --- Fixes #998 --- - **Blog:** EXIF Orientation Handling Is a Ghetto: https://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ - **Chromium Bug:** EXIF orientation is ignored: https://bugs.chromium.org/p/chromium/issues/detail?id=56845 - **Chromium Bug:** Support for the CSS image-orientation CSS property: https://bugs.chromium.org/p/chromium/issues/detail?id=158753 --- commit ce5090b473a2448229dc38e4c3f15d7ad0137714 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 16 10:35:36 2018 -0500 Inline message descriptors commit 329036e59c138c1e950ec7c654eebd7d87076de5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:34:40 2018 -0500 Clarify order of operations Semantically, it makes more sense to do `getFile` before `clearForm` even though it seems to work either way. commit f9d4cfb2ba0d8aa308b0923bbe6066ea34cb97bd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:18:26 2018 -0500 Simplify `operator-linebreak` configuration Enabling `before` caused more code changes and it turns out our previous configuration is already the default. commit db588997acdd90ed2ad829174ecbba744383c78b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:15:59 2018 -0500 Remove obsolete TODO commit 799c8817633f6afa0b731fc3b5434e463bd850e3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:12:18 2018 -0500 Enable ESLint `function-paren-newline` `multiline` Per discussion. commit b660b6bc8ef41df7601a411213d6cda80821df87 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:10:48 2018 -0500 Use `messageDescriptor.id` not `source` commit 5e7309d176f4a7e97d3dc4c738e6b0ccd4792871 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:29:01 2018 -0500 Remove unnecessary `eslint-env` commit 393b3da55eabd7413596c86cc3971b063a0efe31 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:19:17 2018 -0500 Refactor `onSentMessage` and `onMessageReceived` Since they are so similar, we create the handlers using new `createMessageHandler` function. This allows us to ensure both synced and received messages go through schema upgrade pipeline. commit b3db0bf179c9a5bea96480cde28c6fa7193ac117 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:18:21 2018 -0500 Add `Message` descriptor functions commit 8febf125b1b42fe4ae1888dd50fcee2749dc1ff0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 14:46:56 2018 -0500 Fix typo commit 98d951ef77bd578b313a4ff4b496b793e82e88d5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:39 2018 -0500 Remove `promises` reference commit a0e9559ed5bed947dabf28cb672e63d39948d854 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:13 2018 -0500 Fix `AttachmentView::mediaType` fall-through commit 67be916a83951b8a1f9b22efe78a6da6b1825f38 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:03:41 2018 -0500 Remove minor TODOs commit 0af186e118256b62905de38487ffacc41693ff47 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:41 2018 -0500 Enable ESLint for `js/views/attachment_view.js` commit 28a2dc5b8a28e1a087924fdc7275bf7d9a577b92 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:12 2018 -0500 Remove dynamic type checks commit f4ce36fcfc2737de32d911cd6103f889097813f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:27:56 2018 -0500 Rename `process` to `upgradeSchema` - `Message.process` -> `Message.upgradeSchema` - `Attachment.process` -> `Attachment.upgradeSchema` - `Attachment::processVersion` -> `Attachment::schemaVersion` Document version history. commit 41b92c0a31050ba05ddb1c43171d651f3568b9ac Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:11:50 2018 -0500 Add `operator-linebreak` ESLint rule Based on the following discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r168029106 commit 462defbe55879060fe25bc69103d4429bae2b2f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:01:30 2018 -0500 Add missing `await` for `ConversationController.getOrCreateAndWait` Tested this by setting `if` condition to `true` and confirming it works. It turns rotating a profile key is more involved and might require registering a new account according to Matthew. commit c08058ee4b883b3e23a40683de802ac81ed74874 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 16:32:24 2018 -0500 Convert `FileList` to `Array` commit 70a6c4201925f57be1f94d9da3547fdefc7bbb53 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:46:34 2018 -0500 :art: Fix lint errors commit 2ca7cdbc31d4120d6c6a838a6dcf43bc209d9788 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:07:09 2018 -0500 Skip `autoOrientImage` for non-JPEG images commit 58eac383013c16ca363a4ed33dca5c7ba61284e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:55:35 2018 -0500 Move new-style modules to `window.Signal` namespace commit 02c9328877dce289d6116a18b1c223891bd3cd0b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:35:23 2018 -0500 Extract `npm run test-modules` command commit 2c708eb94fba468b81ea9427734896114f5a7807 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:51 2018 -0500 Extract `Message.process` commit 4a2e52f68a77536a0fa04aa3c29ad3e541a8fa7e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:12 2018 -0500 Fix EditorConfig commit a346bab5db082720f5d47363f06301380e870425 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:13:02 2018 -0500 Remove `vim` directives on ESLint-ed files commit 7ec885c6359e495b407d5bc3eac9431d47c37fc6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:08:24 2018 -0500 Remove CSP whitelisting of `blob:` We no longer use `autoOrientImage` using blob URLs. Bring this back if we decide to auto-orient legacy attachments. commit 879b6f58f4a3f4a9ed6915af6b1be46c1e90e0ca Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:57:05 2018 -0500 Use `Message` type to determine send function Throws on invalid message type. commit 5203d945c98fd2562ae4e22c5c9838d27dec305b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:48 2018 -0500 Whitelist `Whisper` global commit 8ad0b066a3690d3382b86bf6ac00c03df7d1e20b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:32 2018 -0500 Add `Whisper.Types` namespace This avoids namespace collision for `Whisper.Message`. commit 785a949fce2656ca7dcaf0869d6b9e0648114e80 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:55:43 2018 -0500 Add `Message` type commit 674a7357abf0dcc365455695d56c0479998ebf27 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:35:23 2018 -0500 Run ESLint on `Conversation::sendMessage` commit cd985aa700caa80946245b17ea1b856449f152a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:34:38 2018 -0500 Document type signature of `FileInputView::readFile` commit d70d70e52c49588a1dc9833dfe5dd7128e13607f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:31:16 2018 -0500 Move attachment processing closer to sending This helps ensure processing happens uniformly, regardless of which code paths are taken to send an attachment. commit 532ac3e273a26b97f831247f9ee3412621b5c112 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:22:29 2018 -0500 Process attachment before it’s sent Picked this place since it already had various async steps, similar to `onMessageReceived` for the incoming `Attachment.process`. Could we try have this live closer to where we store it in IndexedDB, e.g. `Conversation::sendMessage`? commit a4582ae2fb6e1d3487131ba1f8fa6a00170cb32c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:21:42 2018 -0500 Refactor `getFile` and `getFiles` Lint them using ESLint. commit 07e9114e65046d791fc4f6ed90d6e2e938ad559d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:37:31 2018 -0500 Document incoming and outgoing attachments fields Note how outgoing message attachments only have 4 fields. This presumably means the others are not used in our code and could be discarded for simplicity. commit fdc3ef289d6ec1be344a12d496839d5ba747bb6a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:36:21 2018 -0500 Highlight that `dataURLToBlob` is synchronous commit b9c6bf600fcecedfd649ef2ae3c8629cced4e45a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:35:49 2018 -0500 Add EditorConfig configuration commit e56101e229d56810c8e31ad7289043a152c6c449 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:34:23 2018 -0500 Replace custom with `blob-util` functions IMPORTANT: All of them are async so we need to use `await`, otherwise we get strange or silent errors. commit f95150f6a9569fabcb31f3acd9f6b7bf50b5d145 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:17:30 2018 -0500 Revert "Replace custom functions with `blob-util`" This reverts commit 8a81e9c01bfe80c0e1bf76737092206c06949512. commit 33860d93f3d30ec55c32f3f4a58729df2eb43f0d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:13:02 2018 -0500 Revert "Replace `blueimp-canvas-to-blob` with `blob-util`" This reverts commit 31b3e853e4afc78fe80995921aa4152d9f6e4783. commit 7a0ba6fed622d76a3c39c7f03de541a7edb5b8dd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:12:58 2018 -0500 Replace `blueimp-canvas-to-blob` with `blob-util` commit 47a5f2bfd8b3f546e27e8d2b7e1969755d825a66 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:55:34 2018 -0500 Replace custom functions with `blob-util` commit 1cfa0efdb4fb1265369e2bf243c21f04f044fa01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:47:02 2018 -0500 Add `blob-util` dependency commit 9ac26be1bd783cd5070d886de107dd3ad9c91ad1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:44 2018 -0500 Document why we drop original image data during auto-orient commit 4136d6c382b99f41760a4da519d0db537fa7de8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:27 2018 -0500 Extract `DEFAULT_JPEG_QUALITY` commit 4a7156327eb5f94dba80cb300b344ac591226b0e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:37:11 2018 -0500 Drop support for invalid `image/jpg` MIME type commit 69fe96581f25413194032232f1bf704312e4754c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:30 2018 -0500 Document `window.onInvalidStateError` global commit a48ba1c77458da38583ee9cd488f70a59f6ee0fd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:04 2018 -0500 Selectively run ESLint on `js/background.js` Enabling ESLint on a per function basis allows us to incrementally improve the codebase without requiring large and potentially risky refactorings. commit e6d1cf826befc17ad4ec72fda8e761701665635e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:16:23 2018 -0500 Move async attachment processing to `onMessageReceived` We previously processed attachments in `handleDataMessage` which is mostly a synchronous function, except for the saving of the model. Moving the processing into the already async `onMessageReceived` improves code clarity. commit be6ca2a9aae5b59c360817deb1e18d39d705755e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:49 2018 -0500 Document import of ES2015+ modules commit eaaf7c41608fb988b8f4bbaa933cff110115610e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:29 2018 -0500 :art: Fix lint error commit a25b0e2e3d0f72c6a7bf0a15683f02450d5209ee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:13:57 2018 -0500 :art: Organize `require`s commit e0cc3d8fab6529d01b388acddf8605908c3d236b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:07:17 2018 -0500 Implement attachment process version Instead of keeping track of last normalization (processing) date, we now keep track of an internal processing version that will help us understand what kind of processing has already been completed for a given attachment. This will let us retroactively upgrade existing attachments. As we add more processing steps, we can build a processing pipeline that can convert any attachment processing version into a higher one, e.g. 4 -> 5 -> 6 -> 7. commit ad9083d0fdb880bc518e02251e51a39f7e1c585f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:50:31 2018 -0500 Ignore ES2015+ files during JSCS linting commit 96641205f734927aaebc2342d977c555799c3e3b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:48:07 2018 -0500 Improve ESLint ignore rules Apparently, using unqualified `/**` patterns prevents `!` include patterns. Using qualified glob patterns, e.g. `js/models/**/*.js`, lets us work around this. commit 255e0ab15bd1a0ca8ca5746e42d23977c8765d01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:44:59 2018 -0500 :abc: ESLint ignored files commit ebcb70258a26f234bd602072ac7c0a1913128132 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:47 2018 -0500 Whitelist `browser` environment for ESLint commit 3eaace6f3a21421c5aaaaf01592408c7ed83ecd3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:05 2018 -0500 Use `MIME` module commit ba2cf7770e614415733414a2dcc48f110b929892 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:32:54 2018 -0500 :art: Fix lint errors commit 65acc86e8580e88f7a6611eb4b8fa5d7291f7a3f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:30:42 2018 -0500 Add ES2015+ files to JSHint ignored list commit 8b6494ae6c9247acdfa059a9b361ec5ffcdb39f0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:29:20 2018 -0500 Document potentially unexpected `autoScale` behavior commit 8b4c69b2002d1777d3621be10f92cbf432f9d4d6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:26:47 2018 -0500 Test CommonJS modules separately Not sure how to test them as part of Grunt `unit-tests` task as `test/index.html` doesn’t allow for inclusion of CommonJS modules that use `require`. The tests are silently skipped. commit 213400e4b2bba3efee856a25b40e269221c3c39d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:24:27 2018 -0500 Add `MIME` type module commit 37a726e4fb4b3ed65914463122a5662847b5adee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:18:05 2018 -0500 Return proper `Error` from `blobArrayToBuffer` commit 164752db5612220e4dcf58d57bcd682cb489a399 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:15:41 2018 -0500 :art: Fix ESLint errors commit d498dd79a067c75098dd3179814c914780e5cb4f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:14:33 2018 -0500 Update `Attachment` type field definitions commit 141155a1533ff8fb616b70ea313432781bbebffd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:12:50 2018 -0500 Move `blueimp-canvas-to-blob` from Bower to npm commit 7ccb833e5d286ddd6235d3e491c62ac1e4544510 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:33:50 2018 -0500 :art: Clarify data flow commit e7da41591fde5a830467bebf1b6f51c1f7293e74 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:31:21 2018 -0500 Use `blobUrl` for consistency commit 523a80eefe0e2858aa1fb2bb9539ec44da502963 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:28:06 2018 -0500 Remove just-in-time image auto-orient for lightbox We can bring this back if our users would like auto-orient for old attachments. commit 0739feae9c47dd523c10740d6cdf746d539f270c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:27:21 2018 -0500 Remove just-in-time auto-orient of message attachments We can bring this back if our users would like auto-orient for old attachments. But better yet, we might implement this as database migration. commit ed43c66f92830ee233d5a94d0545eea4da43894d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:26:24 2018 -0500 Auto-orient JPEG attachments upon receipt commit e2eb8e36b017b048d57602fca14e45d657e0e1a1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:25:26 2018 -0500 Expose `Attachment` type through `Whisper.Attachment` commit 9638fbc987b84f143ca34211dc4666d96248ea2f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:39 2018 -0500 Use `contentType` from `model` commit 032c0ced46c3876cb9474b26f9d53d6f1c6b16a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:04 2018 -0500 Return `Error` object for `autoOrientImage` failures commit ff04bad8510c4b21aef350bed2b1887d0e055b98 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:22:32 2018 -0500 Add `options` for `autoOrientImage` output type / quality commit 87745b5586d1e182b51c9f9bc5e4eaf6dbc16722 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:18:46 2018 -0500 Add `Attachment` type Defines various functions on attachments, e.g. normalization (auto-orient JPEGs, etc.) commit de27fdc10a53bc8882a9c978e82265db9ac6d6f5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:16:34 2018 -0500 Add `yarn grunt` shortcut This allows us to use local `grunt-cli` for `grunt dev`. commit 59974db5a5da0d8f4cdc8ce5c4e3c974ecd5e754 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:10:11 2018 -0500 Improve readability commit b5ba96f1e6f40f2e1fa77490c583217768e1f412 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:08:12 2018 -0500 Use `snake_case` for module names Prevents problems across case-sensitive and case-insensitive file systems. We can work around this in the future using a lint rule such as `eslint-plugin-require-path-exists`. See discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r167365931 commit 48c5d3155c96ef628b00d99b52975e580d1d5501 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:05:44 2018 -0500 :art: Use destructuring commit 4822f49f22382a99ebf142b337375f7c25251d76 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:41:40 2018 -0500 Auto-orient images in lightbox view commit 7317110809677dddbbef3fadbf912cdba1c010bf Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:40:14 2018 -0500 Document magic number for escape key commit c790d07389a7d0bbf5298de83dbcfa8be1e7696b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:38:35 2018 -0500 Make second `View` argument an `options` object commit fbe010bb63d0088af9dfe11f153437fab34247e0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:27:40 2018 -0500 Allow `loadImage` to fetch `blob://` URLs commit ec35710d002b019a273eeb48f94dcaf2babe5d96 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:48 2018 -0500 :art: Shorten `autoOrientImage` import commit d07433e3cf316c6a143a0c9393ba26df9e3af17b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:19 2018 -0500 Make `autoOrientImage` module standalone commit c285bf5e33cdf10e0ef71e72cd6f55aef0df96ef Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:55:44 2018 -0500 Replace `loadImage` with `autoOrientImage` commit 44318549235af01fd061c25f557c93fd21cebb7a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:53:23 2018 -0500 Add `autoOrientImage` module This module exposes `loadImage` with a `Promise` based interface and pre- populates `orientation: true` option to auto-orient input. Returns data URL as string. The module uses a named export as refactoring references of modules with `default` (`module.exports`) export references can be error-prone. See: https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html commit c77063afc6366fe49615052796fe46f9b369de39 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:44:30 2018 -0500 Auto-orient preview images See: #998 commit 06dba5eb8f662c11af3a9ba8395bb453ab2e5f8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:43:23 2018 -0500 TODO: Use native `Canvas::toBlob` One challenge is that `Canvas::toBlob` is async whereas `dataURLtoBlob` is sync. commit b15c304a3125dd023fd90990e6225a7303f3596f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:42:45 2018 -0500 Make `null` check strict Appeases JSHint. ESLint has a nice `smart` option for `eqeqeq` rule: https://eslint.org/docs/rules/eqeqeq#smart commit ea70b92d9b18201758e11fdc25b09afc97b50055 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 15:23:58 2018 -0500 Use `Canvas::toDataURL` to preserve `ImageView` logic This way, all the other code paths remain untouched in case we want to remove the auto-orient code once Chrome supports the `image-orientation` CSS property. See: - #998 - https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation commit 62fd744f9f27d951573a68d2cdfe7ba2a3784b41 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:38:04 2018 -0500 Use CSS to constrain auto-oriented images commit f4d3392687168c237441b29140c7968b49dbef9e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:35:02 2018 -0500 Replace `ImageView` `el` with auto-oriented `canvas` See: #998 commit 1602d7f610e4993ad1291f88197f9ead1e25e776 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:48 2018 -0500 Pass `Blob` to `View` (for `ImageView`) This allows us to do JPEG auto-orientation based on EXIF metadata. commit e6a414f2b2a80da1137b839b348a38510efd04bb Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:12 2018 -0500 :hocho: Remove newline commit 5f0d9570d7862fc428ff89c2ecfd332a744537e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:17:02 2018 -0500 Expose `blueimp-load-image` as `window.loadImage` commit 1e1c62fe2f6a76dbcf1998dd468c26187c9871dc Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:16:46 2018 -0500 Add `blueimp-load-image` npm dependency commit ad17fa8a68a21ca5ddec336801b8568009bef3d4 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:14:40 2018 -0500 Remove `blueimp-load-image` Bower dependency
2018-02-21 15:26:59 +00:00
return message.send(this.wrapSend(promise));
Auto-orient image attachments based on EXIF metadata As described in #998, images are sometimes displayed with an incorrect orientation. This is because cameras often write files in the native sensor byte order and attach the `Orientation` EXIF metadata to tell end-user devices how to display the images based on the original author’s capture orientation. Electron/Chromium (and therefore Signal Desktop) currently doesn’t support applying this metadata for `<img>` tags, e.g. CSS `image-orientation: from- image`. As a workaround, this change uses the `loadImage` library with the `orientation: true` flag to auto-orient images ~~before display~~ upon receipt and before sending. **Changes** - [x] ~~Auto-orient images during display in message list view~~ - [x] Ensure image is not displayed until loaded (to prevent layout reflow) . - [x] Auto-orient images upon receipt and before storing in IndexedDB (~~or preserve original data until Chromium offers native fix?~~) - [x] Auto-orient images in compose area preview. - [x] ~~Auto-orient images in lightbox view~~ - [x] Auto-orient images before sending / storage. - [x] Add EditorConfig for sharing code styles across editors. - [x] Fix ESLint ignore file. - [x] Change `function-paren-newline` ESLint rule from `consistent` to `multiline`. - [x] Add `operator-linebreak` ESLint rule for consistency. - [x] Added `blob-util` dependency for converting between array buffers, blobs, etc. - [x] Extracted `createMessageHandler` to consolidate logic for `onMessageReceived` and `onSentMessage`. - [x] Introduce `async` / `await` to simplify async coding (restore control flow for branching, loops, and exceptions). - [x] Introduce `window.Signal` namespace for exposing ES2015+ CommonJS modules. - [x] Introduce rudimentary `Message` and `Attachment` types to begin defining a schema and versioning. This will allow us to track which changes, e.g. auto-orient JPEGs, per message / attachment as well as which fields are stored. - [x] Renamed `window.dataURLtoBlob` to `window.dataURLToBlobSync` to both fix the strange `camelCase` as well as to highlight that this operation is synchronous and therefore blocks the user thread. - [x] Normalize all JPEG MIME types to `image/jpeg`, eliminating the invalid `image/jpg`. - [x] Add `npm run test-modules` command for testing non-browser specific CommonJS modules. - **Stretch Goals** - [ ] ~~Restrict `autoOrientImage` to `Blob` to narrow API interface.~~ Do this once we use PureScript. - [ ] ~~Return non-JPEGs as no-op from `autoOrientImage`.~~ Skipping `autoOrientImage` for non-JPEGs altogether. - [ ] Retroactively auto-orient existing JPEG image attachments in the background. --- Fixes #998 --- - **Blog:** EXIF Orientation Handling Is a Ghetto: https://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ - **Chromium Bug:** EXIF orientation is ignored: https://bugs.chromium.org/p/chromium/issues/detail?id=56845 - **Chromium Bug:** Support for the CSS image-orientation CSS property: https://bugs.chromium.org/p/chromium/issues/detail?id=158753 --- commit ce5090b473a2448229dc38e4c3f15d7ad0137714 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 16 10:35:36 2018 -0500 Inline message descriptors commit 329036e59c138c1e950ec7c654eebd7d87076de5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:34:40 2018 -0500 Clarify order of operations Semantically, it makes more sense to do `getFile` before `clearForm` even though it seems to work either way. commit f9d4cfb2ba0d8aa308b0923bbe6066ea34cb97bd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:18:26 2018 -0500 Simplify `operator-linebreak` configuration Enabling `before` caused more code changes and it turns out our previous configuration is already the default. commit db588997acdd90ed2ad829174ecbba744383c78b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:15:59 2018 -0500 Remove obsolete TODO commit 799c8817633f6afa0b731fc3b5434e463bd850e3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:12:18 2018 -0500 Enable ESLint `function-paren-newline` `multiline` Per discussion. commit b660b6bc8ef41df7601a411213d6cda80821df87 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Thu Feb 15 17:10:48 2018 -0500 Use `messageDescriptor.id` not `source` commit 5e7309d176f4a7e97d3dc4c738e6b0ccd4792871 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:29:01 2018 -0500 Remove unnecessary `eslint-env` commit 393b3da55eabd7413596c86cc3971b063a0efe31 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:19:17 2018 -0500 Refactor `onSentMessage` and `onMessageReceived` Since they are so similar, we create the handlers using new `createMessageHandler` function. This allows us to ensure both synced and received messages go through schema upgrade pipeline. commit b3db0bf179c9a5bea96480cde28c6fa7193ac117 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 16:18:21 2018 -0500 Add `Message` descriptor functions commit 8febf125b1b42fe4ae1888dd50fcee2749dc1ff0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 14:46:56 2018 -0500 Fix typo commit 98d951ef77bd578b313a4ff4b496b793e82e88d5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:39 2018 -0500 Remove `promises` reference commit a0e9559ed5bed947dabf28cb672e63d39948d854 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:22:13 2018 -0500 Fix `AttachmentView::mediaType` fall-through commit 67be916a83951b8a1f9b22efe78a6da6b1825f38 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 12:03:41 2018 -0500 Remove minor TODOs commit 0af186e118256b62905de38487ffacc41693ff47 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:41 2018 -0500 Enable ESLint for `js/views/attachment_view.js` commit 28a2dc5b8a28e1a087924fdc7275bf7d9a577b92 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:44:12 2018 -0500 Remove dynamic type checks commit f4ce36fcfc2737de32d911cd6103f889097813f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:27:56 2018 -0500 Rename `process` to `upgradeSchema` - `Message.process` -> `Message.upgradeSchema` - `Attachment.process` -> `Attachment.upgradeSchema` - `Attachment::processVersion` -> `Attachment::schemaVersion` Document version history. commit 41b92c0a31050ba05ddb1c43171d651f3568b9ac Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:11:50 2018 -0500 Add `operator-linebreak` ESLint rule Based on the following discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r168029106 commit 462defbe55879060fe25bc69103d4429bae2b2f6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Wed Feb 14 11:01:30 2018 -0500 Add missing `await` for `ConversationController.getOrCreateAndWait` Tested this by setting `if` condition to `true` and confirming it works. It turns rotating a profile key is more involved and might require registering a new account according to Matthew. commit c08058ee4b883b3e23a40683de802ac81ed74874 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 16:32:24 2018 -0500 Convert `FileList` to `Array` commit 70a6c4201925f57be1f94d9da3547fdefc7bbb53 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:46:34 2018 -0500 :art: Fix lint errors commit 2ca7cdbc31d4120d6c6a838a6dcf43bc209d9788 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 15:07:09 2018 -0500 Skip `autoOrientImage` for non-JPEG images commit 58eac383013c16ca363a4ed33dca5c7ba61284e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:55:35 2018 -0500 Move new-style modules to `window.Signal` namespace commit 02c9328877dce289d6116a18b1c223891bd3cd0b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 14:35:23 2018 -0500 Extract `npm run test-modules` command commit 2c708eb94fba468b81ea9427734896114f5a7807 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:51 2018 -0500 Extract `Message.process` commit 4a2e52f68a77536a0fa04aa3c29ad3e541a8fa7e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:25:12 2018 -0500 Fix EditorConfig commit a346bab5db082720f5d47363f06301380e870425 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:13:02 2018 -0500 Remove `vim` directives on ESLint-ed files commit 7ec885c6359e495b407d5bc3eac9431d47c37fc6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 13:08:24 2018 -0500 Remove CSP whitelisting of `blob:` We no longer use `autoOrientImage` using blob URLs. Bring this back if we decide to auto-orient legacy attachments. commit 879b6f58f4a3f4a9ed6915af6b1be46c1e90e0ca Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:57:05 2018 -0500 Use `Message` type to determine send function Throws on invalid message type. commit 5203d945c98fd2562ae4e22c5c9838d27dec305b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:48 2018 -0500 Whitelist `Whisper` global commit 8ad0b066a3690d3382b86bf6ac00c03df7d1e20b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:56:32 2018 -0500 Add `Whisper.Types` namespace This avoids namespace collision for `Whisper.Message`. commit 785a949fce2656ca7dcaf0869d6b9e0648114e80 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:55:43 2018 -0500 Add `Message` type commit 674a7357abf0dcc365455695d56c0479998ebf27 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:35:23 2018 -0500 Run ESLint on `Conversation::sendMessage` commit cd985aa700caa80946245b17ea1b856449f152a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:34:38 2018 -0500 Document type signature of `FileInputView::readFile` commit d70d70e52c49588a1dc9833dfe5dd7128e13607f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:31:16 2018 -0500 Move attachment processing closer to sending This helps ensure processing happens uniformly, regardless of which code paths are taken to send an attachment. commit 532ac3e273a26b97f831247f9ee3412621b5c112 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:22:29 2018 -0500 Process attachment before it’s sent Picked this place since it already had various async steps, similar to `onMessageReceived` for the incoming `Attachment.process`. Could we try have this live closer to where we store it in IndexedDB, e.g. `Conversation::sendMessage`? commit a4582ae2fb6e1d3487131ba1f8fa6a00170cb32c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 12:21:42 2018 -0500 Refactor `getFile` and `getFiles` Lint them using ESLint. commit 07e9114e65046d791fc4f6ed90d6e2e938ad559d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:37:31 2018 -0500 Document incoming and outgoing attachments fields Note how outgoing message attachments only have 4 fields. This presumably means the others are not used in our code and could be discarded for simplicity. commit fdc3ef289d6ec1be344a12d496839d5ba747bb6a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:36:21 2018 -0500 Highlight that `dataURLToBlob` is synchronous commit b9c6bf600fcecedfd649ef2ae3c8629cced4e45a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:35:49 2018 -0500 Add EditorConfig configuration commit e56101e229d56810c8e31ad7289043a152c6c449 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:34:23 2018 -0500 Replace custom with `blob-util` functions IMPORTANT: All of them are async so we need to use `await`, otherwise we get strange or silent errors. commit f95150f6a9569fabcb31f3acd9f6b7bf50b5d145 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:17:30 2018 -0500 Revert "Replace custom functions with `blob-util`" This reverts commit 8a81e9c01bfe80c0e1bf76737092206c06949512. commit 33860d93f3d30ec55c32f3f4a58729df2eb43f0d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:13:02 2018 -0500 Revert "Replace `blueimp-canvas-to-blob` with `blob-util`" This reverts commit 31b3e853e4afc78fe80995921aa4152d9f6e4783. commit 7a0ba6fed622d76a3c39c7f03de541a7edb5b8dd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 11:12:58 2018 -0500 Replace `blueimp-canvas-to-blob` with `blob-util` commit 47a5f2bfd8b3f546e27e8d2b7e1969755d825a66 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:55:34 2018 -0500 Replace custom functions with `blob-util` commit 1cfa0efdb4fb1265369e2bf243c21f04f044fa01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:47:02 2018 -0500 Add `blob-util` dependency commit 9ac26be1bd783cd5070d886de107dd3ad9c91ad1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:44 2018 -0500 Document why we drop original image data during auto-orient commit 4136d6c382b99f41760a4da519d0db537fa7de8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:46:27 2018 -0500 Extract `DEFAULT_JPEG_QUALITY` commit 4a7156327eb5f94dba80cb300b344ac591226b0e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 10:37:11 2018 -0500 Drop support for invalid `image/jpg` MIME type commit 69fe96581f25413194032232f1bf704312e4754c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:30 2018 -0500 Document `window.onInvalidStateError` global commit a48ba1c77458da38583ee9cd488f70a59f6ee0fd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:54:04 2018 -0500 Selectively run ESLint on `js/background.js` Enabling ESLint on a per function basis allows us to incrementally improve the codebase without requiring large and potentially risky refactorings. commit e6d1cf826befc17ad4ec72fda8e761701665635e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:16:23 2018 -0500 Move async attachment processing to `onMessageReceived` We previously processed attachments in `handleDataMessage` which is mostly a synchronous function, except for the saving of the model. Moving the processing into the already async `onMessageReceived` improves code clarity. commit be6ca2a9aae5b59c360817deb1e18d39d705755e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:49 2018 -0500 Document import of ES2015+ modules commit eaaf7c41608fb988b8f4bbaa933cff110115610e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:14:29 2018 -0500 :art: Fix lint error commit a25b0e2e3d0f72c6a7bf0a15683f02450d5209ee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:13:57 2018 -0500 :art: Organize `require`s commit e0cc3d8fab6529d01b388acddf8605908c3d236b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 09:07:17 2018 -0500 Implement attachment process version Instead of keeping track of last normalization (processing) date, we now keep track of an internal processing version that will help us understand what kind of processing has already been completed for a given attachment. This will let us retroactively upgrade existing attachments. As we add more processing steps, we can build a processing pipeline that can convert any attachment processing version into a higher one, e.g. 4 -> 5 -> 6 -> 7. commit ad9083d0fdb880bc518e02251e51a39f7e1c585f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:50:31 2018 -0500 Ignore ES2015+ files during JSCS linting commit 96641205f734927aaebc2342d977c555799c3e3b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:48:07 2018 -0500 Improve ESLint ignore rules Apparently, using unqualified `/**` patterns prevents `!` include patterns. Using qualified glob patterns, e.g. `js/models/**/*.js`, lets us work around this. commit 255e0ab15bd1a0ca8ca5746e42d23977c8765d01 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:44:59 2018 -0500 :abc: ESLint ignored files commit ebcb70258a26f234bd602072ac7c0a1913128132 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:47 2018 -0500 Whitelist `browser` environment for ESLint commit 3eaace6f3a21421c5aaaaf01592408c7ed83ecd3 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:35:05 2018 -0500 Use `MIME` module commit ba2cf7770e614415733414a2dcc48f110b929892 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:32:54 2018 -0500 :art: Fix lint errors commit 65acc86e8580e88f7a6611eb4b8fa5d7291f7a3f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:30:42 2018 -0500 Add ES2015+ files to JSHint ignored list commit 8b6494ae6c9247acdfa059a9b361ec5ffcdb39f0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:29:20 2018 -0500 Document potentially unexpected `autoScale` behavior commit 8b4c69b2002d1777d3621be10f92cbf432f9d4d6 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:26:47 2018 -0500 Test CommonJS modules separately Not sure how to test them as part of Grunt `unit-tests` task as `test/index.html` doesn’t allow for inclusion of CommonJS modules that use `require`. The tests are silently skipped. commit 213400e4b2bba3efee856a25b40e269221c3c39d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Tue Feb 13 08:24:27 2018 -0500 Add `MIME` type module commit 37a726e4fb4b3ed65914463122a5662847b5adee Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:18:05 2018 -0500 Return proper `Error` from `blobArrayToBuffer` commit 164752db5612220e4dcf58d57bcd682cb489a399 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:15:41 2018 -0500 :art: Fix ESLint errors commit d498dd79a067c75098dd3179814c914780e5cb4f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:14:33 2018 -0500 Update `Attachment` type field definitions commit 141155a1533ff8fb616b70ea313432781bbebffd Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 20:12:50 2018 -0500 Move `blueimp-canvas-to-blob` from Bower to npm commit 7ccb833e5d286ddd6235d3e491c62ac1e4544510 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:33:50 2018 -0500 :art: Clarify data flow commit e7da41591fde5a830467bebf1b6f51c1f7293e74 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:31:21 2018 -0500 Use `blobUrl` for consistency commit 523a80eefe0e2858aa1fb2bb9539ec44da502963 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:28:06 2018 -0500 Remove just-in-time image auto-orient for lightbox We can bring this back if our users would like auto-orient for old attachments. commit 0739feae9c47dd523c10740d6cdf746d539f270c Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:27:21 2018 -0500 Remove just-in-time auto-orient of message attachments We can bring this back if our users would like auto-orient for old attachments. But better yet, we might implement this as database migration. commit ed43c66f92830ee233d5a94d0545eea4da43894d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:26:24 2018 -0500 Auto-orient JPEG attachments upon receipt commit e2eb8e36b017b048d57602fca14e45d657e0e1a1 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:25:26 2018 -0500 Expose `Attachment` type through `Whisper.Attachment` commit 9638fbc987b84f143ca34211dc4666d96248ea2f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:39 2018 -0500 Use `contentType` from `model` commit 032c0ced46c3876cb9474b26f9d53d6f1c6b16a0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:23:04 2018 -0500 Return `Error` object for `autoOrientImage` failures commit ff04bad8510c4b21aef350bed2b1887d0e055b98 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:22:32 2018 -0500 Add `options` for `autoOrientImage` output type / quality commit 87745b5586d1e182b51c9f9bc5e4eaf6dbc16722 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:18:46 2018 -0500 Add `Attachment` type Defines various functions on attachments, e.g. normalization (auto-orient JPEGs, etc.) commit de27fdc10a53bc8882a9c978e82265db9ac6d6f5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 16:16:34 2018 -0500 Add `yarn grunt` shortcut This allows us to use local `grunt-cli` for `grunt dev`. commit 59974db5a5da0d8f4cdc8ce5c4e3c974ecd5e754 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:10:11 2018 -0500 Improve readability commit b5ba96f1e6f40f2e1fa77490c583217768e1f412 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:08:12 2018 -0500 Use `snake_case` for module names Prevents problems across case-sensitive and case-insensitive file systems. We can work around this in the future using a lint rule such as `eslint-plugin-require-path-exists`. See discussion: https://github.com/signalapp/Signal-Desktop/pull/2040#discussion_r167365931 commit 48c5d3155c96ef628b00d99b52975e580d1d5501 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Mon Feb 12 10:05:44 2018 -0500 :art: Use destructuring commit 4822f49f22382a99ebf142b337375f7c25251d76 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:41:40 2018 -0500 Auto-orient images in lightbox view commit 7317110809677dddbbef3fadbf912cdba1c010bf Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:40:14 2018 -0500 Document magic number for escape key commit c790d07389a7d0bbf5298de83dbcfa8be1e7696b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:38:35 2018 -0500 Make second `View` argument an `options` object commit fbe010bb63d0088af9dfe11f153437fab34247e0 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 17:27:40 2018 -0500 Allow `loadImage` to fetch `blob://` URLs commit ec35710d002b019a273eeb48f94dcaf2babe5d96 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:48 2018 -0500 :art: Shorten `autoOrientImage` import commit d07433e3cf316c6a143a0c9393ba26df9e3af17b Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:57:19 2018 -0500 Make `autoOrientImage` module standalone commit c285bf5e33cdf10e0ef71e72cd6f55aef0df96ef Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:55:44 2018 -0500 Replace `loadImage` with `autoOrientImage` commit 44318549235af01fd061c25f557c93fd21cebb7a Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:53:23 2018 -0500 Add `autoOrientImage` module This module exposes `loadImage` with a `Promise` based interface and pre- populates `orientation: true` option to auto-orient input. Returns data URL as string. The module uses a named export as refactoring references of modules with `default` (`module.exports`) export references can be error-prone. See: https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html commit c77063afc6366fe49615052796fe46f9b369de39 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:44:30 2018 -0500 Auto-orient preview images See: #998 commit 06dba5eb8f662c11af3a9ba8395bb453ab2e5f8d Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:43:23 2018 -0500 TODO: Use native `Canvas::toBlob` One challenge is that `Canvas::toBlob` is async whereas `dataURLtoBlob` is sync. commit b15c304a3125dd023fd90990e6225a7303f3596f Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 16:42:45 2018 -0500 Make `null` check strict Appeases JSHint. ESLint has a nice `smart` option for `eqeqeq` rule: https://eslint.org/docs/rules/eqeqeq#smart commit ea70b92d9b18201758e11fdc25b09afc97b50055 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 15:23:58 2018 -0500 Use `Canvas::toDataURL` to preserve `ImageView` logic This way, all the other code paths remain untouched in case we want to remove the auto-orient code once Chrome supports the `image-orientation` CSS property. See: - #998 - https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation commit 62fd744f9f27d951573a68d2cdfe7ba2a3784b41 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:38:04 2018 -0500 Use CSS to constrain auto-oriented images commit f4d3392687168c237441b29140c7968b49dbef9e Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:35:02 2018 -0500 Replace `ImageView` `el` with auto-oriented `canvas` See: #998 commit 1602d7f610e4993ad1291f88197f9ead1e25e776 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:48 2018 -0500 Pass `Blob` to `View` (for `ImageView`) This allows us to do JPEG auto-orientation based on EXIF metadata. commit e6a414f2b2a80da1137b839b348a38510efd04bb Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 14:25:12 2018 -0500 :hocho: Remove newline commit 5f0d9570d7862fc428ff89c2ecfd332a744537e5 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:17:02 2018 -0500 Expose `blueimp-load-image` as `window.loadImage` commit 1e1c62fe2f6a76dbcf1998dd468c26187c9871dc Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:16:46 2018 -0500 Add `blueimp-load-image` npm dependency commit ad17fa8a68a21ca5ddec336801b8568009bef3d4 Author: Daniel Gasienica <daniel@gasienica.ch> Date: Fri Feb 9 11:14:40 2018 -0500 Remove `blueimp-load-image` Bower dependency
2018-02-21 15:26:59 +00:00
});
},
wrapSend(promise) {
return promise.then(
async result => {
// success
2018-10-31 23:58:14 +00:00
if (result) {
await this.handleMessageSendResult(
result.failoverIdentifiers,
2020-09-04 01:25:19 +00:00
result.unidentifiedDeliveries,
result.discoveredIdentifierPairs
2018-10-31 23:58:14 +00:00
);
}
return result;
},
async result => {
// failure
2018-10-31 23:58:14 +00:00
if (result) {
await this.handleMessageSendResult(
result.failoverIdentifiers,
2020-09-04 01:25:19 +00:00
result.unidentifiedDeliveries,
result.discoveredIdentifierPairs
2018-10-31 23:58:14 +00:00
);
}
throw result;
}
);
},
2020-09-04 01:25:19 +00:00
async handleMessageSendResult(
failoverIdentifiers,
unidentifiedDeliveries,
discoveredIdentifierPairs
) {
discoveredIdentifierPairs.forEach(item => {
const { uuid, e164 } = item;
window.ConversationController.ensureContactIds({
uuid,
e164,
highTrust: true,
});
});
2018-10-31 23:58:14 +00:00
await Promise.all(
(failoverIdentifiers || []).map(async identifier => {
const conversation = ConversationController.get(identifier);
2018-10-31 23:58:14 +00:00
if (
conversation &&
conversation.get('sealedSender') !== SEALED_SENDER.DISABLED
) {
window.log.info(
`Setting sealedSender to DISABLED for conversation ${conversation.idForLogging()}`
);
2018-10-31 23:58:14 +00:00
conversation.set({
sealedSender: SEALED_SENDER.DISABLED,
});
window.Signal.Data.updateConversation(conversation.attributes);
2018-10-31 23:58:14 +00:00
}
})
);
await Promise.all(
(unidentifiedDeliveries || []).map(async identifier => {
const conversation = ConversationController.get(identifier);
2018-10-31 23:58:14 +00:00
if (
conversation &&
conversation.get('sealedSender') === SEALED_SENDER.UNKNOWN
) {
if (conversation.get('accessKey')) {
window.log.info(
`Setting sealedSender to ENABLED for conversation ${conversation.idForLogging()}`
);
2018-10-31 23:58:14 +00:00
conversation.set({
sealedSender: SEALED_SENDER.ENABLED,
});
} else {
window.log.info(
`Setting sealedSender to UNRESTRICTED for conversation ${conversation.idForLogging()}`
);
2018-10-31 23:58:14 +00:00
conversation.set({
sealedSender: SEALED_SENDER.UNRESTRICTED,
});
}
window.Signal.Data.updateConversation(conversation.attributes);
}
})
);
},
2018-11-07 19:20:43 +00:00
getSendOptions(options = {}) {
const senderCertificate = storage.get('senderCertificate');
const sendMetadata = this.getSendMetadata(options);
return {
senderCertificate,
sendMetadata,
};
},
getUuidCapable() {
return Boolean(_.property('uuid')(this.get('capabilities')));
},
getSendMetadata(options = {}) {
2018-11-07 19:20:43 +00:00
const { syncMessage, disableMeCheck } = options;
// START: this code has an Expiration date of ~2018/11/21
// We don't want to enable unidentified delivery for send unless it is
// also enabled for our own account.
const myId = ConversationController.getOurConversationId();
2020-06-12 22:36:32 +00:00
const me = ConversationController.get(myId);
2018-10-31 23:58:14 +00:00
if (
!disableMeCheck &&
me.get('sealedSender') === SEALED_SENDER.DISABLED
) {
return null;
}
2018-11-07 19:20:43 +00:00
// END
2018-10-31 23:58:14 +00:00
if (!this.isPrivate()) {
const infoArray = this.contactCollection.map(conversation =>
conversation.getSendMetadata(options)
2018-10-31 23:58:14 +00:00
);
return Object.assign({}, ...infoArray);
}
2018-10-31 23:58:14 +00:00
const accessKey = this.get('accessKey');
const sealedSender = this.get('sealedSender');
const uuidCapable = this.getUuidCapable();
2018-11-07 19:20:43 +00:00
// We never send sync messages as sealed sender
if (syncMessage && this.isMe()) {
2018-11-07 19:20:43 +00:00
return null;
}
const e164 = this.get('e164');
const uuid = this.get('uuid');
2018-10-31 23:58:14 +00:00
// If we've never fetched user's profile, we default to what we have
if (sealedSender === SEALED_SENDER.UNKNOWN) {
const info = {
accessKey: accessKey || arrayBufferToBase64(getRandomBytes(16)),
// Indicates that a client is capable of receiving uuid-only messages.
// Not used yet.
uuidCapable,
};
return {
...(e164 ? { [e164]: info } : {}),
...(uuid ? { [uuid]: info } : {}),
};
}
2018-10-31 23:58:14 +00:00
if (sealedSender === SEALED_SENDER.DISABLED) {
return null;
}
const info = {
accessKey:
accessKey && sealedSender === SEALED_SENDER.ENABLED
? accessKey
: arrayBufferToBase64(getRandomBytes(16)),
// Indicates that a client is capable of receiving uuid-only messages.
// Not used yet.
uuidCapable,
};
2018-10-31 23:58:14 +00:00
return {
...(e164 ? { [e164]: info } : {}),
...(uuid ? { [uuid]: info } : {}),
2018-10-31 23:58:14 +00:00
};
},
// Is this someone who is a contact, or are we sharing our profile with them?
// Or is the person who added us to this group a contact or are we sharing profile
// with them?
isFromOrAddedByTrustedContact() {
2020-05-27 21:37:06 +00:00
if (this.isPrivate()) {
return Boolean(this.get('name')) || this.get('profileSharing');
2020-05-27 21:37:06 +00:00
}
const addedBy = this.get('addedBy');
if (!addedBy) {
return false;
}
const conv = ConversationController.get(addedBy);
if (!conv) {
return false;
}
return Boolean(conv.get('name')) || conv.get('profileSharing');
2020-05-27 21:37:06 +00:00
},
async updateLastMessage() {
if (!this.id) {
return;
}
2020-08-07 00:50:54 +00:00
const [previewMessage, activityMessage] = await Promise.all([
window.Signal.Data.getLastConversationPreview(this.id, {
Message: Whisper.Message,
}),
window.Signal.Data.getLastConversationActivity(this.id, {
Message: Whisper.Message,
}),
]);
2019-08-07 00:40:25 +00:00
if (
this.hasDraft() &&
this.get('draftTimestamp') &&
(!previewMessage ||
previewMessage.get('sent_at') < this.get('draftTimestamp'))
2019-08-07 00:40:25 +00:00
) {
return;
}
2020-08-07 00:50:54 +00:00
const currentTimestamp = this.get('timestamp') || null;
const timestamp = activityMessage
2020-09-09 02:25:05 +00:00
? activityMessage.get('sent_at') ||
activityMessage.get('received_at') ||
currentTimestamp
2020-08-07 00:50:54 +00:00
: currentTimestamp;
this.set({
lastMessage:
(previewMessage ? previewMessage.getNotificationText() : '') || '',
lastMessageStatus:
(previewMessage ? previewMessage.getMessagePropStatus() : null) ||
null,
2020-08-07 00:50:54 +00:00
timestamp,
lastMessageDeletedForEveryone: previewMessage
? previewMessage.deletedForEveryone
: false,
});
2020-08-05 01:13:19 +00:00
window.Signal.Data.updateConversation(this.attributes);
},
2020-09-09 00:56:23 +00:00
setArchived(isArchived) {
const before = this.get('isArchived');
2019-03-12 00:20:16 +00:00
this.set({ isArchived });
window.Signal.Data.updateConversation(this.attributes);
2020-09-09 00:56:23 +00:00
const after = this.get('isArchived');
if (Boolean(before) !== Boolean(after)) {
this.captureChange();
}
2019-03-12 00:20:16 +00:00
},
2020-09-09 02:25:05 +00:00
async updateExpirationTimerInGroupV2(seconds) {
// Make change on the server
const actions = window.Signal.Groups.buildDisappearingMessagesTimerChange(
{
expireTimer: seconds || 0,
group: this.attributes,
}
);
let signedGroupChange;
try {
signedGroupChange = await window.Signal.Groups.uploadGroupChange({
actions,
group: this.attributes,
serverPublicParamsBase64: window.getServerPublicParams(),
});
} catch (error) {
// Get latest GroupV2 data, since we ran into trouble updating it
this.fetchLatestGroupV2Data();
throw error;
}
// Update local conversation
this.set({
expireTimer: seconds || 0,
revision: actions.version,
});
window.Signal.Data.updateConversation(this.attributes);
// Create local notification
const timestamp = Date.now();
const id = window.getGuid();
const message = MessageController.register(
id,
new Whisper.Message({
id,
conversationId: this.id,
sent_at: timestamp,
received_at: timestamp,
flags: textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
expirationTimerUpdate: {
expireTimer: seconds,
sourceUuid: this.ourUuid,
},
})
);
await window.Signal.Data.saveMessage(message.attributes, {
Message: Whisper.Message,
forceSave: true,
});
this.trigger('newmessage', message);
// Send message to all group members
const profileKey = this.get('profileSharing')
? storage.get('profileKey')
: undefined;
const sendOptions = this.getSendOptions();
const promise = textsecure.messaging.sendMessageToGroup(
{
groupV2: this.getGroupV2Info(signedGroupChange.toArrayBuffer()),
timestamp,
profileKey,
},
sendOptions
);
message.send(promise);
},
async updateExpirationTimer(
providedExpireTimer,
providedSource,
receivedAt,
options = {}
) {
2020-09-09 02:25:05 +00:00
if (this.get('groupVersion') === 2) {
if (providedSource || receivedAt) {
throw new Error(
'updateExpirationTimer: GroupV2 timers are not updated this way'
);
}
await this.updateExpirationTimerInGroupV2(providedExpireTimer);
return false;
}
let expireTimer = providedExpireTimer;
let source = providedSource;
if (this.get('left')) {
return false;
}
_.defaults(options, { fromSync: false, fromGroupUpdate: false });
if (!expireTimer) {
expireTimer = null;
}
2018-04-27 21:25:04 +00:00
if (
this.get('expireTimer') === expireTimer ||
(!expireTimer && !this.get('expireTimer'))
) {
2018-09-21 01:47:19 +00:00
return null;
}
window.log.info("Update conversation 'expireTimer'", {
2018-05-03 15:53:39 +00:00
id: this.idForLogging(),
expireTimer,
2018-05-03 15:53:39 +00:00
source,
});
source = source || ConversationController.getOurConversationId();
// When we add a disappearing messages notification to the conversation, we want it
// to be above the message that initiated that change, hence the subtraction.
const timestamp = (receivedAt || Date.now()) - 1;
2018-09-21 01:47:19 +00:00
this.set({ expireTimer });
window.Signal.Data.updateConversation(this.attributes);
const model = new Whisper.Message({
// Even though this isn't reflected to the user, we want to place the last seen
// indicator above it. We set it to 'unread' to trigger that placement.
unread: 1,
conversationId: this.id,
// No type; 'incoming' messages are specially treated by conversation.markRead()
sent_at: timestamp,
received_at: timestamp,
flags: textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
expirationTimerUpdate: {
expireTimer,
source,
fromSync: options.fromSync,
fromGroupUpdate: options.fromGroupUpdate,
},
});
if (this.isPrivate()) {
model.set({ destination: this.getSendTarget() });
}
if (model.isOutgoing()) {
model.set({ recipients: this.getRecipients() });
}
const id = await window.Signal.Data.saveMessage(model.attributes, {
Message: Whisper.Message,
});
model.set({ id });
const message = MessageController.register(id, model);
this.addSingleMessage(message);
// if change was made remotely, don't send it to the number/group
if (receivedAt) {
return message;
}
let profileKey;
if (this.get('profileSharing')) {
profileKey = storage.get('profileKey');
}
const sendOptions = this.getSendOptions();
let promise;
if (this.isMe()) {
const flags =
textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE;
const dataMessage = await textsecure.messaging.getMessageProto(
this.getSendTarget(),
2020-09-09 02:25:05 +00:00
null, // body
[], // attachments
null, // quote
[], // preview
null, // sticker
null, // reaction
message.get('sent_at'),
expireTimer,
profileKey,
flags
);
return message.sendSyncMessageOnly(dataMessage);
}
if (this.get('type') === 'private') {
promise = textsecure.messaging.sendExpirationTimerUpdateToIdentifier(
this.getSendTarget(),
expireTimer,
message.get('sent_at'),
profileKey,
sendOptions
);
} else {
promise = textsecure.messaging.sendExpirationTimerUpdateToGroup(
this.get('groupId'),
this.getRecipients(),
expireTimer,
message.get('sent_at'),
profileKey,
sendOptions
);
}
await message.send(this.wrapSend(promise));
return message;
},
2016-09-27 06:15:20 +00:00
async addMessageHistoryDisclaimer() {
const lastMessage = this.messageCollection.last();
if (
lastMessage &&
lastMessage.get('type') === 'message-history-unsynced'
) {
// We do not need another message history disclaimer
return lastMessage;
}
const timestamp = Date.now();
const model = new Whisper.Message({
type: 'message-history-unsynced',
// Even though this isn't reflected to the user, we want to place the last seen
// indicator above it. We set it to 'unread' to trigger that placement.
unread: 1,
conversationId: this.id,
// No type; 'incoming' messages are specially treated by conversation.markRead()
sent_at: timestamp,
received_at: timestamp,
});
if (this.isPrivate()) {
model.set({ destination: this.id });
}
if (model.isOutgoing()) {
model.set({ recipients: this.getRecipients() });
}
const id = await window.Signal.Data.saveMessage(model.attributes, {
Message: Whisper.Message,
});
model.set({ id });
const message = MessageController.register(id, model);
this.addSingleMessage(message);
return message;
},
isSearchable() {
return !this.get('left');
},
async endSession() {
if (this.isPrivate()) {
const now = Date.now();
const model = new Whisper.Message({
conversationId: this.id,
type: 'outgoing',
sent_at: now,
received_at: now,
destination: this.get('e164'),
destinationUuid: this.get('uuid'),
recipients: this.getRecipients(),
flags: textsecure.protobuf.DataMessage.Flags.END_SESSION,
});
const id = await window.Signal.Data.saveMessage(model.attributes, {
Message: Whisper.Message,
});
model.set({ id });
const message = MessageController.register(model.id, model);
this.addSingleMessage(message);
const options = this.getSendOptions();
message.send(
this.wrapSend(
textsecure.messaging.resetSession(
this.get('uuid'),
this.get('e164'),
now,
options
)
)
);
}
},
2018-09-21 01:47:19 +00:00
async markRead(newestUnreadDate, providedOptions) {
const options = providedOptions || {};
_.defaults(options, { sendReadReceipts: true });
const conversationId = this.id;
Whisper.Notifications.removeBy({ conversationId });
2018-09-21 01:47:19 +00:00
let unreadMessages = await this.getUnread();
const oldUnread = unreadMessages.filter(
message => message.get('received_at') <= newestUnreadDate
);
2018-09-21 01:47:19 +00:00
let read = await Promise.all(
_.map(oldUnread, async providedM => {
const m = MessageController.register(providedM.id, providedM);
2019-09-26 19:56:31 +00:00
// Note that this will update the message in the database
2018-09-21 01:47:19 +00:00
await m.markRead(options.readAt);
2019-09-26 19:56:31 +00:00
return {
senderE164: m.get('source'),
senderUuid: m.get('sourceUuid'),
senderId: ConversationController.ensureContactIds({
e164: m.get('source'),
uuid: m.get('sourceUuid'),
}),
timestamp: m.get('sent_at'),
2020-05-27 21:37:06 +00:00
hasErrors: m.hasErrors(),
};
2018-09-21 01:47:19 +00:00
})
);
2018-09-21 01:47:19 +00:00
// Some messages we're marking read are local notifications with no sender
read = _.filter(read, m => Boolean(m.senderId));
2018-09-21 01:47:19 +00:00
unreadMessages = unreadMessages.filter(m => Boolean(m.isIncoming()));
Feature: Blue check marks for read messages if opted in (#1489) * Refactor delivery receipt event handler * Rename the delivery receipt event For less ambiguity with read receipts. * Rename synced read event For less ambiguity with read receipts from other Signal users. * Add support for incoming receipt messages Handle ReceiptMessages, which may include encrypted delivery receipts or read receipts from recipients of our sent messages. // FREEBIE * Rename ReadReceipts to ReadSyncs * Render read messages with blue double checks * Send read receipts to senders of incoming messages // FREEBIE * Move ReadSyncs to their own file // FREEBIE * Fixup old comments on read receipts (now read syncs) And some variable renaming for extra clarity. // FREEBIE * Add global setting for read receipts Don't send read receipt messages unless the setting is enabled. Don't process read receipts if the setting is disabled. // FREEBIE * Sync read receipt setting from mobile Toggling this setting on your mobile device should sync it to Desktop. When linking, use the setting in the provisioning message. // FREEBIE * Send receipt messages silently Avoid generating phantom messages on ios // FREEBIE * Save recipients on the outgoing message models For accurate tracking and display of sent/delivered/read state, even if group membership changes later. // FREEBIE * Fix conversation type in profile key update handling // FREEBIE * Set recipients on synced sent messages * Render saved recipients in message detail if available For older messages, where we did not save the intended set of recipients at the time of sending, fall back to the current group membership. // FREEBIE * Record who has been successfully sent to // FREEBIE * Record who a message has been delivered to * Invert the not-clickable class * Fix readReceipt setting sync when linking * Render per recipient sent/delivered/read status In the message detail view for outgoing messages, render each recipient's individual sent/delivered/read status with respect to this message, as long as there are no errors associated with the recipient (ie, safety number changes, user not registered, etc...) since the error icon is displayed in that case. *Messages sent before this change may not have per-recipient status lists and will simply show no status icon. // FREEBIE * Add configuration sync request Send these requests in a one-off fashion when: 1. We have just setup from a chrome app import 2. We have just upgraded to read-receipt support // FREEBIE * Expose sendRequestConfigurationSyncMessage // FREEBIE * Fix handling of incoming delivery receipts - union with array FREEBIE
2017-10-04 22:28:43 +00:00
2018-09-21 01:47:19 +00:00
const unreadCount = unreadMessages.length - read.length;
this.set({ unreadCount });
window.Signal.Data.updateConversation(this.attributes);
2018-09-21 01:47:19 +00:00
// If a message has errors, we don't want to send anything out about it.
// read syncs - let's wait for a client that really understands the message
// to mark it read. we'll mark our local error read locally, though.
// read receipts - here we can run into infinite loops, where each time the
// conversation is viewed, another error message shows up for the contact
read = read.filter(item => !item.hasErrors);
2018-09-21 01:47:19 +00:00
if (read.length && options.sendReadReceipts) {
2020-05-27 21:37:06 +00:00
window.log.info(`Sending ${read.length} read syncs`);
// Because syncReadMessages sends to our other devices, and sendReadReceipts goes
// to a contact, we need accessKeys for both.
const {
sendOptions,
} = ConversationController.prepareForSend(
ConversationController.getOurConversationId(),
2018-11-07 19:20:43 +00:00
{ syncMessage: true }
);
await this.wrapSend(
textsecure.messaging.syncReadMessages(read, sendOptions)
);
2020-05-27 21:37:06 +00:00
await this.sendReadReceiptsFor(read);
}
},
async sendReadReceiptsFor(items) {
// Only send read receipts for accepted conversations
if (storage.get('read-receipt-setting') && this.getAccepted()) {
window.log.info(`Sending ${items.length} read receipts`);
const convoSendOptions = this.getSendOptions();
const receiptsBySender = _.groupBy(items, 'senderId');
2020-05-27 21:37:06 +00:00
await Promise.all(
_.map(receiptsBySender, async (receipts, senderId) => {
2020-05-27 21:37:06 +00:00
const timestamps = _.map(receipts, 'timestamp');
const c = ConversationController.get(senderId);
2020-05-27 21:37:06 +00:00
await this.wrapSend(
textsecure.messaging.sendReadReceipts(
c.get('e164'),
c.get('uuid'),
timestamps,
convoSendOptions
)
);
})
);
2018-09-21 01:47:19 +00:00
}
},
2020-08-05 01:13:19 +00:00
// This is an expensive operation we use to populate the message request hero row. It
// shows groups the current user has in common with this potential new contact.
async updateSharedGroups() {
if (!this.isPrivate()) {
return;
}
if (this.isMe()) {
return;
}
const ourGroups = await ConversationController.getAllGroupsInvolvingId(
ConversationController.getOurConversationId()
);
const theirGroups = await ConversationController.getAllGroupsInvolvingId(
this.id
);
const sharedGroups = _.intersection(ourGroups, theirGroups);
const sharedGroupNames = sharedGroups.map(conversation =>
conversation.getTitle()
);
this.set({ sharedGroupNames });
},
onChangeProfileKey() {
if (this.isPrivate()) {
this.getProfiles();
}
Profiles (#1453) * Add AES-GCM encryption for profiles With tests. * Add profileKey to DataMessage protobuf // FREEBIE * Decrypt and save profile names // FREEBIE * Save incoming profile keys * Move pad/unpad to crypto module // FREEBIE * Support fetching avatars from the cdn // FREEBIE * Translate failed authentication errors When AES-GCM authentication fails, webcrypto returns a very generic error. The same error is thrown for invalid length inputs, but our earlier checks in decryptProfile should rule out those failure modes and leave us safe to assume that we either had bad ciphertext or the wrong key. // FREEBIE * Handle profile avatars (wip) and log decrypt errors // FREEBIE * Display profile avatars Synced contact avatars will still override profile avatars. * Display profile names in convo list Only if we don't have a synced contact name. // FREEBIE * Make cdn url an environment config Use different ones for staging and production // FREEBIE * Display profile name in conversation header * Display profile name in group messages * Update conversation header if profile avatar changes // FREEBIE * Style profile names small with ~ * Save profileKeys from contact sync messages // FREEBIE * Save profile keys from provisioning messages For standalone accounts, generate a random profile key. // FREEBIE * Special case for one-time sync of our profile key Android will use a contact sync message to sync a profile key from Android clients who have just upgraded and generated their profile key. Normally we should receive this data in a provisioning message. // FREEBIE * Infer profile sharing from synced data messages * Populate profile keys on outgoing messages Requires that `profileSharing` be set on the conversation. // FREEBIE * Support for the profile key update flag When receiving a message with this flag, don't init a message record, just process the profile key and move on. // FREEBIE * Display profile names in group member list * Refresh contact's profile on profile key changes // FREEBIE * Catch errors on profile save // FREEBIE * Save our own synced contact info Don't return early if we get a contact sync for our own number // FREEBIE
2017-09-11 16:50:35 +00:00
},
getProfiles() {
// request all conversation members' keys
2020-09-09 02:25:05 +00:00
const conversations = this.getMembers();
return Promise.all(
_.map(conversations, conversation => {
this.getProfile(conversation.get('uuid'), conversation.get('e164'));
})
);
},
async getProfile(providedUuid, providedE164) {
if (!textsecure.messaging) {
2018-09-21 01:47:19 +00:00
throw new Error(
'Conversation.getProfile: textsecure.messaging not available'
);
}
Profiles (#1453) * Add AES-GCM encryption for profiles With tests. * Add profileKey to DataMessage protobuf // FREEBIE * Decrypt and save profile names // FREEBIE * Save incoming profile keys * Move pad/unpad to crypto module // FREEBIE * Support fetching avatars from the cdn // FREEBIE * Translate failed authentication errors When AES-GCM authentication fails, webcrypto returns a very generic error. The same error is thrown for invalid length inputs, but our earlier checks in decryptProfile should rule out those failure modes and leave us safe to assume that we either had bad ciphertext or the wrong key. // FREEBIE * Handle profile avatars (wip) and log decrypt errors // FREEBIE * Display profile avatars Synced contact avatars will still override profile avatars. * Display profile names in convo list Only if we don't have a synced contact name. // FREEBIE * Make cdn url an environment config Use different ones for staging and production // FREEBIE * Display profile name in conversation header * Display profile name in group messages * Update conversation header if profile avatar changes // FREEBIE * Style profile names small with ~ * Save profileKeys from contact sync messages // FREEBIE * Save profile keys from provisioning messages For standalone accounts, generate a random profile key. // FREEBIE * Special case for one-time sync of our profile key Android will use a contact sync message to sync a profile key from Android clients who have just upgraded and generated their profile key. Normally we should receive this data in a provisioning message. // FREEBIE * Infer profile sharing from synced data messages * Populate profile keys on outgoing messages Requires that `profileSharing` be set on the conversation. // FREEBIE * Support for the profile key update flag When receiving a message with this flag, don't init a message record, just process the profile key and move on. // FREEBIE * Display profile names in group member list * Refresh contact's profile on profile key changes // FREEBIE * Catch errors on profile save // FREEBIE * Save our own synced contact info Don't return early if we get a contact sync for our own number // FREEBIE
2017-09-11 16:50:35 +00:00
const id = ConversationController.ensureContactIds({
uuid: providedUuid,
e164: providedE164,
});
const c = ConversationController.get(id);
const {
generateProfileKeyCredentialRequest,
getClientZkProfileOperations,
handleProfileKeyCredential,
} = Util.zkgroup;
const clientZkProfileCipher = getClientZkProfileOperations(
window.getServerPublicParams()
);
let profile;
try {
await Promise.all([
c.deriveAccessKeyIfNeeded(),
c.deriveProfileKeyVersionIfNeeded(),
]);
const profileKey = c.get('profileKey');
const uuid = c.get('uuid');
const identifier = c.getSendTarget();
2020-06-24 00:38:32 +00:00
const profileKeyVersionHex = c.get('profileKeyVersion');
const existingProfileKeyCredential = c.get('profileKeyCredential');
const weHaveVersion = Boolean(
profileKey && uuid && profileKeyVersionHex
);
let profileKeyCredentialRequestHex;
let profileCredentialRequestContext;
if (weHaveVersion && !existingProfileKeyCredential) {
window.log.info('Generating request...');
({
requestHex: profileKeyCredentialRequestHex,
context: profileCredentialRequestContext,
} = generateProfileKeyCredentialRequest(
clientZkProfileCipher,
uuid,
profileKey
));
}
const sendMetadata = c.getSendMetadata({ disableMeCheck: true }) || {};
const getInfo =
sendMetadata[c.get('uuid')] || sendMetadata[c.get('e164')] || {};
2018-10-31 23:58:14 +00:00
if (getInfo.accessKey) {
try {
profile = await textsecure.messaging.getProfile(identifier, {
2018-10-31 23:58:14 +00:00
accessKey: getInfo.accessKey,
profileKeyVersion: profileKeyVersionHex,
profileKeyCredentialRequest: profileKeyCredentialRequestHex,
});
} catch (error) {
if (error.code === 401 || error.code === 403) {
window.log.info(
`Setting sealedSender to DISABLED for conversation ${c.idForLogging()}`
);
2018-10-31 23:58:14 +00:00
c.set({ sealedSender: SEALED_SENDER.DISABLED });
profile = await textsecure.messaging.getProfile(identifier, {
profileKeyVersion: profileKeyVersionHex,
profileKeyCredentialRequest: profileKeyCredentialRequestHex,
});
} else {
throw error;
}
}
} else {
profile = await textsecure.messaging.getProfile(identifier, {
profileKeyVersion: profileKeyVersionHex,
profileKeyCredentialRequest: profileKeyCredentialRequestHex,
});
}
Profiles (#1453) * Add AES-GCM encryption for profiles With tests. * Add profileKey to DataMessage protobuf // FREEBIE * Decrypt and save profile names // FREEBIE * Save incoming profile keys * Move pad/unpad to crypto module // FREEBIE * Support fetching avatars from the cdn // FREEBIE * Translate failed authentication errors When AES-GCM authentication fails, webcrypto returns a very generic error. The same error is thrown for invalid length inputs, but our earlier checks in decryptProfile should rule out those failure modes and leave us safe to assume that we either had bad ciphertext or the wrong key. // FREEBIE * Handle profile avatars (wip) and log decrypt errors // FREEBIE * Display profile avatars Synced contact avatars will still override profile avatars. * Display profile names in convo list Only if we don't have a synced contact name. // FREEBIE * Make cdn url an environment config Use different ones for staging and production // FREEBIE * Display profile name in conversation header * Display profile name in group messages * Update conversation header if profile avatar changes // FREEBIE * Style profile names small with ~ * Save profileKeys from contact sync messages // FREEBIE * Save profile keys from provisioning messages For standalone accounts, generate a random profile key. // FREEBIE * Special case for one-time sync of our profile key Android will use a contact sync message to sync a profile key from Android clients who have just upgraded and generated their profile key. Normally we should receive this data in a provisioning message. // FREEBIE * Infer profile sharing from synced data messages * Populate profile keys on outgoing messages Requires that `profileSharing` be set on the conversation. // FREEBIE * Support for the profile key update flag When receiving a message with this flag, don't init a message record, just process the profile key and move on. // FREEBIE * Display profile names in group member list * Refresh contact's profile on profile key changes // FREEBIE * Catch errors on profile save // FREEBIE * Save our own synced contact info Don't return early if we get a contact sync for our own number // FREEBIE
2017-09-11 16:50:35 +00:00
const identityKey = base64ToArrayBuffer(profile.identityKey);
2018-09-21 01:47:19 +00:00
const changed = await textsecure.storage.protocol.saveIdentity(
`${identifier}.1`,
2018-09-21 01:47:19 +00:00
identityKey,
false
);
if (changed) {
// save identity will close all sessions except for .1, so we
// must close that one manually.
const address = new libsignal.SignalProtocolAddress(identifier, 1);
2018-09-21 01:47:19 +00:00
window.log.info('closing session for', address.toString());
const sessionCipher = new libsignal.SessionCipher(
textsecure.storage.protocol,
address
);
await sessionCipher.closeOpenSessionForDevice();
}
2018-04-27 21:25:04 +00:00
2018-10-31 23:58:14 +00:00
const accessKey = c.get('accessKey');
if (
profile.unrestrictedUnidentifiedAccess &&
profile.unidentifiedAccess
) {
window.log.info(
`Setting sealedSender to UNRESTRICTED for conversation ${c.idForLogging()}`
);
c.set({
2018-10-31 23:58:14 +00:00
sealedSender: SEALED_SENDER.UNRESTRICTED,
});
} else if (accessKey && profile.unidentifiedAccess) {
const haveCorrectKey = await verifyAccessKey(
base64ToArrayBuffer(accessKey),
base64ToArrayBuffer(profile.unidentifiedAccess)
);
2018-09-21 01:47:19 +00:00
2018-10-31 23:58:14 +00:00
if (haveCorrectKey) {
window.log.info(
`Setting sealedSender to ENABLED for conversation ${c.idForLogging()}`
);
2018-10-31 23:58:14 +00:00
c.set({
sealedSender: SEALED_SENDER.ENABLED,
});
} else {
window.log.info(
`Setting sealedSender to DISABLED for conversation ${c.idForLogging()}`
);
2018-10-31 23:58:14 +00:00
c.set({
sealedSender: SEALED_SENDER.DISABLED,
});
}
} else {
window.log.info(
`Setting sealedSender to DISABLED for conversation ${c.idForLogging()}`
);
c.set({
2018-10-31 23:58:14 +00:00
sealedSender: SEALED_SENDER.DISABLED,
});
2018-09-21 01:47:19 +00:00
}
if (profile.capabilities) {
c.set({ capabilities: profile.capabilities });
}
if (profileCredentialRequestContext && profile.credential) {
const profileKeyCredential = handleProfileKeyCredential(
clientZkProfileCipher,
profileCredentialRequestContext,
profile.credential
);
c.set({ profileKeyCredential });
}
2018-09-21 01:47:19 +00:00
} catch (error) {
if (error.code !== 403 && error.code !== 404) {
window.log.warn(
'getProfile failure:',
c.idForLogging(),
error && error.stack ? error.stack : error
);
} else {
await c.dropProfileKey();
}
return;
}
try {
2020-09-09 00:56:23 +00:00
await c.setEncryptedProfileName(profile.name);
} catch (error) {
window.log.warn(
'getProfile decryption failure:',
c.idForLogging(),
error && error.stack ? error.stack : error
);
await c.dropProfileKey();
}
try {
await c.setProfileAvatar(profile.avatar);
} catch (error) {
if (error.code === 403 || error.code === 404) {
window.log.info(
`Clearing profile avatar for conversation ${c.idForLogging()}`
);
c.set({
profileAvatar: null,
});
}
2018-09-21 01:47:19 +00:00
}
2020-08-05 01:13:19 +00:00
window.Signal.Data.updateConversation(c.attributes);
2018-09-21 01:47:19 +00:00
},
2020-09-09 00:56:23 +00:00
async setEncryptedProfileName(encryptedName) {
if (!encryptedName) {
return;
}
2018-09-21 01:47:19 +00:00
const key = this.get('profileKey');
if (!key) {
return;
}
2018-09-21 01:47:19 +00:00
// decode
const keyBuffer = base64ToArrayBuffer(key);
const data = base64ToArrayBuffer(encryptedName);
2018-09-21 01:47:19 +00:00
// decrypt
const { given, family } = await textsecure.crypto.decryptProfileName(
2018-09-21 01:47:19 +00:00
data,
keyBuffer
);
// encode
const profileName = given ? stringFromBytes(given) : null;
const profileFamilyName = family ? stringFromBytes(family) : null;
2020-08-05 01:13:19 +00:00
// set then check for changes
const oldName = this.getProfileName();
const hadPreviousName = Boolean(oldName);
2020-08-05 01:13:19 +00:00
this.set({ profileName, profileFamilyName });
const newName = this.getProfileName();
// Note that we compare the combined names to ensure that we don't present the exact
// same before/after string, even if someone is moving from just first name to
// first/last name in their profile data.
const nameChanged = oldName !== newName;
if (!this.isMe() && hadPreviousName && nameChanged) {
const change = {
type: 'name',
oldName,
newName,
};
2020-08-05 01:13:19 +00:00
await this.addProfileChange(change);
}
Profiles (#1453) * Add AES-GCM encryption for profiles With tests. * Add profileKey to DataMessage protobuf // FREEBIE * Decrypt and save profile names // FREEBIE * Save incoming profile keys * Move pad/unpad to crypto module // FREEBIE * Support fetching avatars from the cdn // FREEBIE * Translate failed authentication errors When AES-GCM authentication fails, webcrypto returns a very generic error. The same error is thrown for invalid length inputs, but our earlier checks in decryptProfile should rule out those failure modes and leave us safe to assume that we either had bad ciphertext or the wrong key. // FREEBIE * Handle profile avatars (wip) and log decrypt errors // FREEBIE * Display profile avatars Synced contact avatars will still override profile avatars. * Display profile names in convo list Only if we don't have a synced contact name. // FREEBIE * Make cdn url an environment config Use different ones for staging and production // FREEBIE * Display profile name in conversation header * Display profile name in group messages * Update conversation header if profile avatar changes // FREEBIE * Style profile names small with ~ * Save profileKeys from contact sync messages // FREEBIE * Save profile keys from provisioning messages For standalone accounts, generate a random profile key. // FREEBIE * Special case for one-time sync of our profile key Android will use a contact sync message to sync a profile key from Android clients who have just upgraded and generated their profile key. Normally we should receive this data in a provisioning message. // FREEBIE * Infer profile sharing from synced data messages * Populate profile keys on outgoing messages Requires that `profileSharing` be set on the conversation. // FREEBIE * Support for the profile key update flag When receiving a message with this flag, don't init a message record, just process the profile key and move on. // FREEBIE * Display profile names in group member list * Refresh contact's profile on profile key changes // FREEBIE * Catch errors on profile save // FREEBIE * Save our own synced contact info Don't return early if we get a contact sync for our own number // FREEBIE
2017-09-11 16:50:35 +00:00
},
2018-09-21 01:47:19 +00:00
async setProfileAvatar(avatarPath) {
if (!avatarPath) {
2018-09-21 01:47:19 +00:00
return;
}
2020-09-09 00:56:23 +00:00
if (this.isMe()) {
window.storage.put('avatarUrl', avatarPath);
}
2018-09-21 01:47:19 +00:00
const avatar = await textsecure.messaging.getAvatar(avatarPath);
const key = this.get('profileKey');
if (!key) {
return;
}
const keyBuffer = base64ToArrayBuffer(key);
2018-09-21 01:47:19 +00:00
// decrypt
const decrypted = await textsecure.crypto.decryptProfile(
avatar,
keyBuffer
);
// update the conversation avatar only if hash differs
if (decrypted) {
const newAttributes = await window.Signal.Types.Conversation.maybeUpdateProfileAvatar(
this.attributes,
decrypted,
{
writeNewAttachmentData,
deleteAttachmentData,
doesAttachmentExist,
2018-09-21 01:47:19 +00:00
}
);
this.set(newAttributes);
}
Profiles (#1453) * Add AES-GCM encryption for profiles With tests. * Add profileKey to DataMessage protobuf // FREEBIE * Decrypt and save profile names // FREEBIE * Save incoming profile keys * Move pad/unpad to crypto module // FREEBIE * Support fetching avatars from the cdn // FREEBIE * Translate failed authentication errors When AES-GCM authentication fails, webcrypto returns a very generic error. The same error is thrown for invalid length inputs, but our earlier checks in decryptProfile should rule out those failure modes and leave us safe to assume that we either had bad ciphertext or the wrong key. // FREEBIE * Handle profile avatars (wip) and log decrypt errors // FREEBIE * Display profile avatars Synced contact avatars will still override profile avatars. * Display profile names in convo list Only if we don't have a synced contact name. // FREEBIE * Make cdn url an environment config Use different ones for staging and production // FREEBIE * Display profile name in conversation header * Display profile name in group messages * Update conversation header if profile avatar changes // FREEBIE * Style profile names small with ~ * Save profileKeys from contact sync messages // FREEBIE * Save profile keys from provisioning messages For standalone accounts, generate a random profile key. // FREEBIE * Special case for one-time sync of our profile key Android will use a contact sync message to sync a profile key from Android clients who have just upgraded and generated their profile key. Normally we should receive this data in a provisioning message. // FREEBIE * Infer profile sharing from synced data messages * Populate profile keys on outgoing messages Requires that `profileSharing` be set on the conversation. // FREEBIE * Support for the profile key update flag When receiving a message with this flag, don't init a message record, just process the profile key and move on. // FREEBIE * Display profile names in group member list * Refresh contact's profile on profile key changes // FREEBIE * Catch errors on profile save // FREEBIE * Save our own synced contact info Don't return early if we get a contact sync for our own number // FREEBIE
2017-09-11 16:50:35 +00:00
},
2020-09-09 00:56:23 +00:00
async setProfileKey(profileKey, { viaStorageServiceSync = false } = {}) {
// profileKey is a string so we can compare it directly
2018-09-21 01:47:19 +00:00
if (this.get('profileKey') !== profileKey) {
window.log.info(
`Setting sealedSender to UNKNOWN for conversation ${this.idForLogging()}`
);
2018-10-31 23:58:14 +00:00
this.set({
profileKey,
profileKeyVersion: null,
profileKeyCredential: null,
2018-10-31 23:58:14 +00:00
accessKey: null,
sealedSender: SEALED_SENDER.UNKNOWN,
});
2020-09-09 00:56:23 +00:00
if (!viaStorageServiceSync) {
this.captureChange();
}
await Promise.all([
this.deriveAccessKeyIfNeeded(),
this.deriveProfileKeyVersionIfNeeded(),
]);
window.Signal.Data.updateConversation(this.attributes, {
2018-09-21 01:47:19 +00:00
Conversation: Whisper.Conversation,
});
}
Profiles (#1453) * Add AES-GCM encryption for profiles With tests. * Add profileKey to DataMessage protobuf // FREEBIE * Decrypt and save profile names // FREEBIE * Save incoming profile keys * Move pad/unpad to crypto module // FREEBIE * Support fetching avatars from the cdn // FREEBIE * Translate failed authentication errors When AES-GCM authentication fails, webcrypto returns a very generic error. The same error is thrown for invalid length inputs, but our earlier checks in decryptProfile should rule out those failure modes and leave us safe to assume that we either had bad ciphertext or the wrong key. // FREEBIE * Handle profile avatars (wip) and log decrypt errors // FREEBIE * Display profile avatars Synced contact avatars will still override profile avatars. * Display profile names in convo list Only if we don't have a synced contact name. // FREEBIE * Make cdn url an environment config Use different ones for staging and production // FREEBIE * Display profile name in conversation header * Display profile name in group messages * Update conversation header if profile avatar changes // FREEBIE * Style profile names small with ~ * Save profileKeys from contact sync messages // FREEBIE * Save profile keys from provisioning messages For standalone accounts, generate a random profile key. // FREEBIE * Special case for one-time sync of our profile key Android will use a contact sync message to sync a profile key from Android clients who have just upgraded and generated their profile key. Normally we should receive this data in a provisioning message. // FREEBIE * Infer profile sharing from synced data messages * Populate profile keys on outgoing messages Requires that `profileSharing` be set on the conversation. // FREEBIE * Support for the profile key update flag When receiving a message with this flag, don't init a message record, just process the profile key and move on. // FREEBIE * Display profile names in group member list * Refresh contact's profile on profile key changes // FREEBIE * Catch errors on profile save // FREEBIE * Save our own synced contact info Don't return early if we get a contact sync for our own number // FREEBIE
2017-09-11 16:50:35 +00:00
},
async dropProfileKey() {
if (this.get('profileKey')) {
window.log.info(
`Dropping profileKey, setting sealedSender to UNKNOWN for conversation ${this.idForLogging()}`
);
const profileAvatar = this.get('profileAvatar');
if (profileAvatar && profileAvatar.path) {
await deleteAttachmentData(profileAvatar.path);
}
this.set({
profileKey: null,
profileKeyVersion: null,
profileKeyCredential: null,
accessKey: null,
profileName: null,
profileFamilyName: null,
profileAvatar: null,
sealedSender: SEALED_SENDER.UNKNOWN,
});
window.Signal.Data.updateConversation(this.attributes);
}
},
2018-10-31 23:58:14 +00:00
async deriveAccessKeyIfNeeded() {
const profileKey = this.get('profileKey');
if (!profileKey) {
return;
}
if (this.get('accessKey')) {
return;
}
const profileKeyBuffer = base64ToArrayBuffer(profileKey);
const accessKeyBuffer = await deriveAccessKey(profileKeyBuffer);
const accessKey = arrayBufferToBase64(accessKeyBuffer);
2018-10-31 23:58:14 +00:00
this.set({ accessKey });
},
async deriveProfileKeyVersionIfNeeded() {
const profileKey = this.get('profileKey');
if (!profileKey) {
return;
}
const uuid = this.get('uuid');
if (!uuid || this.get('profileKeyVersion')) {
return;
}
const profileKeyVersion = Util.zkgroup.deriveProfileKeyVersion(
profileKey,
uuid
);
this.set({ profileKeyVersion });
},
2018-10-31 23:58:14 +00:00
hasMember(identifier) {
2020-09-09 02:25:05 +00:00
const id = ConversationController.getConversationId(identifier);
const memberIds = this.getMemberIds();
return _.contains(memberIds, id);
},
fetchContacts() {
if (this.isPrivate()) {
this.contactCollection.reset([this]);
}
2020-09-09 02:25:05 +00:00
const members = this.getMembers();
_.forEach(members, member => {
this.listenTo(member, 'change:verified', this.onMemberVerifiedChange);
});
2020-09-09 02:25:05 +00:00
this.contactCollection.reset(members);
},
async destroyMessages() {
this.messageCollection.reset([]);
2018-09-21 01:47:19 +00:00
this.set({
lastMessage: null,
timestamp: null,
active_at: null,
});
window.Signal.Data.updateConversation(this.attributes);
await window.Signal.Data.removeAllMessagesInConversation(this.id, {
MessageCollection: Whisper.MessageCollection,
});
},
getTitle() {
if (this.isPrivate()) {
2020-07-24 01:35:32 +00:00
return (
this.get('name') ||
this.getProfileName() ||
this.getNumber() ||
i18n('unknownContact')
);
}
return this.get('name') || i18n('unknownGroup');
2015-02-04 19:23:00 +00:00
},
getProfileName() {
2019-10-17 18:22:07 +00:00
if (this.isPrivate()) {
return Util.combineNames(
this.get('profileName'),
this.get('profileFamilyName')
);
}
return null;
Profiles (#1453) * Add AES-GCM encryption for profiles With tests. * Add profileKey to DataMessage protobuf // FREEBIE * Decrypt and save profile names // FREEBIE * Save incoming profile keys * Move pad/unpad to crypto module // FREEBIE * Support fetching avatars from the cdn // FREEBIE * Translate failed authentication errors When AES-GCM authentication fails, webcrypto returns a very generic error. The same error is thrown for invalid length inputs, but our earlier checks in decryptProfile should rule out those failure modes and leave us safe to assume that we either had bad ciphertext or the wrong key. // FREEBIE * Handle profile avatars (wip) and log decrypt errors // FREEBIE * Display profile avatars Synced contact avatars will still override profile avatars. * Display profile names in convo list Only if we don't have a synced contact name. // FREEBIE * Make cdn url an environment config Use different ones for staging and production // FREEBIE * Display profile name in conversation header * Display profile name in group messages * Update conversation header if profile avatar changes // FREEBIE * Style profile names small with ~ * Save profileKeys from contact sync messages // FREEBIE * Save profile keys from provisioning messages For standalone accounts, generate a random profile key. // FREEBIE * Special case for one-time sync of our profile key Android will use a contact sync message to sync a profile key from Android clients who have just upgraded and generated their profile key. Normally we should receive this data in a provisioning message. // FREEBIE * Infer profile sharing from synced data messages * Populate profile keys on outgoing messages Requires that `profileSharing` be set on the conversation. // FREEBIE * Support for the profile key update flag When receiving a message with this flag, don't init a message record, just process the profile key and move on. // FREEBIE * Display profile names in group member list * Refresh contact's profile on profile key changes // FREEBIE * Catch errors on profile save // FREEBIE * Save our own synced contact info Don't return early if we get a contact sync for our own number // FREEBIE
2017-09-11 16:50:35 +00:00
},
getNumber() {
if (!this.isPrivate()) {
return '';
}
const number = this.get('e164');
try {
const parsedNumber = libphonenumber.parse(number);
const regionCode = libphonenumber.getRegionCodeForNumber(parsedNumber);
if (regionCode === storage.get('regionCode')) {
return libphonenumber.format(
parsedNumber,
libphonenumber.PhoneNumberFormat.NATIONAL
);
}
return libphonenumber.format(
parsedNumber,
libphonenumber.PhoneNumberFormat.INTERNATIONAL
);
} catch (e) {
return number;
}
},
getInitials(name) {
if (!name) {
return null;
}
const cleaned = name.replace(/[^A-Za-z\s]+/g, '').replace(/\s+/g, ' ');
const parts = cleaned.split(' ');
const initials = parts.map(part => part.trim()[0]);
if (!initials.length) {
return null;
}
return initials.slice(0, 2).join('');
},
isPrivate() {
return this.get('type') === 'private';
},
getColor() {
if (!this.isPrivate()) {
return 'signal-blue';
}
const { migrateColor } = Util;
return migrateColor(this.get('color'));
},
getAvatarPath() {
const avatar = this.isMe()
? this.get('profileAvatar') || this.get('avatar')
: this.get('avatar') || this.get('profileAvatar');
if (avatar && avatar.path) {
return getAbsoluteAttachmentPath(avatar.path);
}
return null;
},
2020-09-09 02:25:05 +00:00
canChangeTimer() {
if (this.isPrivate()) {
return true;
}
if (this.get('groupVersion') !== 2) {
return true;
}
const accessControlEnum =
textsecure.protobuf.AccessControl.AccessRequired;
const accessControl = this.get('accessControl');
const canAnyoneChangeTimer =
accessControl &&
(accessControl.attributes === accessControlEnum.ANY ||
accessControl.attributes === accessControlEnum.MEMBER);
if (canAnyoneChangeTimer) {
return true;
}
const memberEnum = textsecure.protobuf.Member.Role;
const members = this.get('membersV2') || [];
const myId = ConversationController.getConversationId(
textsecure.storage.user.getUuid() || textsecure.storage.user.getNumber()
);
const me = members.find(item => item.conversationId === myId);
if (!me) {
return false;
}
const isAdministrator = me.role === memberEnum.ADMINISTRATOR;
if (isAdministrator) {
return true;
}
return false;
},
2020-09-09 00:56:23 +00:00
// Set of items to captureChanges on:
// [-] uuid
// [-] e164
// [X] profileKey
// [-] identityKey
// [X] verified!
// [-] profileName
// [-] profileFamilyName
// [X] blocked
// [X] whitelisted
// [X] archived
captureChange() {
if (!window.Signal.RemoteConfig.isEnabled('desktop.storageWrite')) {
window.log.info(
'conversation.captureChange: Returning early; desktop.storageWrite is falsey'
);
return;
}
2020-09-09 00:56:23 +00:00
this.set({ needsStorageServiceSync: true });
this.queueJob(() => {
Services.storageServiceUploadJob();
});
},
2020-01-17 22:23:19 +00:00
async notify(message, reaction) {
2020-08-27 19:45:08 +00:00
if (this.get('muteExpiresAt') && Date.now() < this.get('muteExpiresAt')) {
return;
}
2020-01-17 22:23:19 +00:00
if (!message.isIncoming() && !reaction) {
return;
}
2020-01-17 22:23:19 +00:00
const conversationId = this.id;
const sender = reaction
? ConversationController.get(reaction.get('fromId'))
: message.getContact();
const senderName = sender ? sender.getTitle() : i18n('unknownContact');
const senderTitle = this.isPrivate()
? senderName
: i18n('notificationSenderInGroup', {
sender: senderName,
group: this.getTitle(),
});
2020-01-17 22:23:19 +00:00
let notificationIconUrl;
const avatar = this.get('avatar') || this.get('profileAvatar');
if (avatar && avatar.path) {
notificationIconUrl = getAbsoluteAttachmentPath(avatar.path);
} else if (this.isPrivate()) {
notificationIconUrl = await new Whisper.IdenticonSVGView({
color: this.getColor(),
content: this.getInitials(this.get('name')) || '#',
}).getDataUrl();
} else {
// Not technically needed, but helps us be explicit: we don't show an icon for a
// group that doesn't have an icon.
notificationIconUrl = undefined;
}
2020-01-17 22:23:19 +00:00
const messageJSON = message.toJSON();
const messageId = message.id;
const isExpiringMessage = Message.hasExpiration(messageJSON);
Whisper.Notifications.add({
senderTitle,
2020-01-17 22:23:19 +00:00
conversationId,
notificationIconUrl,
2020-01-17 22:23:19 +00:00
isExpiringMessage,
message: message.getNotificationText(),
messageId,
reaction: reaction ? reaction.toJSON() : null,
});
2015-09-22 22:52:33 +00:00
},
2018-11-14 19:10:32 +00:00
notifyTyping(options = {}) {
const { isTyping, senderId, isMe, senderDevice } = options;
2018-11-14 19:10:32 +00:00
// We don't do anything with typing messages from our other devices
2020-06-12 22:36:32 +00:00
if (isMe) {
2018-11-14 19:10:32 +00:00
return;
}
const typingToken = `${senderId}.${senderDevice}`;
2018-11-14 19:10:32 +00:00
this.contactTypingTimers = this.contactTypingTimers || {};
const record = this.contactTypingTimers[typingToken];
2018-11-14 19:10:32 +00:00
if (record) {
clearTimeout(record.timer);
}
if (isTyping) {
this.contactTypingTimers[typingToken] = this.contactTypingTimers[
typingToken
2018-11-14 19:10:32 +00:00
] || {
timestamp: Date.now(),
2020-06-12 22:36:32 +00:00
senderId,
2018-11-14 19:10:32 +00:00
senderDevice,
};
this.contactTypingTimers[typingToken].timer = setTimeout(
this.clearContactTypingTimer.bind(this, typingToken),
2018-11-14 19:10:32 +00:00
15 * 1000
);
if (!record) {
// User was not previously typing before. State change!
2019-01-14 21:49:58 +00:00
this.trigger('change', this);
2018-11-14 19:10:32 +00:00
}
} else {
delete this.contactTypingTimers[typingToken];
2018-11-14 19:10:32 +00:00
if (record) {
// User was previously typing, and is no longer. State change!
2019-01-14 21:49:58 +00:00
this.trigger('change', this);
2018-11-14 19:10:32 +00:00
}
}
},
clearContactTypingTimer(typingToken) {
2018-11-14 19:10:32 +00:00
this.contactTypingTimers = this.contactTypingTimers || {};
const record = this.contactTypingTimers[typingToken];
2018-11-14 19:10:32 +00:00
if (record) {
clearTimeout(record.timer);
delete this.contactTypingTimers[typingToken];
2018-11-14 19:10:32 +00:00
// User was previously typing, but timed out or we received message. State change!
2019-01-14 21:49:58 +00:00
this.trigger('change', this);
2018-11-14 19:10:32 +00:00
}
},
});
Whisper.ConversationCollection = Backbone.Collection.extend({
2015-02-08 02:18:53 +00:00
model: Whisper.Conversation,
/**
* Backbone defines a `_byId` field. Here we set up additional `_byE164`,
* `_byUuid`, and `_byGroupId` fields so we can track conversations by more
* than just their id.
*/
initialize() {
this.eraseLookups();
this.on('idUpdated', (model, idProp, oldValue) => {
if (oldValue) {
if (idProp === 'e164') {
delete this._byE164[oldValue];
}
if (idProp === 'uuid') {
delete this._byUuid[oldValue];
}
if (idProp === 'groupId') {
delete this._byGroupid[oldValue];
}
}
if (model.get('e164')) {
this._byE164[model.get('e164')] = model;
}
if (model.get('uuid')) {
this._byUuid[model.get('uuid')] = model;
}
if (model.get('groupId')) {
this._byGroupid[model.get('groupId')] = model;
}
});
},
reset(...args) {
Backbone.Collection.prototype.reset.apply(this, args);
this.resetLookups();
},
resetLookups() {
this.eraseLookups();
this.generateLookups(this.models);
},
generateLookups(models) {
models.forEach(model => {
const e164 = model.get('e164');
if (e164) {
const existing = this._byE164[e164];
// Prefer the contact with both e164 and uuid
if (!existing || (existing && !existing.get('uuid'))) {
this._byE164[e164] = model;
}
}
const uuid = model.get('uuid');
if (uuid) {
const existing = this._byUuid[uuid];
// Prefer the contact with both e164 and uuid
if (!existing || (existing && !existing.get('e164'))) {
this._byUuid[uuid] = model;
}
}
const groupId = model.get('groupId');
if (groupId) {
this._byGroupId[groupId] = model;
}
});
},
eraseLookups() {
this._byE164 = Object.create(null);
this._byUuid = Object.create(null);
this._byGroupId = Object.create(null);
},
add(...models) {
const result = Backbone.Collection.prototype.add.apply(this, models);
this.generateLookups(Array.isArray(result) ? result.slice(0) : [result]);
return result;
},
/**
* Backbone collections have a `_byId` field that `get` defers to. Here, we
* override `get` to first access our custom `_byE164`, `_byUuid`, and
* `_byGroupId` functions, followed by falling back to the original
* Backbone implementation.
*/
get(id) {
return (
this._byE164[id] ||
this._byE164[`+${id}`] ||
this._byUuid[id] ||
this._byGroupId[id] ||
Backbone.Collection.prototype.get.call(this, id)
);
},
comparator(m) {
return -m.get('timestamp');
},
});
Whisper.Conversation.COLORS = COLORS.concat(['grey', 'default']).join(' ');
2020-09-09 02:25:05 +00:00
// This is a wrapper model used to display group members in the member list view, within
// the world of backbone, but layering another bit of group-specific data top of base
// conversation data.
Whisper.GroupMemberConversation = Backbone.Model.extend({
initialize(attributes) {
const { conversation, isAdmin } = attributes;
if (!conversation) {
throw new Error(
'GroupMemberConversation.initialze: conversation required!'
);
}
if (!_.isBoolean(isAdmin)) {
throw new Error('GroupMemberConversation.initialze: isAdmin required!');
}
// If our underlying conversation changes, we change too
this.listenTo(conversation, 'change', () => {
this.trigger('change', this);
});
this.conversation = conversation;
this.isAdmin = isAdmin;
},
format() {
return {
...this.conversation.format(),
isAdmin: this.isAdmin,
};
},
get(...params) {
return this.conversation.get(...params);
},
getTitle() {
return this.conversation.getTitle();
},
isMe() {
return this.conversation.isMe();
},
});
// We need a custom collection here to get the sorting we need
Whisper.GroupConversationCollection = Backbone.Collection.extend({
model: Whisper.GroupMemberConversation,
initialize() {
this.collator = new Intl.Collator();
},
comparator(left, right) {
if (left.isAdmin && !right.isAdmin) {
return -1;
}
if (!left.isAdmin && right.isAdmin) {
return 1;
}
const leftLower = left.getTitle().toLowerCase();
const rightLower = right.getTitle().toLowerCase();
return this.collator.compare(leftLower, rightLower);
},
});
2018-04-27 21:25:04 +00:00
})();