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;