From 2b2c6ab040f2621493ec587231f8e999d8ab7e4a Mon Sep 17 00:00:00 2001 From: lilia Date: Wed, 28 Sep 2016 16:47:57 -0700 Subject: [PATCH] Frontend for timer updates and timer indicator --- _locales/en/messages.json | 72 +++++++++++++++++++++++++++++++++++ background.html | 9 +++++ js/expiring_messages.js | 51 +++++++++++++++++++++++++ js/models/conversations.js | 18 ++++++--- js/models/messages.js | 23 ++++++----- js/views/conversation_view.js | 54 ++++++++++++++++++++++++-- js/views/message_list_view.js | 2 +- js/views/message_view.js | 10 +++-- stylesheets/_global.scss | 16 ++++++++ stylesheets/manifest.css | 13 +++++++ 10 files changed, 242 insertions(+), 26 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 18c30414..88c46a17 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -360,5 +360,77 @@ "example": "10m" } } + }, + "timerOption_0_seconds": { + "message": "off", + "description": "Label for option to turn off message expiration in the timer menu" + }, + "timerOption_5_seconds": { + "message": "5 seconds", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_10_seconds": { + "message": "10 seconds", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_30_seconds": { + "message": "30 seconds", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_1_day": { + "message": "1 day", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_1_week": { + "message": "1 week", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "disappearingMessages": { + "message": "Disappearing messages", + "description": "Conversation menu option to enable disappearing messages" + }, + "timerOption_5_seconds_abbreviated": { + "message": "5s", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_10_seconds_abbreviated": { + "message": "10s", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_30_seconds_abbreviated": { + "message": "30s", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_1_minute_abbreviated": { + "message": "1m", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_5_minutes_abbreviated": { + "message": "5m", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_30_minutes_abbreviated": { + "message": "30m", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_1_hour_abbreviated": { + "message": "1h", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_6_hours_abbreviated": { + "message": "6h", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_12_hours_abbreviated": { + "message": "12h", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_1_day_abbreviated": { + "message": "1d", + "description": "Label for a selectable option in the message expiration timer menu" + }, + "timerOption_1_week_abbreviated": { + "message": "1w", + "description": "Label for a selectable option in the message expiration timer menu" } } diff --git a/background.html b/background.html index 2d172491..142f6941 100644 --- a/background.html +++ b/background.html @@ -75,9 +75,18 @@
  • {{ end-session }}
  • {{ verify-identity }}
  • {{/group}} +
  • {{ disappearing-messages }}
  • {{ destroy }}
  • + diff --git a/js/expiring_messages.js b/js/expiring_messages.js index 40fa5565..ab7b0ce4 100644 --- a/js/expiring_messages.js +++ b/js/expiring_messages.js @@ -11,4 +11,55 @@ this.fetchExpiring(); } }))(); + + var TimerOption = Backbone.Model.extend({ + getName: function() { + return i18n([ + 'timerOption', this.get('time'), this.get('unit'), + ].join('_')) || moment.duration(this.get('time'), this.get('unit')).humanize(); + }, + getAbbreviated: function() { + return i18n([ + 'timerOption', this.get('time'), this.get('unit'), 'abbreviated' + ].join('_')); + } + }); + Whisper.ExpirationTimerOptions = new (Backbone.Collection.extend({ + model: TimerOption, + getName: function(seconds) { + if (!seconds) { + seconds = 0; + } + var o = this.findWhere({seconds: seconds}); + if (o) { return o.getName(); } + }, + getAbbreviated: function(seconds) { + if (!seconds) { + seconds = 0; + } + var o = this.findWhere({seconds: seconds}); + if (o) { return o.getAbbreviated(); } + } + }))([ + [ 0, 'seconds' ], + [ 5, 'seconds' ], + [ 10, 'seconds' ], + [ 30, 'seconds' ], + [ 1, 'minute' ], + [ 5, 'minutes' ], + [ 30, 'minutes' ], + [ 1, 'hour' ], + [ 6, 'hours' ], + [ 12, 'hours' ], + [ 1, 'day' ], + [ 1, 'week' ], + ].map(function(o) { + var duration = moment.duration(o[0], o[1]); // 5, 'seconds' + return { + time: o[0], + unit: o[1], + seconds: duration.asSeconds() + }; + })); + })(); diff --git a/js/models/conversations.js b/js/models/conversations.js index 54d81f2f..49f8c6be 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -166,20 +166,26 @@ }.bind(this)); }, - addExpirationTimerUpdate: function(source, time) { + addExpirationTimerUpdate: function(time, source) { var now = Date.now(); + this.save({ expireTimer: time }); var message = this.messageCollection.add({ - conversationId : this.id, - type : 'expirationTimerUpdate', - sent_at : now, - received_at : now, - timerUpdate : { + conversationId : this.id, + type : 'outgoing', + sent_at : now, + received_at : now, + flags : textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE, + expirationTimerUpdate : { expireTimer : time, source : source } }); message.save(); }, + sendExpirationTimerUpdate: function(time) { + this.addExpirationTimerUpdate(time, textsecure.storage.user.getNumber()); + // todo: send. + }, isSearchable: function() { return !this.get('left') || !!this.get('lastMessage'); diff --git a/js/models/messages.js b/js/models/messages.js index 503c2146..9aea937b 100644 --- a/js/models/messages.js +++ b/js/models/messages.js @@ -71,7 +71,6 @@ if (this.isIncoming() && this.hasErrors()) { return i18n('incomingError'); } - return this.get('body'); }, getNotificationText: function() { @@ -126,15 +125,6 @@ } return c; }, - getModelForExpirationTimerUpdate: function() { - var id = this.get('timerUpdate').source; - var c = ConversationController.get(id); - if (!c) { - c = ConversationController.create({ id: id, type: 'private' }); - c.fetch(); - } - return c; - }, isOutgoing: function() { return this.get('type') === 'outgoing'; }, @@ -364,9 +354,18 @@ flags : dataMessage.flags, errors : [] }); - - if (dataMessage.expireTimer) { + if (message.isExpirationTimerUpdate()) { + message.set({ + expirationTimerUpdate: { + source : source, + expireTimer : dataMessage.expireTimer + } + }); + conversation.set({expireTimer: dataMessage.expireTimer}); + } else if (dataMessage.expireTimer) { message.set({expireTimer: dataMessage.expireTimer}); + // todo: insert an update if needed + conversation.set({expireTimer: dataMessage.expireTimer}); } var conversation_timestamp = conversation.get('timestamp'); diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index 0d211783..e3dfa685 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -16,6 +16,39 @@ } }); + var MenuView = Whisper.View.extend({ + toggleMenu: function() { + this.$('.menu-list').toggle(); + } + }); + var TimerMenuView = MenuView.extend({ + initialize: function() { + this.render(); + this.listenTo(this.model, 'change:expireTimer', this.render); + }, + events: { + 'click button': 'toggleMenu', + 'click li': 'setTimer' + }, + setTimer: function(e) { + var seconds = this.$(e.target).data().seconds; + if (seconds >= 0) { + this.model.sendExpirationTimerUpdate(seconds); + } + }, + render: function() { + var seconds = this.model.get('expireTimer'); + if (seconds) { + var s = Whisper.ExpirationTimerOptions.getAbbreviated(seconds); + this.$el.attr('data-time', s); + this.$el.show(); + } else { + this.$el.attr('data-time', null); + this.$el.hide(); + } + } + }); + Whisper.ConversationView = Whisper.View.extend({ className: function() { return [ 'conversation', this.model.get('type') ].join(' '); @@ -30,11 +63,14 @@ name: this.model.getName(), number: this.model.getNumber(), avatar: this.model.getAvatar(), + expireTimer: this.model.get('expireTimer'), 'view-members' : i18n('members'), 'end-session' : i18n('resetSession'), 'verify-identity' : i18n('verifyIdentity'), 'destroy' : i18n('deleteMessages'), - 'send-message' : i18n('sendMessage') + 'send-message' : i18n('sendMessage'), + 'disappearing-messages': i18n('disappearingMessages'), + timer_options : Whisper.ExpirationTimerOptions.models }; }, initialize: function(options) { @@ -47,6 +83,7 @@ this.listenTo(this.model.messageCollection, 'expired', this.onExpiredCollection); this.render(); + new TimerMenuView({ el: this.$('.timer-menu'), model: this.model }); emoji_util.parse(this.$('.conversation-name')); @@ -105,6 +142,7 @@ 'click .bottom-bar': 'focusMessageField', 'click .back': 'resetPanel', 'click .microphone': 'captureAudio', + 'click .disappearing-messages': 'enableDisappearingMessages', 'focus .send-message': 'focusBottomBar', 'change .file-input': 'toggleMicrophone', 'blur .send-message': 'unfocusBottomBar', @@ -113,6 +151,13 @@ 'select .message-list .entry': 'messageDetail', 'force-resize': 'forceUpdateMessageFieldSize' }, + enableDisappearingMessages: function() { + if (!this.model.get('expireTimer')) { + this.model.sendExpirationTimerUpdate( + moment.duration(1, 'day').asSeconds() + ); + } + }, toggleMicrophone: function() { if (this.$('.send-message').val().length > 0 || this.fileInput.hasFiles()) { this.$('.capture-audio').hide(); @@ -240,7 +285,10 @@ closeMenu: function(e) { if (e && !$(e.target).hasClass('hamburger')) { - this.$('.menu-list').hide(); + this.$('.conversation-menu .menu-list').hide(); + } + if (e && !$(e.target).hasClass('clock')) { + this.$('.timer-menu .menu-list').hide(); } }, @@ -255,7 +303,7 @@ }, toggleMenu: function() { - this.$('.menu-list').toggle(); + this.$('.conversation-menu .menu-list').toggle(); }, newGroupUpdate: function() { diff --git a/js/views/message_list_view.js b/js/views/message_list_view.js index 4ed79c47..e7d2cde6 100644 --- a/js/views/message_list_view.js +++ b/js/views/message_list_view.js @@ -44,7 +44,7 @@ }, addOne: function(model) { var view; - if (model.get('type') === 'expirationTimerUpdate') { + if (model.isExpirationTimerUpdate()) { view = new Whisper.ExpirationTimerUpdateView({model: model}).render(); } else { view = new this.itemView({model: model}).render(); diff --git a/js/views/message_view.js b/js/views/message_view.js index 65414e3c..5e430fd4 100644 --- a/js/views/message_view.js +++ b/js/views/message_view.js @@ -48,14 +48,16 @@ className: 'expirationTimerUpdate advisory', templateName: 'expirationTimerUpdate', initialize: function() { - this.conversation = this.model.getModelForExpirationTimerUpdate(); + this.conversation = this.model.getContact(); this.listenTo(this.conversation, 'change', this.render); }, render_attributes: function() { + var seconds = this.model.get('expirationTimerUpdate').expireTimer; return { - content: i18n('changedTheTimer', - this.conversation.getTitle(), - this.model.get('timerUpdate').time) + content: i18n('changedTheTimer', [ + this.conversation.getTitle(), + Whisper.ExpirationTimerOptions.getName(seconds) + ]) }; } }); diff --git a/stylesheets/_global.scss b/stylesheets/_global.scss index 5c51c709..3c920172 100644 --- a/stylesheets/_global.scss +++ b/stylesheets/_global.scss @@ -123,6 +123,22 @@ button.hamburger { } } +.conversation-header .timer-menu { + margin-right: 10px; + + &:before { + content: attr(data-time); + display: inline-block; + position: absolute; + bottom: -10px; + height: 10px; + width: 100%; + text-align: center; + font-size: 8px; + font-weight: bold; + } +} + .menu { position: relative; float: right; diff --git a/stylesheets/manifest.css b/stylesheets/manifest.css index 31450071..986310ac 100644 --- a/stylesheets/manifest.css +++ b/stylesheets/manifest.css @@ -126,6 +126,19 @@ button.hamburger { vertical-align: middle; display: table-cell; } +.conversation-header .timer-menu { + margin-right: 10px; } + .conversation-header .timer-menu:before { + content: attr(data-time); + display: inline-block; + position: absolute; + bottom: -10px; + height: 10px; + width: 100%; + text-align: center; + font-size: 8px; + font-weight: bold; } + .menu { position: relative; float: right; }