diff --git a/images/check.png b/images/check.png new file mode 100644 index 00000000..ee846c03 Binary files /dev/null and b/images/check.png differ diff --git a/index.html b/index.html index eaadc745..14015895 100644 --- a/index.html +++ b/index.html @@ -50,6 +50,7 @@ + + diff --git a/js/views/new_conversation_view.js b/js/views/new_conversation_view.js index ff7dcc11..9922d673 100644 --- a/js/views/new_conversation_view.js +++ b/js/views/new_conversation_view.js @@ -17,90 +17,27 @@ 'use strict'; window.Whisper = window.Whisper || {}; - var ContactsTypeahead = Backbone.TypeaheadCollection.extend({ - typeaheadAttributes: [ - 'name', - 'e164_number', - 'national_number', - 'international_number' - ], - database: Whisper.Database, - storeName: 'conversations', - model: Whisper.Conversation - }); - - Whisper.ContactPillView = Whisper.View.extend({ - tagName: 'span', - className: 'recipient', - events: { - 'click .remove': 'removeModel' - }, - template: $('#contact_pill').html(), - initialize: function() { - var error = this.model.validate(this.model.attributes); - if (error) { - this.$el.addClass('error'); - } - }, - removeModel: function() { - this.$el.trigger('remove', {modelId: this.model.id}); - this.remove(); - }, - render_attributes: function() { - return { name: this.model.getTitle() }; - } - }); - - Whisper.RecipientListView = Whisper.ListView.extend({ - itemView: Whisper.ContactPillView - }); - Whisper.NewConversationView = Whisper.View.extend({ className: 'new-conversation', template: $('#new-conversation').html(), initialize: function() { this.render(); this.$group_update = this.$el.find('.new-group-update-form'); - this.$buttons = this.$el.find('.buttons'); - this.$input = this.$el.find('input.new-message'); - this.$new_contact = this.$el.find('.new-contact'); - - // Collection of contacts to match user input against - this.typeahead = new ContactsTypeahead(); - this.typeahead.fetch({ conditions: { type: 'private' } }); - - // View to display the matched contacts from typeahead - this.typeahead_view = new Whisper.ConversationListView({ - collection : new Whisper.ConversationCollection([], { - comparator: function(m) { return m.getTitle(); } - }) - }); - this.$el.find('.contacts').append(this.typeahead_view.el); - - this.initNewContact(); + this.$create = this.$el.find('.create'); + this.$input = this.$el.find('input.search'); // Group avatar file input this.avatarInput = new Whisper.FileInputView({ el: this.$el.find('.group-avatar') }); - // Collection of recipients selected for the new message - this.recipients = new Whisper.ConversationCollection([], { - comparator: false - }); - // View to display the selected recipients - this.recipients_view = new Whisper.RecipientListView({ - collection: this.recipients, - el: this.$el.find('.recipients') - }); + this.recipients_view = new Whisper.RecipientsInputView(); + this.$el.find('.scrollable').append(this.recipients_view.el); + this.listenTo(this.getRecipients(), 'add', this.updateControls); + this.listenTo(this.getRecipients(), 'remove', this.updateControls); }, events: { - 'change input.new-message': 'filterContacts', - 'keyup input.new-message': 'filterContacts', - 'select .new-contact': 'addNewRecipient', - 'select .contacts': 'addRecipient', - 'remove .recipient': 'removeRecipient', 'click .create': 'create', 'click .back': 'goBack', 'keyup': 'keyup' @@ -116,51 +53,17 @@ this.trigger('back'); }, - initNewContact: function() { - if (this.new_contact) { - this.new_contact.undelegateEvents(); - this.new_contact.$el.hide(); - } - // Creates a view to display a new contact - this.new_contact = new Whisper.ConversationListItemView({ - el: this.$new_contact, - model: new Whisper.Conversation({ - active_at: null, - type: 'private', - newContact: true - }) - }).render(); - }, - - addNewRecipient: function(e, data) { - this.recipients.add(this.new_contact.model); - this.initNewContact(); - this.resetTypeahead(); - this.updateControls(); - }, - - addRecipient: function(e, data) { - this.recipients.add(this.typeahead.remove(data.modelId)); - this.filterContacts(); - this.updateControls(); - }, - - removeRecipient: function(e, data) { - var model = this.recipients.remove(data.modelId); - if (!model.get('newContact')) { - this.typeahead.add(model); - } - this.filterContacts(); - this.updateControls(); + getRecipients: function() { + return this.recipients_view.recipients; }, updateControls: function() { - if (this.recipients.length > 0) { - this.$buttons.slideDown(); + if (this.getRecipients().length > 0) { + this.$create.show(); } else { - this.$buttons.slideUp(); + this.$create.hide(); } - if (this.recipients.length > 1) { + if (this.getRecipients().length > 1) { this.$group_update.slideDown(); } else { this.$group_update.slideUp(); @@ -178,7 +81,7 @@ return; } - if (this.recipients.length > 1) { + if (this.getRecipients().length > 1) { this.createGroup(); } else { this.createConversation(); @@ -188,7 +91,7 @@ createConversation: function() { var conversation = new Whisper.Conversation({ active_at: null, - id: this.recipients.at(0).id, + id: this.getRecipients().at(0).id, type: 'private' }); conversation.fetch().then(function() { @@ -214,7 +117,7 @@ type: 'group', name: name, avatar: avatarFiles[0], - members: this.recipients.pluck('id') + members: this.getRecipients().pluck('id') }; return textsecure.messaging.createGroup( attributes.members, attributes.name, attributes.avatar @@ -228,44 +131,11 @@ }.bind(this)); }, - resetTypeahead: function() { - this.new_contact.$el.hide(); - this.$input.val('').focus(); - this.typeahead_view.collection.reset(this.typeahead.models); - }, - reset: function() { - this.$buttons.hide(); + this.$create.hide(); this.$group_update.hide(); - this.typeahead.add( - this.recipients.filter(function(model) { - return !model.get('newContact'); - }) - ); - this.recipients.reset([]); - this.resetTypeahead(); + this.recipients_view.reset(); }, - - filterContacts: function() { - var query = this.$input.val(); - if (query.length) { - if (this.maybeNumber(query)) { - this.new_contact.model.set('id', query); - this.new_contact.render().$el.show(); - } else { - this.new_contact.$el.hide(); - } - this.typeahead_view.collection.reset( - this.typeahead.typeahead(query) - ); - } else { - this.resetTypeahead(); - } - }, - - maybeNumber: function(number) { - return number.match(/^\+?[0-9]*$/); - } }); })(); diff --git a/js/views/recipients_input_view.js b/js/views/recipients_input_view.js new file mode 100644 index 00000000..d3c0dd67 --- /dev/null +++ b/js/views/recipients_input_view.js @@ -0,0 +1,174 @@ +/* vim: ts=4:sw=4:expandtab + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ +(function () { + 'use strict'; + window.Whisper = window.Whisper || {}; + + var ContactsTypeahead = Backbone.TypeaheadCollection.extend({ + typeaheadAttributes: [ + 'name', + 'e164_number', + 'national_number', + 'international_number' + ], + database: Whisper.Database, + storeName: 'conversations', + model: Whisper.Conversation + }); + + Whisper.ContactPillView = Whisper.View.extend({ + tagName: 'span', + className: 'recipient', + events: { + 'click .remove': 'removeModel' + }, + template: $('#contact_pill').html(), + initialize: function() { + var error = this.model.validate(this.model.attributes); + if (error) { + this.$el.addClass('error'); + } + }, + removeModel: function() { + this.$el.trigger('remove', {modelId: this.model.id}); + this.remove(); + }, + render_attributes: function() { + return { name: this.model.getTitle() }; + } + }); + + Whisper.RecipientListView = Whisper.ListView.extend({ + itemView: Whisper.ContactPillView + }); + + Whisper.RecipientsInputView = Whisper.View.extend({ + className: 'recipients-input', + template: $('#recipients-input').html(), + initialize: function() { + this.render(); + this.$input = this.$el.find('input.search'); + this.$new_contact = this.$el.find('.new-contact'); + + // Collection of recipients selected for the new message + this.recipients = new Whisper.ConversationCollection([], { + comparator: false + }); + + // View to display the selected recipients + this.recipients_view = new Whisper.RecipientListView({ + collection: this.recipients, + el: this.$el.find('.recipients') + }); + + // Collection of contacts to match user input against + this.typeahead = new ContactsTypeahead(); + this.typeahead.fetch({ conditions: { type: 'private' } }); + + // View to display the matched contacts from typeahead + this.typeahead_view = new Whisper.ConversationListView({ + collection : new Whisper.ConversationCollection([], { + comparator: function(m) { return m.getTitle(); } + }) + }); + this.$el.find('.contacts').append(this.typeahead_view.el); + + this.initNewContact(); + }, + + events: { + 'change input.search': 'filterContacts', + 'keyup input.search': 'filterContacts', + 'select .new-contact': 'addNewRecipient', + 'select .contacts': 'addRecipient', + 'remove .recipient': 'removeRecipient', + }, + + filterContacts: function(e) { + var query = this.$input.val(); + if (query.length) { + if (this.maybeNumber(query)) { + this.new_contact.model.set('id', query); + this.new_contact.render().$el.show(); + } else { + this.new_contact.$el.hide(); + } + this.typeahead_view.collection.reset( + this.typeahead.typeahead(query) + ); + } else { + this.resetTypeahead(); + } + }, + + initNewContact: function() { + if (this.new_contact) { + this.new_contact.undelegateEvents(); + this.new_contact.$el.hide(); + } + // Creates a view to display a new contact + this.new_contact = new Whisper.ConversationListItemView({ + el: this.$new_contact, + model: new Whisper.Conversation({ + active_at: null, + type: 'private', + newContact: true + }) + }).render(); + }, + + addNewRecipient: function(e, data) { + this.recipients.add(this.new_contact.model); + this.initNewContact(); + this.resetTypeahead(); + }, + + addRecipient: function(e, data) { + this.recipients.add(this.typeahead.remove(data.modelId)); + this.filterContacts(); + }, + + removeRecipient: function(e, data) { + var model = this.recipients.remove(data.modelId); + if (!model.get('newContact')) { + this.typeahead.add(model); + } + this.filterContacts(); + }, + + reset: function() { + this.typeahead.add( + this.recipients.filter(function(model) { + return !model.get('newContact'); + }) + ); + this.recipients.reset([]); + this.resetTypeahead(); + }, + + resetTypeahead: function() { + this.new_contact.$el.hide(); + this.$input.val('').focus(); + this.typeahead_view.collection.reset(this.typeahead.models); + }, + + + maybeNumber: function(number) { + return number.match(/^\+?[0-9]*$/); + } + }); + +})(); diff --git a/stylesheets/_global.scss b/stylesheets/_global.scss index 6864cb94..5c01a156 100644 --- a/stylesheets/_global.scss +++ b/stylesheets/_global.scss @@ -51,7 +51,6 @@ body { width: $header-height; height: $header-height; line-height: $header-height; - margin-right: 8px; padding: 0; border: 0; outline: 0; @@ -152,3 +151,20 @@ img.emoji { margin: 0 .05em 0 .1em; vertical-align: -0.1em; } + +.new-group-update-form { + .group-avatar { + float: left; + height: 36px; + } +} + +.title-bar .check { + float: right; + background: $blue url('/images/check.png') no-repeat center center; + margin: none; + + &:hover { + background-color: $blue_l; + } +} diff --git a/stylesheets/_index.scss b/stylesheets/_index.scss index b4ec47e6..066d69c0 100644 --- a/stylesheets/_index.scss +++ b/stylesheets/_index.scss @@ -45,7 +45,7 @@ } } -input.new-message { +input.search { border: none; padding: 0; margin: 0; @@ -55,23 +55,8 @@ input.new-message { .new-conversation { .new-group-update-form { display: none; - - button.create-group { - float: right; - } - - .group-avatar { - float: left; - height: 36px; - } } - .buttons { - display: none; - } -} - -.new-conversation { .recipients-container { background-color: white; padding: 2px; diff --git a/stylesheets/manifest.css b/stylesheets/manifest.css index 6a97e83e..5438d51f 100644 --- a/stylesheets/manifest.css +++ b/stylesheets/manifest.css @@ -58,7 +58,6 @@ body { width: 36px; height: 36px; line-height: 36px; - margin-right: 8px; padding: 0; border: 0; outline: 0; } @@ -140,6 +139,17 @@ img.emoji { margin: 0 .05em 0 .1em; vertical-align: -0.1em; } +.new-group-update-form .group-avatar { + float: left; + height: 36px; } + +.title-bar .check { + float: right; + background: #2a92e7 url("/images/check.png") no-repeat center center; + margin: none; } + .title-bar .check:hover { + background-color: #a2d2f4; } + .gutter { padding: 36px 0 0; } @@ -170,7 +180,7 @@ img.emoji { .contact .number, .contact .checkbox { display: none; } -input.new-message { +input.search { border: none; padding: 0; margin: 0; @@ -178,14 +188,6 @@ input.new-message { .new-conversation .new-group-update-form { display: none; } - .new-conversation .new-group-update-form button.create-group { - float: right; } - .new-conversation .new-group-update-form .group-avatar { - float: left; - height: 36px; } -.new-conversation .buttons { - display: none; } - .new-conversation .recipients-container { background-color: white; padding: 2px;