From d4d0d05adf4314ed99845cc82ea8d7f0d6065f56 Mon Sep 17 00:00:00 2001 From: sachaaaaa Date: Thu, 18 Oct 2018 09:33:07 +1100 Subject: [PATCH 1/3] Lock text input while waiting for friend request reply --- background.html | 8 ++--- js/models/conversations.js | 49 +++++++++++++++++++++++++++++++ js/views/conversation_view.js | 6 ++++ libtextsecure/outgoing_message.js | 3 +- stylesheets/_conversation.scss | 2 +- 5 files changed, 62 insertions(+), 6 deletions(-) diff --git a/background.html b/background.html index 8af81e984..4e2c58282 100644 --- a/background.html +++ b/background.html @@ -129,16 +129,16 @@
- - + +
- +
{{ android-length-warning }}
- +
diff --git a/js/models/conversations.js b/js/models/conversations.js index afd7e73ee..9f2ee7562 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -77,6 +77,7 @@ unreadCount: 0, verified: textsecure.storage.protocol.VerifiedStatus.DEFAULT, keyExchangeCompleted: false, + friendRequestStatus: { allowSending: true, unlockTimestamp: null } }; }, @@ -141,6 +142,10 @@ this.on('read', this.updateAndMerge); this.on('expiration-change', this.updateAndMerge); this.on('expired', this.onExpired); + + setTimeout(() => { + this.setFriendRequestTimer(); + }, 0); }, isMe() { @@ -420,6 +425,50 @@ this.set({ keyExchangeCompleted: completed }); }, + getFriendRequestStatus() { + return this.get('friendRequestStatus'); + }, + shouldDisableInputs() { + const status = this.getFriendRequestStatus(); + if (!status) { + return false; + } + return !status.allowSending; + }, + setFriendRequestTimer() { + const friendRequestStatus = this.getFriendRequestStatus(); + if (friendRequestStatus) { + if (!friendRequestStatus.allowSending) { + const delay = Math.max(friendRequestStatus.unlockTimestamp - Date.now(), 0); + setTimeout(() => { + this.friendRequestTimedOut(); + }, delay); + } + } + }, + friendRequestTimedOut() { + let friendRequestStatus = this.getFriendRequestStatus(); + friendRequestStatus.allowSending = true; + this.save({ friendRequestStatus }) + this.trigger('disable:input', false); + }, + friendRequestSent() { + const friendRequestLockDuration = 72; // hours + + let friendRequestStatus = this.getFriendRequestStatus(); + if (!friendRequestStatus) { + friendRequestStatus = {}; + } + + friendRequestStatus.allowSending = false; + const delayMs = 100 * 1000 ;//(60 * 60 * 1000 * friendRequestLockDuration); + friendRequestStatus.unlockTimestamp = Date.now() + delayMs; + this.trigger('disable:input', true); + + setTimeout(() => { this.friendRequestTimedOut() }, delayMs); + + this.save({ friendRequestStatus }) + }, isUnverified() { if (this.isPrivate()) { const verified = this.get('verified'); diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index b26a6bbc6..b7fcc3d14 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -70,6 +70,7 @@ template: $('#conversation').html(), render_attributes() { return { + 'disable-inputs': this.model.shouldDisableInputs(), 'send-message': i18n('sendMessage'), 'android-length-warning': i18n('androidMessageLengthWarning'), }; @@ -80,6 +81,7 @@ this.listenTo(this.model, 'newmessage', this.addMessage); this.listenTo(this.model, 'opened', this.onOpened); this.listenTo(this.model, 'prune', this.onPrune); + this.listenTo(this.model, 'disable:input', this.onDisableInput); this.listenTo( this.model.messageCollection, 'show-identity', @@ -277,6 +279,10 @@ } }, + onDisableInput(disable) { + this.$('button.emoji, button.microphone, button.paperclip, .send-message').attr('disabled', disable); + }, + unload(reason) { window.log.info( 'unloading conversation', diff --git a/libtextsecure/outgoing_message.js b/libtextsecure/outgoing_message.js index 665975f1b..0be0a2a77 100644 --- a/libtextsecure/outgoing_message.js +++ b/libtextsecure/outgoing_message.js @@ -368,15 +368,16 @@ OutgoingMessage.prototype = { return this.getStaleDeviceIdsForNumber(number).then(updateDevices => this.getKeysForNumber(number, updateDevices) .then(async (keysFound) => { + const conversation = ConversationController.get(number); let attachPrekeys = false; if (!keysFound) { log.info("Fallback encryption enabled"); + conversation.friendRequestSent(); this.fallBackEncryption = true; attachPrekeys = true; } else { try { - const conversation = ConversationController.get(number); attachPrekeys = !conversation.isKeyExchangeCompleted(); } catch(e) { // do nothing diff --git a/stylesheets/_conversation.scss b/stylesheets/_conversation.scss index 74094937a..7ffdcfd45 100644 --- a/stylesheets/_conversation.scss +++ b/stylesheets/_conversation.scss @@ -283,7 +283,7 @@ font-family: inherit; &[disabled='disabled'] { - background: transparent; + background: $color-light-35; } } .capture-audio { From 5602f4bfff69295cb2f30c9460f8ea289c5674bb Mon Sep 17 00:00:00 2001 From: sachaaaaa Date: Thu, 18 Oct 2018 14:19:04 +1100 Subject: [PATCH 2/3] Change placeholder in discussion --- _locales/en/messages.json | 8 ++++++++ js/models/conversations.js | 23 +++++++++++++++-------- js/views/conversation_view.js | 28 ++++++++++++++++++++++++++-- libtextsecure/outgoing_message.js | 2 +- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index c9b9e7c2a..10e36976d 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -816,6 +816,14 @@ "message": "Send a message", "description": "Placeholder text in the message entry field" }, + "sendMessageDisabled": { + "message": "Waiting for friend request approval", + "description": "Placeholder text in the message entry field when it is disabled while we are waiting for a friend request approval" + }, + "sendMessageFriendRequest": { + "message": "Hi there! This is !", + "description": "Placeholder text in the message entry field when it is the first message sent to that contact" + }, "groupMembers": { "message": "Group members" }, diff --git a/js/models/conversations.js b/js/models/conversations.js index 9f2ee7562..33aadb89d 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -428,12 +428,12 @@ getFriendRequestStatus() { return this.get('friendRequestStatus'); }, - shouldDisableInputs() { - const status = this.getFriendRequestStatus(); - if (!status) { + waitingForFriendRequestApproval() { + const friendRequestStatus = this.getFriendRequestStatus(); + if (!friendRequestStatus) { return false; } - return !status.allowSending; + return !friendRequestStatus.allowSending; }, setFriendRequestTimer() { const friendRequestStatus = this.getFriendRequestStatus(); @@ -441,18 +441,24 @@ if (!friendRequestStatus.allowSending) { const delay = Math.max(friendRequestStatus.unlockTimestamp - Date.now(), 0); setTimeout(() => { - this.friendRequestTimedOut(); + this.onFriendRequestTimedOut(); }, delay); } } }, - friendRequestTimedOut() { + onFriendRequestAccepted() { + this.save({ friendRequestStatus: null }) + this.trigger('disable:input', false); + this.trigger('change:placeholder', 'chat'); + }, + onFriendRequestTimedOut() { let friendRequestStatus = this.getFriendRequestStatus(); friendRequestStatus.allowSending = true; this.save({ friendRequestStatus }) this.trigger('disable:input', false); + this.trigger('change:placeholder', 'friend-request'); }, - friendRequestSent() { + onFriendRequestSent() { const friendRequestLockDuration = 72; // hours let friendRequestStatus = this.getFriendRequestStatus(); @@ -464,8 +470,9 @@ const delayMs = 100 * 1000 ;//(60 * 60 * 1000 * friendRequestLockDuration); friendRequestStatus.unlockTimestamp = Date.now() + delayMs; this.trigger('disable:input', true); + this.trigger('change:placeholder', 'disabled'); - setTimeout(() => { this.friendRequestTimedOut() }, delayMs); + setTimeout(() => { this.onFriendRequestTimedOut() }, delayMs); this.save({ friendRequestStatus }) }, diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index b7fcc3d14..05d609e78 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -69,9 +69,16 @@ }, template: $('#conversation').html(), render_attributes() { + let sendMessagePlaceholder = 'sendMessageFriendRequest'; + const sendDisabled = this.model.waitingForFriendRequestApproval(); + if (sendDisabled) { + sendMessagePlaceholder = 'sendMessageDisabled'; + } else if (this.model.getFriendRequestStatus() === null) { + sendMessagePlaceholder = 'sendMessage'; + } return { - 'disable-inputs': this.model.shouldDisableInputs(), - 'send-message': i18n('sendMessage'), + 'disable-inputs': sendDisabled, + 'send-message': i18n(sendMessagePlaceholder), 'android-length-warning': i18n('androidMessageLengthWarning'), }; }, @@ -82,6 +89,7 @@ this.listenTo(this.model, 'opened', this.onOpened); this.listenTo(this.model, 'prune', this.onPrune); this.listenTo(this.model, 'disable:input', this.onDisableInput); + this.listenTo(this.model, 'change:placeholder', this.onChangePlaceholder); this.listenTo( this.model.messageCollection, 'show-identity', @@ -283,6 +291,22 @@ this.$('button.emoji, button.microphone, button.paperclip, .send-message').attr('disabled', disable); }, + onChangePlaceholder(type) { + let placeholder; + switch (type) { + case 'friend-request': + placeholder = i18n('sendMessageFriendRequest'); + break; + case 'disabled': + placeholder = i18n('sendMessageDisabled'); + break; + default: + placeholder = i18n('sendMessage'); + break; + } + this.$messageField.attr('placeholder', placeholder); + }, + unload(reason) { window.log.info( 'unloading conversation', diff --git a/libtextsecure/outgoing_message.js b/libtextsecure/outgoing_message.js index 0be0a2a77..3303d7be2 100644 --- a/libtextsecure/outgoing_message.js +++ b/libtextsecure/outgoing_message.js @@ -373,7 +373,7 @@ OutgoingMessage.prototype = { if (!keysFound) { log.info("Fallback encryption enabled"); - conversation.friendRequestSent(); + conversation.onFriendRequestSent(); this.fallBackEncryption = true; attachPrekeys = true; } else { From fb020f88ea34f97529aacec2b94ead49392818d8 Mon Sep 17 00:00:00 2001 From: sachaaaaa Date: Tue, 23 Oct 2018 15:35:44 +1100 Subject: [PATCH 3/3] call 'onFriendRequestSent' only after the message was successfully sent --- libtextsecure/outgoing_message.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/libtextsecure/outgoing_message.js b/libtextsecure/outgoing_message.js index 3303d7be2..1c05f848a 100644 --- a/libtextsecure/outgoing_message.js +++ b/libtextsecure/outgoing_message.js @@ -365,18 +365,22 @@ OutgoingMessage.prototype = { }, sendToNumber(number) { + let conversation; + try { + conversation = ConversationController.get(number); + } catch(e) { + } + return this.getStaleDeviceIdsForNumber(number).then(updateDevices => this.getKeysForNumber(number, updateDevices) .then(async (keysFound) => { - const conversation = ConversationController.get(number); let attachPrekeys = false; if (!keysFound) { log.info("Fallback encryption enabled"); - conversation.onFriendRequestSent(); this.fallBackEncryption = true; attachPrekeys = true; - } else { + } else if (conversation) { try { attachPrekeys = !conversation.isKeyExchangeCompleted(); } catch(e) { @@ -389,6 +393,11 @@ OutgoingMessage.prototype = { this.message.preKeyBundleMessage = await libloki.getPreKeyBundleForNumber(number); } }).then(this.reloadDevicesAndSend(number, true)) + .then(() => { + if (this.fallBackEncryption && conversation) { + conversation.onFriendRequestSent(); + } + }) .catch(error => { if (error.message === 'Identity key changed') { // eslint-disable-next-line no-param-reassign