diff --git a/js/models/conversations.js b/js/models/conversations.js index 09f993fcd..2c8c0f7b8 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -92,6 +92,8 @@ conversation: this, }); + this.pendingFriendRequest = false; + this.messageCollection.on('change:errors', this.handleMessageError, this); this.messageCollection.on('send-error', this.onMessageError, this); @@ -202,6 +204,32 @@ title: this.getTitle(), }; }, + // This function sets `pendingFriendRequest` variable in memory + async updatePendingFriendRequests() { + const pendingFriendRequest = await this.hasPendingFriendRequests(); + // Only update if we have different values + if (this.pendingFriendRequest !== pendingFriendRequest) { + this.pendingFriendRequest = pendingFriendRequest; + // trigger an update + this.trigger('change'); + } + }, + // This goes through all our message history and finds a friend request + // But this is not a concurrent operation and thus `updatePendingFriendRequests` is used + async hasPendingFriendRequests() { + // Go through the messages and check for any pending friend requests + const messages = await window.Signal.Data.getMessagesByConversation( + this.id, + { MessageCollection: Whisper.MessageCollection } + ); + + for (let i = 0; i < messages.models.length; ++i) { + const message = messages.models[i]; + if (message.isFriendRequest() && message.attributes.status === 'pending') return true; + } + + return false; + }, getPropsForListItem() { const result = { ...this.format(), @@ -210,7 +238,7 @@ lastUpdated: this.get('timestamp'), unreadCount: this.get('unreadCount') || 0, isSelected: this.isSelected, - + showFriendRequestIndicator: this.pendingFriendRequest, lastMessage: { status: this.lastMessageStatus, text: this.lastMessage, @@ -566,8 +594,7 @@ ); }, // This will add a message which will allow the user to reply to a friend request - // TODO: Maybe add callbacks for accept and decline? - async addFriendRequest(body, type = 'incoming') { + async addFriendRequest(body, status = 'pending', type = 'incoming') { if (this.isMe()) { window.log.info( 'refusing to send friend request to ourselves' @@ -584,6 +611,17 @@ lastMessage ); + this.lastMessageStatus = 'sending'; + + this.set({ + active_at: Date.now(), + timestamp: Date.now(), + }); + + await window.Signal.Data.updateConversation(this.id, this.attributes, { + Conversation: Whisper.Conversation, + }); + const timestamp = Date.now(); const message = { conversationId: this.id, @@ -593,7 +631,7 @@ unread: 1, from: this.id, to: this.ourNumber, - status: 'pending', + status, requestType: type, body, }; @@ -601,7 +639,7 @@ const id = await window.Signal.Data.saveMessage(message, { Message: Whisper.Message, }); - + this.trigger( 'newmessage', new Whisper.Message({ @@ -902,6 +940,9 @@ return; } + // Update our friend indicator + this.updatePendingFriendRequests(); + const messages = await window.Signal.Data.getMessagesByConversation( this.id, { limit: 1, MessageCollection: Whisper.MessageCollection } diff --git a/js/views/app_view.js b/js/views/app_view.js index c094d5e07..aa0a8c0ae 100644 --- a/js/views/app_view.js +++ b/js/views/app_view.js @@ -182,10 +182,8 @@ const controller = window.ConversationController; const conversation = await controller.getOrCreateAndWait(pubKey, 'private'); if (conversation) { - conversation.addFriendRequest(message, 'incoming'); + conversation.addFriendRequest(message); } - - this.openConversation(conversation); }, }); })(); diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index 53d6cab44..79de1bf0d 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -837,7 +837,6 @@ MessageReceiver.prototype.extend({ }); }, promptUserToAcceptFriendRequest(pubKey, message) { - // pubKey = pubKey.slice(0, 30) + '...'; window.Whisper.events.trigger('showFriendRequest', { pubKey, message, @@ -846,6 +845,15 @@ MessageReceiver.prototype.extend({ // A handler function for when a friend request is accepted or declined onFriendRequestUpdate(pubKey, message) { if (!message || !message.requestType || !message.status) return; + + // Update the conversation + const conversation = ConversationController.get(pubKey); + if (conversation) { + // Update the conversation friend request indicator + conversation.updatePendingFriendRequests(); + } + + // Send our own prekeys as a response if (message.requestType === 'incoming' && message.status === 'accepted') { libloki.sendEmptyMessageWithPreKeys(pubKey); } diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index 8951f6570..cd0a2c0c7 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -1796,6 +1796,11 @@ border-left: 4px solid $color-signal-blue; } +.module-conversation-list-item--has-friend-request { + padding-left: 12px; + border-left: 4px solid $color-conversation-indigo; +} + .module-conversation-list-item--is-selected { background-color: $color-gray-05; } diff --git a/stylesheets/_theme_dark.scss b/stylesheets/_theme_dark.scss index defec00e9..c29bb6312 100644 --- a/stylesheets/_theme_dark.scss +++ b/stylesheets/_theme_dark.scss @@ -1261,6 +1261,10 @@ body.dark-theme { border-left: 4px solid $color-signal-blue; } + .module-conversation-list-item--has-friend-request { + border-left: 4px solid $color-conversation-indigo; + } + .module-conversation-list-item--is-selected { background-color: $color-dark-70; } diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index deeb5dbfe..a2e9cf644 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -23,6 +23,7 @@ interface Props { status: 'sending' | 'sent' | 'delivered' | 'read' | 'error'; text: string; }; + showFriendRequestIndicator?: boolean; i18n: Localizer; onClick?: () => void; @@ -156,7 +157,7 @@ export class ConversationListItem extends React.Component { } public render() { - const { unreadCount, onClick, isSelected } = this.props; + const { unreadCount, onClick, isSelected, showFriendRequestIndicator } = this.props; return (
{ className={classNames( 'module-conversation-list-item', unreadCount > 0 ? 'module-conversation-list-item--has-unread' : null, - isSelected ? 'module-conversation-list-item--is-selected' : null + isSelected ? 'module-conversation-list-item--is-selected' : null, + showFriendRequestIndicator ? 'module-conversation-list-item--has-friend-request' : null )} > {this.renderAvatar()}