diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 192a8bd20018..20ed3859b609 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -285,6 +285,10 @@ } } }, + "messageHistoryUnsynced": { + "message": "For your security, conversation history isn't transferred to new linked devices.", + "description": "Shown in the conversation history when a user links a new device to explain what is not supported." + }, "youMarkedAsVerified": { "message": "You marked your Safety Number with $name$ as verified", "description": "Shown in the conversation history when the user marks a contact as verified.", @@ -1530,6 +1534,10 @@ "message": "Start new conversation…", "description": "Label underneath number a user enters that is not an existing contact" }, + "notSupportedSMS": { + "message": "SMS/MMS messages are not supported.", + "description": "Label underneath number informing user that SMS is not supported on desktop" + }, "newPhoneNumber": { "message": "Enter a phone number to add a contact.", "description": "Placeholder for adding a new number to a contact" diff --git a/images/icons/v2/info-outline-24.svg b/images/icons/v2/info-outline-24.svg new file mode 100644 index 000000000000..a241fc35e578 --- /dev/null +++ b/images/icons/v2/info-outline-24.svg @@ -0,0 +1 @@ +info-outline-24 \ No newline at end of file diff --git a/images/icons/v2/info-solid-24.svg b/images/icons/v2/info-solid-24.svg new file mode 100644 index 000000000000..88c8f9dd8d77 --- /dev/null +++ b/images/icons/v2/info-solid-24.svg @@ -0,0 +1 @@ +info-solid-24 \ No newline at end of file diff --git a/js/background.js b/js/background.js index fd308bf38ad6..ec3de96e79db 100644 --- a/js/background.js +++ b/js/background.js @@ -1975,6 +1975,7 @@ name: details.name, color: details.color, active_at: activeAt, + inbox_position: details.inboxPosition, }); // Update the conversation avatar only if new avatar exists and hash differs @@ -2030,6 +2031,14 @@ verifiedEvent.viaContactSync = true; await onVerified(verifiedEvent); } + + const { appView } = window.owsDesktopApp; + if (appView && appView.installView && appView.installView.didLink) { + window.log.info( + 'onContactReceived: Adding the message history disclaimer on link' + ); + await conversation.addMessageHistoryDisclaimer(); + } } catch (error) { window.log.error('onContactReceived error:', Errors.toLogFormat(error)); } @@ -2063,6 +2072,7 @@ members, color: details.color, type: 'group', + inbox_position: details.inboxPosition, }; if (details.active) { @@ -2103,6 +2113,13 @@ window.Signal.Data.updateConversation(id, conversation.attributes); + const { appView } = window.owsDesktopApp; + if (appView && appView.installView && appView.installView.didLink) { + window.log.info( + 'onGroupReceived: Adding the message history disclaimer on link' + ); + await conversation.addMessageHistoryDisclaimer(); + } const { expireTimer } = details; const isValidExpireTimer = typeof expireTimer === 'number'; if (!isValidExpireTimer) { diff --git a/js/models/conversations.js b/js/models/conversations.js index 7807d17b79ba..c5e13b030873 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -385,6 +385,7 @@ const draftText = this.get('draft'); const shouldShowDraft = this.hasDraft() && draftTimestamp && draftTimestamp >= timestamp; + const inboxPosition = this.get('inbox_position'); const result = { id: this.id, @@ -400,6 +401,7 @@ name: this.getName(), profileName: this.getProfileName(), timestamp, + inboxPosition, title: this.getTitle(), unreadCount: this.get('unreadCount') || 0, @@ -1662,6 +1664,38 @@ return message; }, + async addMessageHistoryDisclaimer() { + 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'); }, diff --git a/js/models/messages.js b/js/models/messages.js index 1de87e5b563e..57d6c59b674d 100644 --- a/js/models/messages.js +++ b/js/models/messages.js @@ -134,6 +134,7 @@ !this.isUnsupportedMessage() && !this.isExpirationTimerUpdate() && !this.isKeyChange() && + !this.isMessageHistoryUnsynced() && !this.isVerifiedChange() && !this.isGroupUpdate() && !this.isEndSession() @@ -147,6 +148,11 @@ type: 'unsupportedMessage', data: this.getPropsForUnsupportedMessage(), }; + } else if (this.isMessageHistoryUnsynced()) { + return { + type: 'linkNotification', + data: null, + }; } else if (this.isExpirationTimerUpdate()) { return { type: 'timerNotification', @@ -343,6 +349,9 @@ isVerifiedChange() { return this.get('type') === 'verified-change'; }, + isMessageHistoryUnsynced() { + return this.get('type') === 'message-history-unsynced'; + }, isGroupUpdate() { return !!this.get('group_update'); }, diff --git a/js/views/install_view.js b/js/views/install_view.js index 2cea5a740ca1..b6151f83fd32 100644 --- a/js/views/install_view.js +++ b/js/views/install_view.js @@ -32,6 +32,7 @@ initialize(options = {}) { window.readyForUpdates(); + this.didLink = false; this.selectStep(Steps.SCAN_QR_CODE); this.connect(); this.on('disconnected', this.reconnect); @@ -179,7 +180,10 @@ this.selectStep(Steps.PROGRESS_BAR); - const finish = () => resolve(name); + const finish = () => { + this.didLink = true; + return resolve(name); + }; // Delete all data from database unless we're in the middle // of a re-link, or we are finishing a light import. Without this, diff --git a/protos/SignalService.proto b/protos/SignalService.proto index fe93a90694c6..ef543d698524 100644 --- a/protos/SignalService.proto +++ b/protos/SignalService.proto @@ -377,15 +377,16 @@ message ContactDetails { optional uint32 length = 2; } - optional string number = 1; - optional string uuid = 9; - optional string name = 2; - optional Avatar avatar = 3; - optional string color = 4; - optional Verified verified = 5; - optional bytes profileKey = 6; - optional bool blocked = 7; - optional uint32 expireTimer = 8; + optional string number = 1; + optional string uuid = 9; + optional string name = 2; + optional Avatar avatar = 3; + optional string color = 4; + optional Verified verified = 5; + optional bytes profileKey = 6; + optional bool blocked = 7; + optional uint32 expireTimer = 8; + optional uint32 inboxPosition = 10; } message GroupDetails { @@ -399,13 +400,14 @@ message GroupDetails { optional string e164 = 2; } - optional bytes id = 1; - optional string name = 2; - repeated string membersE164 = 3; - repeated Member members = 9; - optional Avatar avatar = 4; - optional bool active = 5 [default = true]; - optional uint32 expireTimer = 6; - optional string color = 7; - optional bool blocked = 8; + optional bytes id = 1; + optional string name = 2; + repeated string membersE164 = 3; + repeated Member members = 9; + optional Avatar avatar = 4; + optional bool active = 5 [default = true]; + optional uint32 expireTimer = 6; + optional string color = 7; + optional bool blocked = 8; + optional uint32 inboxPosition = 10; } diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index e9a7d0e6cb64..7b3869d5d8ce 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -2335,6 +2335,36 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05', } } +.module-message-unsynced { + padding-bottom: 24px; + text-align: center; + + @include light-theme { + color: $color-gray-60; + } + @include dark-theme { + color: $color-gray-25; + } +} + +.module-message-unsynced__icon { + height: 24px; + margin-bottom: 4px; + margin-left: auto; + margin-right: auto; + width: 24px; + + @include light-theme { + @include color-svg( + '../images/icons/v2/info-outline-24.svg', + $color-gray-60 + ); + } + @include dark-theme { + @include color-svg('../images/icons/v2/info-solid-24.svg', $color-gray-25); + } +} + // Module: Verification Notification .module-verification-notification { @@ -4729,6 +4759,19 @@ button.module-image__border-overlay:focus { } } +.module-search-results__sms-not-supported { + font-size: 14px; + padding-top: 12px; + text-align: center; + + @include light-theme { + color: $color-gray-60; + } + @include dark-theme { + color: $color-gray-25; + } +} + .module-search-results__no-results { margin-top: 27px; padding-left: 1em; diff --git a/ts/components/SearchResults.md b/ts/components/SearchResults.md deleted file mode 100644 index da055bc55259..000000000000 --- a/ts/components/SearchResults.md +++ /dev/null @@ -1,983 +0,0 @@ -#### With all result types - -```jsx -const items = [ - { - type: 'conversations-header', - data: undefined, - }, - { - type: 'conversation', - data: { - name: 'Everyone πŸŒ†', - conversationType: 'group', - phoneNumber: '(202) 555-0011', - avatarPath: util.landscapeGreenObjectUrl, - lastUpdated: Date.now() - 5 * 60 * 1000, - lastMessage: { - text: 'The rabbit hopped silently in the night.', - status: 'sent', - }, - }, - }, - { - type: 'conversation', - data: { - name: 'Everyone Else πŸ”₯', - conversationType: 'direct', - phoneNumber: '(202) 555-0012', - avatarPath: util.landscapePurpleObjectUrl, - lastUpdated: Date.now() - 5 * 60 * 1000, - lastMessage: { - text: "What's going on?", - status: 'sent', - }, - }, - }, - { - type: 'contacts-header', - data: undefined, - }, - { - type: 'contact', - data: { - name: 'The one Everyone', - conversationType: 'direct', - phoneNumber: '(202) 555-0013', - avatarPath: util.gifObjectUrl, - }, - }, - { - type: 'contact', - data: { - name: 'No likey everyone', - conversationType: 'direct', - phoneNumber: '(202) 555-0014', - color: 'red', - }, - }, - { - type: 'messages-header', - data: undefined, - }, -]; - -const messages = [ - { - from: { - isMe: true, - avatarPath: util.gifObjectUrl, - }, - to: { - name: 'Mr. Fire πŸ”₯', - phoneNumber: '(202) 555-0015', - }, - id: '1-guid-guid-guid-guid-guid', - conversationId: '(202) 555-0015', - receivedAt: Date.now() - 5 * 60 * 1000, - snippet: '<>Everyone<>! Get in!', - conversationOpenInternal: () => console.log('onClick'), - }, - { - from: { - name: 'Jon ❄️', - phoneNumber: '(202) 555-0016', - color: 'green', - }, - to: { - isMe: true, - }, - id: '2-guid-guid-guid-guid-guid', - conversationId: '(202) 555-0016', - snippet: 'Why is <>everyone<> so frustrated?', - receivedAt: Date.now() - 20 * 60 * 1000, - conversationOpenInternal: () => console.log('onClick'), - }, - { - from: { - name: 'Someone', - phoneNumber: '(202) 555-0011', - color: 'green', - avatarPath: util.pngObjectUrl, - }, - to: { - name: "Y'all πŸŒ†", - }, - id: '3-guid-guid-guid-guid-guid', - conversationId: 'EveryoneGroupID', - snippet: 'Hello, <>everyone<>! Woohooo!', - receivedAt: Date.now() - 24 * 60 * 1000, - conversationOpenInternal: () => console.log('onClick'), - }, - { - from: { - isMe: true, - avatarPath: util.gifObjectUrl, - }, - to: { - name: "Y'all πŸŒ†", - }, - id: '4-guid-guid-guid-guid-guid', - conversationId: 'EveryoneGroupID', - snippet: 'Well, <>everyone<>, happy new year!', - receivedAt: Date.now() - 24 * 60 * 1000, - conversationOpenInternal: () => console.log('onClick'), - }, -]; - -const messageLookup = util._.fromPairs( - util._.map(messages, message => [message.id, message]) -); -messages.forEach(message => { - items.push({ - type: 'message', - data: message.id, - }); -}); - - - - console.log('openConversationInternal', args) - } - startNewConversation={(...args) => - console.log('startNewConversation', args) - } - onStartNewConversation={(...args) => - console.log('onStartNewConversation', args) - } - renderMessageSearchResult={id => ( - - console.log('openConversationInternal', args) - } - /> - )} - /> -; -``` - -#### With 'start new conversation' - -```jsx -const items = [ - { - type: 'start-new-conversation', - data: undefined, - }, - { - type: 'conversations-header', - data: undefined, - }, - { - type: 'conversation', - data: { - name: 'Everyone πŸŒ†', - conversationType: 'group', - phoneNumber: '(202) 555-0011', - avatarPath: util.landscapeGreenObjectUrl, - lastUpdated: Date.now() - 5 * 60 * 1000, - lastMessage: { - text: 'The rabbit hopped silently in the night.', - status: 'sent', - }, - }, - }, - { - type: 'conversation', - data: { - name: 'Everyone Else πŸ”₯', - conversationType: 'direct', - phoneNumber: '(202) 555-0012', - avatarPath: util.landscapePurpleObjectUrl, - lastUpdated: Date.now() - 5 * 60 * 1000, - lastMessage: { - text: "What's going on?", - status: 'sent', - }, - }, - }, - { - type: 'contacts-header', - data: undefined, - }, - { - type: 'contact', - data: { - name: 'The one Everyone', - conversationType: 'direct', - phoneNumber: '(202) 555-0013', - avatarPath: util.gifObjectUrl, - }, - }, - { - type: 'contact', - data: { - name: 'No likey everyone', - conversationType: 'direct', - phoneNumber: '(202) 555-0014', - color: 'red', - }, - }, - { - type: 'messages-header', - data: undefined, - }, -]; - -const messages = [ - { - from: { - isMe: true, - avatarPath: util.gifObjectUrl, - }, - to: { - name: 'Mr. Fire πŸ”₯', - phoneNumber: '(202) 555-0015', - }, - id: '1-guid-guid-guid-guid-guid', - conversationId: '(202) 555-0015', - receivedAt: Date.now() - 5 * 60 * 1000, - snippet: '<>Everyone<>! Get in!', - conversationOpenInternal: () => console.log('onClick'), - }, - { - from: { - name: 'Jon ❄️', - phoneNumber: '(202) 555-0016', - color: 'green', - }, - to: { - isMe: true, - }, - id: '2-guid-guid-guid-guid-guid', - conversationId: '(202) 555-0016', - snippet: 'Why is <>everyone<> so frustrated?', - receivedAt: Date.now() - 20 * 60 * 1000, - conversationOpenInternal: () => console.log('onClick'), - }, - { - from: { - name: 'Someone', - phoneNumber: '(202) 555-0011', - color: 'green', - avatarPath: util.pngObjectUrl, - }, - to: { - name: "Y'all πŸŒ†", - }, - id: '3-guid-guid-guid-guid-guid', - conversationId: 'EveryoneGroupID', - snippet: 'Hello, <>everyone<>! Woohooo!', - receivedAt: Date.now() - 24 * 60 * 1000, - conversationOpenInternal: () => console.log('onClick'), - }, - { - from: { - isMe: true, - avatarPath: util.gifObjectUrl, - }, - to: { - name: "Y'all πŸŒ†", - }, - id: '4-guid-guid-guid-guid-guid', - conversationId: 'EveryoneGroupID', - snippet: 'Well, <>everyone<>, happy new year!', - receivedAt: Date.now() - 24 * 60 * 1000, - conversationOpenInternal: () => console.log('onClick'), - }, -]; - -const messageLookup = util._.fromPairs( - util._.map(messages, message => [message.id, message]) -); -messages.forEach(message => { - items.push({ - type: 'message', - data: message.id, - }); -}); - - - - console.log('openConversationInternal', args) - } - startNewConversation={(...args) => - console.log('startNewConversation', args) - } - onStartNewConversation={(...args) => - console.log('onStartNewConversation', args) - } - renderMessageSearchResult={id => ( - - console.log('openConversationInternal', args) - } - /> - )} - /> -; -``` - -#### With no conversations - -```jsx -const items = [ - { - type: 'contacts-header', - data: undefined, - }, - { - type: 'contact', - data: { - name: 'The one Everyone', - conversationType: 'direct', - phoneNumber: '(202) 555-0013', - avatarPath: util.gifObjectUrl, - }, - }, - { - type: 'contact', - data: { - name: 'No likey everyone', - conversationType: 'direct', - phoneNumber: '(202) 555-0014', - color: 'red', - }, - }, - { - type: 'messages-header', - data: undefined, - }, -]; - -const messages = [ - { - from: { - isMe: true, - avatarPath: util.gifObjectUrl, - }, - to: { - name: 'Mr. Fire πŸ”₯', - phoneNumber: '(202) 555-0015', - }, - id: '1-guid-guid-guid-guid-guid', - conversationId: '(202) 555-0015', - receivedAt: Date.now() - 5 * 60 * 1000, - snippet: '<>Everyone<>! Get in!', - conversationOpenInternal: () => console.log('onClick'), - }, - { - from: { - name: 'Jon ❄️', - phoneNumber: '(202) 555-0016', - color: 'green', - }, - to: { - isMe: true, - }, - id: '2-guid-guid-guid-guid-guid', - conversationId: '(202) 555-0016', - snippet: 'Why is <>everyone<> so frustrated?', - receivedAt: Date.now() - 20 * 60 * 1000, - conversationOpenInternal: () => console.log('onClick'), - }, - { - from: { - name: 'Someone', - phoneNumber: '(202) 555-0011', - color: 'green', - avatarPath: util.pngObjectUrl, - }, - to: { - name: "Y'all πŸŒ†", - }, - id: '3-guid-guid-guid-guid-guid', - conversationId: 'EveryoneGroupID', - snippet: 'Hello, <>everyone<>! Woohooo!', - receivedAt: Date.now() - 24 * 60 * 1000, - conversationOpenInternal: () => console.log('onClick'), - }, - { - from: { - isMe: true, - avatarPath: util.gifObjectUrl, - }, - to: { - name: "Y'all πŸŒ†", - }, - id: '4-guid-guid-guid-guid-guid', - conversationId: 'EveryoneGroupID', - snippet: 'Well, <>everyone<>, happy new year!', - receivedAt: Date.now() - 24 * 60 * 1000, - conversationOpenInternal: () => console.log('onClick'), - }, -]; - -const messageLookup = util._.fromPairs( - util._.map(messages, message => [message.id, message]) -); -messages.forEach(message => { - items.push({ - type: 'message', - data: message.id, - }); -}); - - - - console.log('openConversationInternal', args) - } - startNewConversation={(...args) => - console.log('startNewConversation', args) - } - onStartNewConversation={(...args) => - console.log('onStartNewConversation', args) - } - renderMessageSearchResult={id => ( - - console.log('openConversationInternal', args) - } - /> - )} - /> -; -``` - -#### With no contacts - -```jsx -const items = [ - { - type: 'conversations-header', - data: undefined, - }, - { - type: 'conversation', - data: { - name: 'Everyone πŸŒ†', - conversationType: 'group', - phoneNumber: '(202) 555-0011', - avatarPath: util.landscapeGreenObjectUrl, - lastUpdated: Date.now() - 5 * 60 * 1000, - lastMessage: { - text: 'The rabbit hopped silently in the night.', - status: 'sent', - }, - }, - }, - { - type: 'conversation', - data: { - name: 'Everyone Else πŸ”₯', - conversationType: 'direct', - phoneNumber: '(202) 555-0012', - avatarPath: util.landscapePurpleObjectUrl, - lastUpdated: Date.now() - 5 * 60 * 1000, - lastMessage: { - text: "What's going on?", - status: 'sent', - }, - }, - }, - { - type: 'messages-header', - data: undefined, - }, -]; - -const messages = [ - { - from: { - isMe: true, - avatarPath: util.gifObjectUrl, - }, - to: { - name: 'Mr. Fire πŸ”₯', - phoneNumber: '(202) 555-0015', - }, - id: '1-guid-guid-guid-guid-guid', - conversationId: '(202) 555-0015', - receivedAt: Date.now() - 5 * 60 * 1000, - snippet: '<>Everyone<>! Get in!', - conversationOpenInternal: () => console.log('onClick'), - }, - { - from: { - name: 'Jon ❄️', - phoneNumber: '(202) 555-0016', - color: 'green', - }, - to: { - isMe: true, - }, - id: '2-guid-guid-guid-guid-guid', - conversationId: '(202) 555-0016', - snippet: 'Why is <>everyone<> so frustrated?', - receivedAt: Date.now() - 20 * 60 * 1000, - conversationOpenInternal: () => console.log('onClick'), - }, - { - from: { - name: 'Someone', - phoneNumber: '(202) 555-0011', - color: 'green', - avatarPath: util.pngObjectUrl, - }, - to: { - name: "Y'all πŸŒ†", - }, - id: '3-guid-guid-guid-guid-guid', - conversationId: 'EveryoneGroupID', - snippet: 'Hello, <>everyone<>! Woohooo!', - receivedAt: Date.now() - 24 * 60 * 1000, - conversationOpenInternal: () => console.log('onClick'), - }, - { - from: { - isMe: true, - avatarPath: util.gifObjectUrl, - }, - to: { - name: "Y'all πŸŒ†", - }, - id: '4-guid-guid-guid-guid-guid', - conversationId: 'EveryoneGroupID', - snippet: 'Well, <>everyone<>, happy new year!', - receivedAt: Date.now() - 24 * 60 * 1000, - conversationOpenInternal: () => console.log('onClick'), - }, -]; - -const messageLookup = util._.fromPairs( - util._.map(messages, message => [message.id, message]) -); -messages.forEach(message => { - items.push({ - type: 'message', - data: message.id, - }); -}); - - - - console.log('openConversationInternal', args) - } - startNewConversation={(...args) => - console.log('startNewConversation', args) - } - onStartNewConversation={(...args) => - console.log('onStartNewConversation', args) - } - renderMessageSearchResult={id => ( - - console.log('openConversationInternal', args) - } - /> - )} - /> -; -``` - -#### With no messages - -```jsx -const items = [ - { - type: 'conversations-header', - data: undefined, - }, - { - type: 'conversation', - data: { - name: 'Everyone πŸŒ†', - conversationType: 'group', - phoneNumber: '(202) 555-0011', - avatarPath: util.landscapeGreenObjectUrl, - lastUpdated: Date.now() - 5 * 60 * 1000, - lastMessage: { - text: 'The rabbit hopped silently in the night.', - status: 'sent', - }, - }, - }, - { - type: 'conversation', - data: { - name: 'Everyone Else πŸ”₯', - conversationType: 'direct', - phoneNumber: '(202) 555-0012', - avatarPath: util.landscapePurpleObjectUrl, - lastUpdated: Date.now() - 5 * 60 * 1000, - lastMessage: { - text: "What's going on?", - status: 'sent', - }, - }, - }, - { - type: 'contacts-header', - data: undefined, - }, - { - type: 'contact', - data: { - name: 'The one Everyone', - conversationType: 'direct', - phoneNumber: '(202) 555-0013', - avatarPath: util.gifObjectUrl, - }, - }, - { - type: 'contact', - data: { - name: 'No likey everyone', - conversationType: 'direct', - phoneNumber: '(202) 555-0014', - color: 'red', - }, - }, -]; - - - - console.log('openConversationInternal', args) - } - startNewConversation={(...args) => - console.log('startNewConversation', args) - } - onStartNewConversation={(...args) => - console.log('onStartNewConversation', args) - } - /> -; -``` - -#### With no results at all - -```jsx - - - console.log('openConversationInternal', args) - } - startNewConversation={(...args) => - console.log('startNewConversation', args) - } - onStartNewConversation={(...args) => - console.log('onStartNewConversation', args) - } - renderMessageSearchResult={id => ( - - console.log('openConversationInternal', args) - } - /> - )} - /> - -``` - -#### With no results at all, searching in conversation - -```jsx - - - console.log('openConversationInternal', args) - } - startNewConversation={(...args) => - console.log('startNewConversation', args) - } - onStartNewConversation={(...args) => - console.log('onStartNewConversation', args) - } - renderMessageSearchResult={id => ( - - console.log('openConversationInternal', args) - } - /> - )} - /> - -``` - -#### Searching in conversation but no search term - -```jsx - - - console.log('openConversationInternal', args) - } - startNewConversation={(...args) => - console.log('startNewConversation', args) - } - onStartNewConversation={(...args) => - console.log('onStartNewConversation', args) - } - renderMessageSearchResult={id => ( - - console.log('openConversationInternal', args) - } - /> - )} - /> - -``` - -#### With a lot of results - -```jsx -const items = [ - { - type: 'conversations-header', - data: undefined, - }, - { - type: 'conversation', - data: { - name: 'Everyone πŸŒ†', - conversationType: 'group', - phoneNumber: '(202) 555-0011', - avatarPath: util.landscapeGreenObjectUrl, - lastUpdated: Date.now() - 5 * 60 * 1000, - lastMessage: { - text: 'The rabbit hopped silently in the night.', - status: 'sent', - }, - }, - }, - { - type: 'conversation', - data: { - name: 'Everyone Else πŸ”₯', - conversationType: 'direct', - phoneNumber: '(202) 555-0012', - avatarPath: util.landscapePurpleObjectUrl, - lastUpdated: Date.now() - 5 * 60 * 1000, - lastMessage: { - text: "What's going on?", - status: 'sent', - }, - }, - }, - { - type: 'contacts-header', - data: undefined, - }, - { - type: 'contact', - data: { - name: 'The one Everyone', - conversationType: 'direct', - phoneNumber: '(202) 555-0013', - avatarPath: util.gifObjectUrl, - }, - }, - { - type: 'contact', - data: { - name: 'No likey everyone', - conversationType: 'direct', - phoneNumber: '(202) 555-0014', - color: 'red', - }, - }, - { - type: 'messages-header', - data: undefined, - }, -]; - -const messages = []; -for (let i = 0; i < 100; i += 1) { - messages.push({ - from: { - name: 'Mr. Fire πŸ”₯', - phoneNumber: '(202) 555-0015', - avatarPath: util.landscapeGreenObjectUrl, - }, - to: { - isMe: true, - }, - id: `${i}-guid-guid-guid-guid-guid`, - conversationId: '(202) 555-0015', - receivedAt: Date.now() - 5 * 60 * 1000, - snippet: `${i} <>Everyone<>! Get in!`, - conversationOpenInternal: data => console.log('onClick', data), - }); -} - -const messageLookup = util._.fromPairs( - util._.map(messages, message => [message.id, message]) -); -messages.forEach(message => { - items.push({ - type: 'message', - data: message.id, - }); -}); - - - - console.log('openConversationInternal', args) - } - startNewConversation={(...args) => - console.log('startNewConversation', args) - } - onStartNewConversation={(...args) => - console.log('onStartNewConversation', args) - } - renderMessageSearchResult={id => ( - - console.log('openConversationInternal', args) - } - /> - )} - /> -; -``` - -#### With just messages and no header - -```jsx -const items = []; - -const messages = []; -for (let i = 0; i < 10; i += 1) { - messages.push({ - from: { - name: 'Mr. Fire πŸ”₯', - phoneNumber: '(202) 555-0015', - avatarPath: util.landscapeGreenObjectUrl, - }, - to: { - isMe: true, - }, - id: `${i}-guid-guid-guid-guid-guid`, - conversationId: '(202) 555-0015', - receivedAt: Date.now() - 5 * 60 * 1000, - snippet: `${i} <>Everyone<>! Get in!`, - conversationOpenInternal: data => console.log('onClick', data), - }); -} - -const messageLookup = util._.fromPairs( - util._.map(messages, message => [message.id, message]) -); -messages.forEach(message => { - items.push({ - type: 'message', - data: message.id, - }); -}); - - - - console.log('openConversationInternal', args) - } - startNewConversation={(...args) => - console.log('startNewConversation', args) - } - onStartNewConversation={(...args) => - console.log('onStartNewConversation', args) - } - renderMessageSearchResult={id => ( - - console.log('openConversationInternal', args) - } - /> - )} - /> -; -``` diff --git a/ts/components/SearchResults.stories.tsx b/ts/components/SearchResults.stories.tsx new file mode 100644 index 000000000000..a86a9e0c91b0 --- /dev/null +++ b/ts/components/SearchResults.stories.tsx @@ -0,0 +1,423 @@ +import * as React from 'react'; +import { SearchResults } from './SearchResults'; +import { + MessageSearchResult, + PropsDataType as MessageSearchResultPropsType, +} from './MessageSearchResult'; + +// @ts-ignore +import { setup as setupI18n } from '../../js/modules/i18n'; +// @ts-ignore +import enMessages from '../../_locales/en/messages.json'; + +import { storiesOf } from '@storybook/react'; +//import { boolean, select } from '@storybook/addon-knobs'; +import { action } from '@storybook/addon-actions'; + +// @ts-ignore +import gif from '../../fixtures/giphy-GVNvOUpeYmI7e.gif'; +// @ts-ignore +import png from '../../fixtures/freepngs-2cd43b_bed7d1327e88454487397574d87b64dc_mv2.png'; +// @ts-ignore +import landscapeGreen from '../../fixtures/1000x50-green.jpeg'; +// @ts-ignore +import landscapePurple from '../../fixtures/200x50-purple.png'; + +const i18n = setupI18n('en', enMessages); + +function makeObjectUrl(data: ArrayBuffer, contentType: string): string { + const blob = new Blob([data], { + type: contentType, + }); + + return URL.createObjectURL(blob); +} + +// 320x240 +const gifObjectUrl = makeObjectUrl(gif, 'image/gif'); +// 800Γ—1200 +const pngObjectUrl = makeObjectUrl(png, 'image/png'); +const landscapeGreenObjectUrl = makeObjectUrl(landscapeGreen, 'image/jpeg'); +const landscapePurpleObjectUrl = makeObjectUrl(landscapePurple, 'image/png'); + +const messageLookup: Map = new Map(); + +const CONTACT = 'contact' as 'contact'; +const CONTACTS_HEADER = 'contacts-header' as 'contacts-header'; +const CONVERSATION = 'conversation' as 'conversation'; +const CONVERSATIONS_HEADER = 'conversations-header' as 'conversations-header'; +const DIRECT = 'direct' as 'direct'; +const GROUP = 'group' as 'group'; +const MESSAGE = 'message' as 'message'; +const MESSAGES_HEADER = 'messages-header' as 'messages-header'; +const SENT = 'sent' as 'sent'; +const START_NEW_CONVERSATION = 'start-new-conversation' as 'start-new-conversation'; +const SMS_MMS_NOT_SUPPORTED = 'sms-mms-not-supported-text' as 'sms-mms-not-supported-text'; + +// tslint:disable-next-line no-backbone-get-set-outside-model +messageLookup.set('1-guid-guid-guid-guid-guid', { + id: '1-guid-guid-guid-guid-guid', + conversationId: '(202) 555-0015', + sentAt: Date.now() - 5 * 60 * 1000, + snippet: '<>Everyone<>! Get in!', + + from: { + phoneNumber: '(202) 555-0020', + isMe: true, + avatarPath: gifObjectUrl, + }, + to: { + phoneNumber: '(202) 555-0015', + name: 'Mr. Fire πŸ”₯', + }, +}); + +// tslint:disable-next-line no-backbone-get-set-outside-model +messageLookup.set('2-guid-guid-guid-guid-guid', { + id: '2-guid-guid-guid-guid-guid', + conversationId: '(202) 555-0016', + sentAt: Date.now() - 20 * 60 * 1000, + snippet: 'Why is <>everyone<> so frustrated?', + from: { + phoneNumber: '(202) 555-0016', + name: 'Jon ❄️', + color: 'green', + }, + to: { + phoneNumber: '(202) 555-0020', + isMe: true, + }, +}); + +// tslint:disable-next-line no-backbone-get-set-outside-model +messageLookup.set('3-guid-guid-guid-guid-guid', { + id: '3-guid-guid-guid-guid-guid', + conversationId: 'EveryoneGroupID', + sentAt: Date.now() - 24 * 60 * 1000, + snippet: 'Hello, <>everyone<>! Woohooo!', + from: { + phoneNumber: '(202) 555-0011', + name: 'Someone', + color: 'green', + avatarPath: pngObjectUrl, + }, + to: { + phoneNumber: '(202) 555-0016', + name: "Y'all πŸŒ†", + }, +}); + +// tslint:disable-next-line no-backbone-get-set-outside-model +messageLookup.set('4-guid-guid-guid-guid-guid', { + id: '4-guid-guid-guid-guid-guid', + conversationId: 'EveryoneGroupID', + sentAt: Date.now() - 24 * 60 * 1000, + snippet: 'Well, <>everyone<>, happy new year!', + from: { + phoneNumber: '(202) 555-0020', + isMe: true, + avatarPath: gifObjectUrl, + }, + to: { + phoneNumber: '(202) 555-0016', + name: "Y'all πŸŒ†", + }, +}); + +const defaultProps = { + discussionsLoading: false, + items: [], + i18n, + messagesLoading: false, + noResults: false, + openConversationInternal: action('open-conversation-internal'), + regionCode: 'US', + renderMessageSearchResult(id: string): JSX.Element { + const messageProps = messageLookup.get(id) as MessageSearchResultPropsType; + + return ( + + ); + }, + searchConversationName: undefined, + searchTerm: '1234567890', + selectedConversationId: undefined, + selectedMessageId: undefined, + startNewConversation: action('start-new-conversation'), +}; + +const conversations = [ + { + type: CONVERSATION, + data: { + id: '+12025550011', + phoneNumber: '(202) 555-0011', + name: 'Everyone πŸŒ†', + type: GROUP, + avatarPath: landscapeGreenObjectUrl, + isMe: false, + lastUpdated: Date.now() - 5 * 60 * 1000, + unreadCount: 0, + isSelected: false, + lastMessage: { + text: 'The rabbit hopped silently in the night.', + status: SENT, + }, + }, + }, + { + type: CONVERSATION, + data: { + id: '+12025550012', + phoneNumber: '(202) 555-0012', + name: 'Everyone Else πŸ”₯', + type: DIRECT, + avatarPath: landscapePurpleObjectUrl, + isMe: false, + lastUpdated: Date.now() - 5 * 60 * 1000, + unreadCount: 0, + isSelected: false, + lastMessage: { + text: "What's going on?", + status: SENT, + }, + }, + }, +]; + +const contacts = [ + { + type: CONTACT, + data: { + id: '+12025550013', + phoneNumber: '(202) 555-0013', + name: 'The one Everyone', + type: DIRECT, + avatarPath: gifObjectUrl, + isMe: false, + lastUpdated: Date.now() - 10 * 60 * 1000, + unreadCount: 0, + isSelected: false, + }, + }, + { + type: CONTACT, + data: { + id: '+12025550014', + phoneNumber: '(202) 555-0014', + name: 'No likey everyone', + type: DIRECT, + color: 'red', + isMe: false, + lastUpdated: Date.now() - 11 * 60 * 1000, + unreadCount: 0, + isSelected: false, + }, + }, +]; + +const messages = [ + { + type: MESSAGE, + data: '1-guid-guid-guid-guid-guid', + }, + { + type: MESSAGE, + data: '2-guid-guid-guid-guid-guid', + }, + { + type: MESSAGE, + data: '3-guid-guid-guid-guid-guid', + }, + { + type: MESSAGE, + data: '4-guid-guid-guid-guid-guid', + }, +]; + +const messagesMany = Array.from(Array(100), (_, i) => messages[i % 4]); + +const permutations = [ + { + title: 'SMS/MMS Not Supported Text', + props: { + items: [ + { + type: START_NEW_CONVERSATION, + data: undefined, + }, + { + type: SMS_MMS_NOT_SUPPORTED, + data: undefined, + }, + ], + }, + }, + { + title: 'All Result Types', + props: { + items: [ + { + type: CONVERSATIONS_HEADER, + data: undefined, + }, + ...conversations, + { + type: CONTACTS_HEADER, + data: undefined, + }, + ...contacts, + { + type: MESSAGES_HEADER, + data: undefined, + }, + ...messages, + ], + }, + }, + { + title: 'Start new Conversation', + props: { + items: [ + { + type: START_NEW_CONVERSATION, + data: undefined, + }, + { + type: CONVERSATIONS_HEADER, + data: undefined, + }, + ...conversations, + { + type: CONTACTS_HEADER, + data: undefined, + }, + ...contacts, + { + type: MESSAGES_HEADER, + data: undefined, + }, + ...messages, + ], + }, + }, + { + title: 'No Conversations', + props: { + items: [ + { + type: CONTACTS_HEADER, + data: undefined, + }, + ...contacts, + { + type: MESSAGES_HEADER, + data: undefined, + }, + ...messages, + ], + }, + }, + { + title: 'No Contacts', + props: { + items: [ + { + type: CONVERSATIONS_HEADER, + data: undefined, + }, + ...conversations, + { + type: MESSAGES_HEADER, + data: undefined, + }, + ...messages, + ], + }, + }, + { + title: 'No Messages', + props: { + items: [ + { + type: CONVERSATIONS_HEADER, + data: undefined, + }, + ...conversations, + { + type: CONTACTS_HEADER, + data: undefined, + }, + ...contacts, + ], + }, + }, + { + title: 'No Results', + props: { + noResults: true, + }, + }, + { + title: 'No Results, Searching in Conversation', + props: { + noResults: true, + searchInConversationName: 'Everyone πŸ”₯', + searchTerm: 'something', + }, + }, + { + title: 'Searching in Conversation no search term', + props: { + noResults: true, + searchInConversationName: 'Everyone πŸ”₯', + searchTerm: '', + }, + }, + { + title: 'Lots of results', + props: { + items: [ + { + type: CONVERSATIONS_HEADER, + data: undefined, + }, + ...conversations, + { + type: CONTACTS_HEADER, + data: undefined, + }, + ...contacts, + { + type: MESSAGES_HEADER, + data: undefined, + }, + ...messagesMany, + ], + }, + }, + { + title: 'Messages, no header', + props: { + items: messages, + }, + }, +]; + +storiesOf('Components/SearchResults', module).add('Iterations', () => { + return permutations.map(({ props, title }) => ( + <> +

