From 8be1c61f4c577289b6ff95f2b582bb152b352ffa Mon Sep 17 00:00:00 2001 From: Maxim Shishmarev Date: Wed, 2 Oct 2019 13:28:10 +1000 Subject: [PATCH] Highlight conversations with unread mentions of the user --- js/models/conversations.js | 16 ++++++++++ js/models/messages.js | 6 ++++ js/views/conversation_view.js | 3 +- stylesheets/_mentions.scss | 29 +++++++++++++++++++ stylesheets/_modules.scss | 2 +- ts/components/ConversationListItem.tsx | 16 ++++++++-- ts/state/ducks/conversations.ts | 1 + ts/test/state/selectors/conversations_test.ts | 5 ++++ 8 files changed, 73 insertions(+), 5 deletions(-) diff --git a/js/models/conversations.js b/js/models/conversations.js index f03eb4a73..de9c2216f 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -466,6 +466,7 @@ timestamp: this.get('timestamp'), title: this.getTitle(), unreadCount: this.get('unreadCount') || 0, + mentionedUs: this.get('mentionedUs') || false, showFriendRequestIndicator: this.isPendingFriendRequest(), isBlocked: this.isBlocked(), @@ -2007,6 +2008,21 @@ const unreadCount = unreadMessages.length - read.length; this.set({ unreadCount }); + + const mentionRead = (() => { + const stillUnread = unreadMessages.filter( + m => m.get('received_at') > newestUnreadDate + ); + const ourNumber = textsecure.storage.user.getNumber(); + return !stillUnread.some( + m => m.propsForMessage.text.indexOf(`@${ourNumber}`) !== -1 + ); + })(); + + if (mentionRead) { + this.set({ mentionedUs: false }); + } + await window.Signal.Data.updateConversation(this.id, this.attributes, { Conversation: Whisper.Conversation, }); diff --git a/js/models/messages.js b/js/models/messages.js index c0301ec05..7c2022fe6 100644 --- a/js/models/messages.js +++ b/js/models/messages.js @@ -1969,6 +1969,12 @@ c.onReadMessage(message); } } else { + const ourNumber = textsecure.storage.user.getNumber(); + + if (message.attributes.body.indexOf(`@${ourNumber}`) !== -1) { + conversation.set({ mentionedUs: true }); + } + conversation.set({ unreadCount: conversation.get('unreadCount') + 1, isArchived: false, diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index 355d55617..6606343fd 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -314,10 +314,11 @@ this.selectMember = this.selectMember.bind(this); const updateMemberList = async () => { + const maxToFetch = 1000; const allMessages = await window.Signal.Data.getMessagesByConversation( this.model.id, { - limit: Number.MAX_SAFE_INTEGER, + limit: maxToFetch, MessageCollection: Whisper.MessageCollection, } ); diff --git a/stylesheets/_mentions.scss b/stylesheets/_mentions.scss index 58c65c5af..150e94237 100644 --- a/stylesheets/_mentions.scss +++ b/stylesheets/_mentions.scss @@ -67,3 +67,32 @@ } } } + +.module-conversation-list-item--mentioned-us { + border-left: 4px solid #ffb000 !important; +} + +.at-symbol { + background-color: #ffb000; + + color: $color-black; + text-align: center; + + padding-top: 1px; + padding-left: 3px; + padding-right: 3px; + + position: absolute; + right: -6px; + top: 12px; + + font-weight: 300; + font-size: 11px; + letter-spacing: 0.25px; + + height: 16px; + min-width: 16px; + border-radius: 8px; + + box-shadow: 0px 0px 0px 1px $color-dark-85; +} diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index b109feb2d..87e8e5e45 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -1895,7 +1895,7 @@ position: absolute; right: -6px; - top: 6px; + top: -6px; font-weight: 300; font-size: 11px; diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 3a2fab947..8a973420d 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -26,6 +26,7 @@ export type PropsData = { lastUpdated: number; unreadCount: number; + mentionedUs: boolean; isSelected: boolean; isTyping: boolean; @@ -93,12 +94,17 @@ export class ConversationListItem extends React.PureComponent { } public renderUnread() { - const { unreadCount } = this.props; + const { unreadCount, mentionedUs } = this.props; if (unreadCount > 0) { + const atSymbol = mentionedUs ?

@

: null; + return ( -
- {unreadCount} +
+

+ {unreadCount} +

+ {atSymbol}
); } @@ -285,6 +291,7 @@ export class ConversationListItem extends React.PureComponent { showFriendRequestIndicator, isBlocked, style, + mentionedUs, } = this.props; const triggerId = `${phoneNumber}-ctxmenu-${Date.now()}`; @@ -305,6 +312,9 @@ export class ConversationListItem extends React.PureComponent { unreadCount > 0 ? 'module-conversation-list-item--has-unread' : null, + unreadCount > 0 && mentionedUs + ? 'module-conversation-list-item--mentioned-us' + : null, isSelected ? 'module-conversation-list-item--is-selected' : null, showFriendRequestIndicator ? 'module-conversation-list-item--has-friend-request' diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index b0ced5094..ab189d13c 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -49,6 +49,7 @@ export type ConversationType = { isClosable?: boolean; lastUpdated: number; unreadCount: number; + mentionedUs: boolean; isSelected: boolean; isTyping: boolean; isFriend?: boolean; diff --git a/ts/test/state/selectors/conversations_test.ts b/ts/test/state/selectors/conversations_test.ts index c56f50f2a..63b6cd9e9 100644 --- a/ts/test/state/selectors/conversations_test.ts +++ b/ts/test/state/selectors/conversations_test.ts @@ -24,6 +24,7 @@ describe('state/selectors/conversations', () => { isMe: false, lastUpdated: Date.now(), unreadCount: 1, + mentionedUs: false, isSelected: false, isTyping: false, }, @@ -39,6 +40,7 @@ describe('state/selectors/conversations', () => { isMe: false, lastUpdated: Date.now(), unreadCount: 1, + mentionedUs: false, isSelected: false, isTyping: false, }, @@ -54,6 +56,7 @@ describe('state/selectors/conversations', () => { isMe: false, lastUpdated: Date.now(), unreadCount: 1, + mentionedUs: false, isSelected: false, isTyping: false, }, @@ -69,6 +72,7 @@ describe('state/selectors/conversations', () => { isMe: false, lastUpdated: Date.now(), unreadCount: 1, + mentionedUs: false, isSelected: false, isTyping: false, }, @@ -84,6 +88,7 @@ describe('state/selectors/conversations', () => { isMe: false, lastUpdated: Date.now(), unreadCount: 1, + mentionedUs: false, isSelected: false, isTyping: false, },