(function() { 'use strict'; window.Whisper = window.Whisper || {}; var ContactView = Whisper.View.extend({ className: 'contact-detail', templateName: 'contact-detail', initialize: function(options) { this.listenBack = options.listenBack; this.resetPanel = options.resetPanel; this.message = options.message; var newIdentity = i18n('newIdentity'); this.errors = _.map(options.errors, function(error) { if (error.name === 'OutgoingIdentityKeyError') { error.message = newIdentity; } return error; }); this.outgoingKeyError = _.find(this.errors, function(error) { return error.name === 'OutgoingIdentityKeyError'; }); }, events: { click: 'onClick', }, onClick: function() { if (this.outgoingKeyError) { var view = new Whisper.IdentityKeySendErrorPanelView({ model: this.model, listenBack: this.listenBack, resetPanel: this.resetPanel, }); this.listenTo(view, 'send-anyway', this.onSendAnyway); view.render(); this.listenBack(view); view.$('.cancel').focus(); } }, forceSend: function() { this.model .updateVerified() .then( function() { if (this.model.isUnverified()) { return this.model.setVerifiedDefault(); } }.bind(this) ) .then( function() { return this.model.isUntrusted(); }.bind(this) ) .then( function(untrusted) { if (untrusted) { return this.model.setApproved(); } }.bind(this) ) .then( function() { this.message.resend(this.outgoingKeyError.number); }.bind(this) ); }, onSendAnyway: function() { if (this.outgoingKeyError) { this.forceSend(); } }, render_attributes: function() { var showButton = Boolean(this.outgoingKeyError); return { status: this.message.getStatus(this.model.id), name: this.model.getTitle(), avatar: this.model.getAvatar(), errors: this.errors, showErrorButton: showButton, errorButtonLabel: i18n('view'), }; }, }); Whisper.MessageDetailView = Whisper.View.extend({ className: 'message-detail panel', templateName: 'message-detail', initialize: function(options) { this.listenBack = options.listenBack; this.resetPanel = options.resetPanel; this.view = new Whisper.MessageView({ model: this.model }); this.view.render(); this.conversation = options.conversation; this.listenTo(this.model, 'change', this.render); }, events: { 'click button.delete': 'onDelete', }, onDelete: function() { var dialog = new Whisper.ConfirmationDialogView({ message: i18n('deleteWarning'), okText: i18n('delete'), resolve: function() { this.model.destroy(); this.resetPanel(); }.bind(this), }); this.$el.prepend(dialog.el); dialog.focusCancel(); }, getContacts: function() { // Return the set of models to be rendered in this view var ids; if (this.model.isIncoming()) { ids = [this.model.get('source')]; } else if (this.model.isOutgoing()) { ids = this.model.get('recipients'); if (!ids) { // older messages have no recipients field // use the current set of recipients ids = this.conversation.getRecipients(); } } return Promise.all( ids.map(function(number) { return ConversationController.getOrCreateAndWait(number, 'private'); }) ); }, renderContact: function(contact) { var view = new ContactView({ model: contact, errors: this.grouped[contact.id], listenBack: this.listenBack, resetPanel: this.resetPanel, message: this.model, }).render(); this.$('.contacts').append(view.el); }, render: function() { var errorsWithoutNumber = _.reject(this.model.get('errors'), function( error ) { return Boolean(error.number); }); this.$el.html( Mustache.render(_.result(this, 'template', ''), { sent_at: moment(this.model.get('sent_at')).format('LLLL'), received_at: this.model.isIncoming() ? moment(this.model.get('received_at')).format('LLLL') : null, tofrom: this.model.isIncoming() ? i18n('from') : i18n('to'), errors: errorsWithoutNumber, title: i18n('messageDetail'), sent: i18n('sent'), received: i18n('received'), errorLabel: i18n('error'), deleteLabel: i18n('deleteMessage'), }) ); this.view.$el.prependTo(this.$('.message-container')); this.grouped = _.groupBy(this.model.get('errors'), 'number'); this.getContacts().then( function(contacts) { _.sortBy( contacts, function(c) { var prefix = this.grouped[c.id] ? '0' : '1'; // this prefix ensures that contacts with errors are listed first; // otherwise it's alphabetical return prefix + c.getTitle(); }.bind(this) ).forEach(this.renderContact.bind(this)); }.bind(this) ); }, }); })();