{title}

+
+ +
+
+ + )); +}); diff --git a/ts/components/SearchResults.tsx b/ts/components/SearchResults.tsx index 09ee9a70c154..46bf15d4cee5 100644 --- a/ts/components/SearchResults.tsx +++ b/ts/components/SearchResults.tsx @@ -35,6 +35,10 @@ type StartNewConversationType = { type: 'start-new-conversation'; data: undefined; }; +type NotSupportedSMS = { + type: 'sms-mms-not-supported-text'; + data: undefined; +}; type ConversationHeaderType = { type: 'conversations-header'; data: undefined; @@ -66,6 +70,7 @@ type SpinnerType = { export type SearchResultRowType = | StartNewConversationType + | NotSupportedSMS | ConversationHeaderType | ContactsHeaderType | MessagesHeaderType @@ -368,6 +373,12 @@ export class SearchResults extends React.Component { onClick={this.handleStartNewConversation} /> ); + } else if (row.type === 'sms-mms-not-supported-text') { + return ( +
+ {i18n('notSupportedSMS')} +
+ ); } else if (row.type === 'conversations-header') { return (
{ notification = ( ); + } else if (item.type === 'linkNotification') { + notification = ( +
+
+ {i18n('messageHistoryUnsynced')} +
+ ); } else if (item.type === 'timerNotification') { notification = ( diff --git a/ts/shims/textsecure.ts b/ts/shims/textsecure.ts index cc0bbf5813d2..17dd50daeea1 100644 --- a/ts/shims/textsecure.ts +++ b/ts/shims/textsecure.ts @@ -5,6 +5,7 @@ type TextSecureType = { user: { getNumber: () => string; }; + get: (item: string) => any; }; messaging: { sendStickerPackSync: ( diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index a59903d710bd..e8da1222c6f4 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -27,6 +27,7 @@ export type ConversationType = { isArchived: boolean; activeAt?: number; timestamp: number; + inboxPosition: number; lastMessage?: { status: 'error' | 'sending' | 'sent' | 'delivered' | 'read'; text: string; @@ -56,7 +57,13 @@ export type MessageType = { id: string; conversationId: string; source: string; - type: 'incoming' | 'outgoing' | 'group' | 'keychange' | 'verified-change'; + type: + | 'incoming' + | 'outgoing' + | 'group' + | 'keychange' + | 'verified-change' + | 'message-history-unsynced'; quote?: { author: string }; received_at: number; hasSignalAccount?: boolean; diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index 96757da029fe..bd3f0392415d 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -117,6 +117,21 @@ export const _getConversationComparator = ( return rightTimestamp - leftTimestamp; } + if ( + typeof left.inboxPosition === 'number' && + typeof right.inboxPosition === 'number' + ) { + return right.inboxPosition > left.inboxPosition ? -1 : 1; + } + + if (typeof left.inboxPosition === 'number' && right.inboxPosition == null) { + return -1; + } + + if (typeof right.inboxPosition === 'number' && left.inboxPosition == null) { + return 1; + } + const leftTitle = getConversationTitle(left, { i18n, ourRegionCode, diff --git a/ts/state/selectors/search.ts b/ts/state/selectors/search.ts index c7a384df26d6..8fe1501a6316 100644 --- a/ts/state/selectors/search.ts +++ b/ts/state/selectors/search.ts @@ -1,6 +1,7 @@ import memoizee from 'memoizee'; import { createSelector } from 'reselect'; import { getSearchResultsProps } from '../../shims/Whisper'; +import { instance } from '../../util/libphonenumberInstance'; import { StateType } from '../reducer'; @@ -20,7 +21,7 @@ import { } from '../../components/SearchResults'; import { PropsDataType as MessageSearchResultPropsDataType } from '../../components/MessageSearchResult'; -import { getRegionCode, getUserNumber } from './user'; +import { getRegionCode, getUserAgent, getUserNumber } from './user'; import { GetConversationByIdType, getConversationLookup, @@ -72,6 +73,7 @@ export const getSearchResults = createSelector( [ getSearch, getRegionCode, + getUserAgent, getConversationLookup, getSelectedConversation, getSelectedMessage, @@ -79,6 +81,7 @@ export const getSearchResults = createSelector( ( state: SearchStateType, regionCode: string, + userAgent: string, lookup: ConversationLookupType, selectedConversationId?: string, selectedMessageId?: string @@ -114,6 +117,17 @@ export const getSearchResults = createSelector( type: 'start-new-conversation', data: undefined, }); + + const isIOS = userAgent === 'OWI'; + const parsedNumber = instance.parse(state.query, regionCode); + const isValidNumber = instance.isValidNumber(parsedNumber); + + if (!isIOS && isValidNumber) { + items.push({ + type: 'sms-mms-not-supported-text', + data: undefined, + }); + } } if (haveConversations) { diff --git a/ts/state/selectors/user.ts b/ts/state/selectors/user.ts index e7241e6e6c21..c4e2517b4ceb 100644 --- a/ts/state/selectors/user.ts +++ b/ts/state/selectors/user.ts @@ -4,9 +4,12 @@ import { LocalizerType } from '../../types/Util'; import { StateType } from '../reducer'; import { UserStateType } from '../ducks/user'; +import { ItemsStateType } from '../ducks/items'; export const getUser = (state: StateType): UserStateType => state.user; +export const getItems = (state: StateType): ItemsStateType => state.items; + export const getUserNumber = createSelector( getUser, (state: UserStateType): string => state.ourNumber @@ -27,6 +30,11 @@ export const getUserUuid = createSelector( (state: UserStateType): string => state.ourUuid ); +export const getUserAgent = createSelector( + getItems, + (state: ItemsStateType): string => state.userAgent +); + export const getIntl = createSelector( getUser, (state: UserStateType): LocalizerType => state.i18n diff --git a/ts/test/state/selectors/conversations_test.ts b/ts/test/state/selectors/conversations_test.ts index 52cd60288a1d..25b3051d069c 100644 --- a/ts/test/state/selectors/conversations_test.ts +++ b/ts/test/state/selectors/conversations_test.ts @@ -17,6 +17,7 @@ describe('state/selectors/conversations', () => { activeAt: Date.now(), name: 'No timestamp', timestamp: 0, + inboxPosition: 0, phoneNumber: 'notused', isArchived: false, @@ -36,6 +37,7 @@ describe('state/selectors/conversations', () => { activeAt: Date.now(), name: 'B', timestamp: 20, + inboxPosition: 21, phoneNumber: 'notused', isArchived: false, @@ -55,6 +57,7 @@ describe('state/selectors/conversations', () => { activeAt: Date.now(), name: 'C', timestamp: 20, + inboxPosition: 22, phoneNumber: 'notused', isArchived: false, @@ -74,6 +77,7 @@ describe('state/selectors/conversations', () => { activeAt: Date.now(), name: 'Á', timestamp: 20, + inboxPosition: 20, phoneNumber: 'notused', isArchived: false, @@ -93,6 +97,7 @@ describe('state/selectors/conversations', () => { activeAt: Date.now(), name: 'First!', timestamp: 30, + inboxPosition: 30, phoneNumber: 'notused', isArchived: false, diff --git a/ts/test/types/Conversation_test.ts b/ts/test/types/Conversation_test.ts index a220f52bb6e6..e57b4decd707 100644 --- a/ts/test/types/Conversation_test.ts +++ b/ts/test/types/Conversation_test.ts @@ -3,6 +3,7 @@ import { assert } from 'chai'; import * as Conversation from '../../types/Conversation'; import { IncomingMessage, + MessageHistoryUnsyncedMessage, OutgoingMessage, VerifiedChangeMessage, } from '../../types/Message'; @@ -44,6 +45,30 @@ describe('Conversation', () => { assert.deepEqual(actual, expected); }); }); + + context('for message history unsynced message', () => { + it('should skip update', () => { + const input = { + currentTimestamp: 555, + lastMessage: { + type: 'message-history-unsynced', + conversationId: 'foo', + sent_at: 666, + timestamp: 666, + } as MessageHistoryUnsyncedMessage, + lastMessageNotificationText: 'xoxoxoxo', + }; + const expected = { + lastMessage: 'xoxoxoxo', + lastMessageStatus: null, + timestamp: 555, + }; + + const actual = Conversation.createLastMessageUpdate(input); + assert.deepEqual(actual, expected); + }); + }); + context('for verified change message', () => { it('should skip update', () => { const input = { diff --git a/ts/types/Conversation.ts b/ts/types/Conversation.ts index 943e4caec82d..9b3b08ed2a2b 100644 --- a/ts/types/Conversation.ts +++ b/ts/types/Conversation.ts @@ -26,13 +26,16 @@ export const createLastMessageUpdate = ({ } const { type, expirationTimerUpdate } = lastMessage; + const isMessageHistoryUnsynced = type === 'message-history-unsynced'; const isVerifiedChangeMessage = type === 'verified-change'; const isExpireTimerUpdateFromSync = Boolean( expirationTimerUpdate && expirationTimerUpdate.fromSync ); const shouldUpdateTimestamp = Boolean( - !isVerifiedChangeMessage && !isExpireTimerUpdateFromSync + !isMessageHistoryUnsynced && + !isVerifiedChangeMessage && + !isExpireTimerUpdateFromSync ); const newTimestamp = shouldUpdateTimestamp ? lastMessage.sent_at diff --git a/ts/types/Message.ts b/ts/types/Message.ts index 4fb2bbdf1a76..6d002a682666 100644 --- a/ts/types/Message.ts +++ b/ts/types/Message.ts @@ -2,7 +2,10 @@ import { Attachment } from './Attachment'; import { ContactType } from './Contact'; import { IndexableBoolean, IndexablePresence } from './IndexedDB'; -export type Message = UserMessage | VerifiedChangeMessage; +export type Message = + | UserMessage + | VerifiedChangeMessage + | MessageHistoryUnsyncedMessage; export type UserMessage = IncomingMessage | OutgoingMessage; export type IncomingMessage = Readonly< @@ -65,6 +68,14 @@ export type VerifiedChangeMessage = Readonly< ExpirationTimerUpdate >; +export type MessageHistoryUnsyncedMessage = Readonly< + { + type: 'message-history-unsynced'; + } & SharedMessageProperties & + MessageSchemaVersion5 & + ExpirationTimerUpdate +>; + type SharedMessageProperties = Readonly<{ conversationId: string; sent_at: number; diff --git a/ts/types/message/initializeAttachmentMetadata.ts b/ts/types/message/initializeAttachmentMetadata.ts index 56d19d45e8ff..f2075b71293b 100644 --- a/ts/types/message/initializeAttachmentMetadata.ts +++ b/ts/types/message/initializeAttachmentMetadata.ts @@ -16,6 +16,9 @@ export const initializeAttachmentMetadata = async ( if (message.type === 'verified-change') { return message; } + if (message.type === 'message-history-unsynced') { + return message; + } if (message.messageTimer || message.isViewOnce) { return message; } diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index 88e0f3792e23..2e8b3ae6871b 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -570,7 +570,7 @@ "rule": "jQuery-$(", "path": "js/views/install_view.js", "line": " this.$('#qr img').remove();", - "lineNumber": 135, + "lineNumber": 136, "reasonCategory": "usageTrusted", "updated": "2018-09-19T21:59:32.770Z", "reasonDetail": "Protected from arbitrary input" @@ -579,7 +579,7 @@ "rule": "jQuery-$(", "path": "js/views/install_view.js", "line": " this.$('#qr .container').show();", - "lineNumber": 137, + "lineNumber": 138, "reasonCategory": "usageTrusted", "updated": "2018-09-19T21:59:32.770Z", "reasonDetail": "Protected from arbitrary input" @@ -588,7 +588,7 @@ "rule": "jQuery-$(", "path": "js/views/install_view.js", "line": " if ($('#qr').length === 0) {", - "lineNumber": 141, + "lineNumber": 142, "reasonCategory": "usageTrusted", "updated": "2018-09-19T21:59:32.770Z", "reasonDetail": "Protected from arbitrary input" @@ -597,25 +597,25 @@ "rule": "jQuery-$(", "path": "js/views/install_view.js", "line": " this.$('#qr .container').hide();", - "lineNumber": 146, - "reasonCategory": "usageTrusted", - "updated": "2018-09-19T21:59:32.770Z", - "reasonDetail": "Protected from arbitrary input" - }, - { - "rule": "jQuery-$(", - "path": "js/views/install_view.js", - "line": " this.qr = new QRCode(this.$('#qr')[0]).makeCode(url);", "lineNumber": 147, "reasonCategory": "usageTrusted", "updated": "2018-09-19T21:59:32.770Z", "reasonDetail": "Protected from arbitrary input" }, + { + "rule": "jQuery-$(", + "path": "js/views/install_view.js", + "line": " this.qr = new QRCode(this.$('#qr')[0]).makeCode(url);", + "lineNumber": 148, + "reasonCategory": "usageTrusted", + "updated": "2018-09-19T21:59:32.770Z", + "reasonDetail": "Protected from arbitrary input" + }, { "rule": "jQuery-$(", "path": "js/views/install_view.js", "line": " this.$('#qr').addClass('ready');", - "lineNumber": 149, + "lineNumber": 150, "reasonCategory": "usageTrusted", "updated": "2018-09-19T21:59:32.770Z", "reasonDetail": "Protected from arbitrary input" @@ -624,7 +624,7 @@ "rule": "jQuery-$(", "path": "js/views/install_view.js", "line": " this.$(DEVICE_NAME_SELECTOR).val(deviceName || window.getHostName());", - "lineNumber": 154, + "lineNumber": 155, "reasonCategory": "usageTrusted", "updated": "2018-09-19T21:59:32.770Z", "reasonDetail": "Protected from arbitrary input" @@ -633,7 +633,7 @@ "rule": "jQuery-$(", "path": "js/views/install_view.js", "line": " this.$('#link-phone').submit();", - "lineNumber": 159, + "lineNumber": 160, "reasonCategory": "usageTrusted", "updated": "2018-09-19T21:59:32.770Z", "reasonDetail": "Protected from arbitrary input" @@ -642,7 +642,7 @@ "rule": "jQuery-$(", "path": "js/views/install_view.js", "line": " this.$('#link-phone').submit(e => {", - "lineNumber": 169, + "lineNumber": 170, "reasonCategory": "usageTrusted", "updated": "2018-09-19T21:59:32.770Z", "reasonDetail": "Protected from arbitrary input" @@ -651,7 +651,7 @@ "rule": "jQuery-$(", "path": "js/views/install_view.js", "line": " let name = this.$(DEVICE_NAME_SELECTOR).val();", - "lineNumber": 173, + "lineNumber": 174, "reasonCategory": "usageTrusted", "updated": "2018-09-19T21:59:32.770Z", "reasonDetail": "Protected from arbitrary input" @@ -660,7 +660,7 @@ "rule": "jQuery-$(", "path": "js/views/install_view.js", "line": " this.$(DEVICE_NAME_SELECTOR).focus();", - "lineNumber": 176, + "lineNumber": 177, "reasonCategory": "usageTrusted", "updated": "2018-09-19T21:59:32.770Z", "reasonDetail": "Protected from arbitrary input" @@ -11812,7 +11812,7 @@ "rule": "jQuery-wrap(", "path": "ts/shims/textsecure.ts", "line": " wrap(", - "lineNumber": 63, + "lineNumber": 64, "reasonCategory": "falseMatch", "updated": "2020-02-07T19:52:28.522Z" }