diff --git a/app/assets/javascripts/app/router.js b/app/assets/javascripts/app/router.js
index 160123505..cda3ec98d 100644
--- a/app/assets/javascripts/app/router.js
+++ b/app/assets/javascripts/app/router.js
@@ -9,7 +9,7 @@ app.Router = Backbone.Router.extend({
"commented(/)": "stream",
"community_spotlight(/)": "spotlight",
"contacts(/)": "contacts",
- "conversations(/)": "conversations",
+ "conversations(/)(:id)(/)": "conversations",
"followed_tags(/)": "followed_tags",
"getting_started(/)": "gettingStarted",
"help(/)": "help",
@@ -93,8 +93,11 @@ app.Router = Backbone.Router.extend({
app.page = new app.pages.Contacts({stream: stream});
},
- conversations: function() {
- app.conversations = new app.views.Conversations();
+ conversations: function(id) {
+ app.conversations = app.conversations || new app.views.ConversationsInbox();
+ if (parseInt("" + id, 10)) {
+ app.conversations.renderConversation(id);
+ }
},
/* eslint-disable camelcase */
diff --git a/app/assets/javascripts/app/views/conversations_form_view.js b/app/assets/javascripts/app/views/conversations_form_view.js
index 53dbee681..ebd59f1b4 100644
--- a/app/assets/javascripts/app/views/conversations_form_view.js
+++ b/app/assets/javascripts/app/views/conversations_form_view.js
@@ -1,28 +1,24 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.ConversationsForm = Backbone.View.extend({
+ el: ".conversations-form-container",
events: {
- "keydown textarea#conversation_text" : "keyDown",
+ "keydown .conversation-message-text": "keyDown",
+ "submit #conversation-new": "onSubmitNewConversation"
},
initialize: function(opts) {
this.contacts = _.has(opts, "contacts") ? opts.contacts : null;
- if(!this.contacts || this.contacts.length === 0) {
- this.displayNoContactsMessage();
- return;
- }
this.prefill = [];
if (_.has(opts, "prefillName") && _.has(opts, "prefillValue")) {
- this.prefill = [{name : opts.prefillName,
- value : opts.prefillValue}];
+ this.prefill = [{name: opts.prefillName, value: opts.prefillValue}];
}
- this.autocompleteInput = $("#contact_autocomplete");
this.prepareAutocomplete(this.contacts);
},
prepareAutocomplete: function(data){
- this.autocompleteInput.autoSuggest(data, {
+ this.$("#contact-autocomplete").autoSuggest(data, {
selectedItemProp: "name",
searchObjProps: "name",
asHtmlID: "contact_ids",
@@ -32,20 +28,26 @@ app.views.ConversationsForm = Backbone.View.extend({
startText: '',
emptyText: Diaspora.I18n.t("no_results"),
preFill: this.prefill
- }).focus();
- $("#contact_ids").attr("aria-labelledby", "toLabel");
- },
-
- displayNoContactsMessage: function() {
- $("form#new_conversation").replaceWith(
- "
" + Diaspora.I18n.t("conversation.new.no_contacts") + "
"
- );
+ });
+ $("#contact_ids").attr("aria-labelledby", "toLabel").focus();
},
keyDown : function(evt) {
if(evt.which === Keycodes.ENTER && evt.ctrlKey) {
$(evt.target).parents("form").submit();
}
+ },
+
+ getConversationParticipants: function() {
+ return this.$("#as-values-contact_ids").val().split(",");
+ },
+
+ onSubmitNewConversation: function(evt) {
+ evt.preventDefault();
+ if (this.getConversationParticipants().length === 0) {
+ evt.stopPropagation();
+ app.flashMessages.error(Diaspora.I18n.t("conversation.create.no_recipient"));
+ }
}
});
// @license-end
diff --git a/app/assets/javascripts/app/views/conversations_inbox_view.js b/app/assets/javascripts/app/views/conversations_inbox_view.js
new file mode 100644
index 000000000..97e31f5c3
--- /dev/null
+++ b/app/assets/javascripts/app/views/conversations_inbox_view.js
@@ -0,0 +1,82 @@
+// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
+
+app.views.ConversationsInbox = Backbone.View.extend({
+ el: "#conversations-container",
+
+ events: {
+ "click .conversation-wrapper": "displayConversation",
+ "click .new-conversation-btn": "displayNewConversation"
+ },
+
+ initialize: function() {
+ new app.views.ConversationsForm({contacts: gon.contacts});
+ this.setupConversation();
+ },
+
+ renderConversation: function(conversationId) {
+ var self = this;
+ $.ajax({
+ url: Routes.conversationRaw(conversationId),
+ dataType: "html",
+ success: function(data) {
+ self.$el.find("#conversation-new").addClass("hidden");
+ self.$el.find("#conversation-show").removeClass("hidden").html(data);
+ self.selectConversation(conversationId);
+ self.setupConversation();
+ }
+ });
+ },
+
+ selectConversation: function(conversationId) {
+ this.$el.find("#conversation-inbox .stream-element").removeClass("selected");
+ if (conversationId) {
+ this.$el.find("#conversation-inbox .stream-element[data-guid='" + conversationId + "']").addClass("selected");
+ }
+ },
+
+ displayNewConversation: function(evt) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ this.$el.find("#conversation-new").removeClass("hidden");
+ this.$el.find("#conversation-show").addClass("hidden");
+ this.selectConversation();
+ app.router.navigate(Routes.conversations());
+ },
+
+ setupConversation: function() {
+ app.helpers.timeago($(this.el));
+ $(".control-icons a").tooltip({placement: "bottom"});
+
+ var conv = $(".conversation-wrapper .stream-element.selected"),
+ cBadge = $("#conversations-link .badge");
+
+ if (conv.hasClass("unread")) {
+ var unreadCount = parseInt(conv.find(".unread-message-count").text(), 10);
+
+ if (cBadge.text() !== "") {
+ cBadge.text().replace(/\d+/, function(num) {
+ num = parseInt(num, 10) - unreadCount;
+ if (num > 0) {
+ cBadge.text(num);
+ } else {
+ cBadge.text(0).addClass("hidden");
+ }
+ });
+ }
+ conv.removeClass("unread");
+ conv.find(".unread-message-count").remove();
+
+ var pos = $("#first_unread").offset().top - 50;
+ $("html").animate({scrollTop: pos});
+ } else {
+ $("html").animate({scrollTop: 0});
+ }
+ },
+
+ displayConversation: function(evt) {
+ var $target = $(evt.target).closest(".conversation-wrapper");
+ app.router.navigate($target.data("conversation-path"), {trigger: true});
+ }
+});
+// @license-end
+
diff --git a/app/assets/javascripts/app/views/conversations_view.js b/app/assets/javascripts/app/views/conversations_view.js
deleted file mode 100644
index 524b93f6b..000000000
--- a/app/assets/javascripts/app/views/conversations_view.js
+++ /dev/null
@@ -1,59 +0,0 @@
-// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
-
-app.views.Conversations = Backbone.View.extend({
-
- el: "#conversations_container",
-
- events: {
- "keydown textarea#message_text" : "keyDown",
- "conversation:loaded" : "setupConversation"
- },
-
- initialize: function() {
- if($("#conversation_new:visible").length > 0) {
- new app.views.ConversationsForm({
- el: $("#conversation_new"),
- contacts: gon.contacts
- });
- }
- this.setupConversation();
- },
-
- setupConversation: function() {
- app.helpers.timeago($(this.el));
- $(".control-icons a").tooltip({placement: "bottom"});
-
- var conv = $(".conversation-wrapper .stream-element.selected"),
- cBadge = $("#conversations-link .badge");
-
- if(conv.hasClass("unread") ){
- var unreadCount = parseInt(conv.find(".unread-message-count").text(), 10);
-
- if(cBadge.text() !== "") {
- cBadge.text().replace(/\d+/, function(num){
- num = parseInt(num, 10) - unreadCount;
- if(num > 0) {
- cBadge.text(num);
- } else {
- cBadge.text(0).addClass("hidden");
- }
- });
- }
- conv.removeClass("unread");
- conv.find(".unread-message-count").remove();
-
- var pos = $("#first_unread").offset().top - 50;
- $("html").animate({scrollTop:pos});
- } else {
- $("html").animate({scrollTop:0});
- }
- },
-
- keyDown : function(evt) {
- if(evt.which === Keycodes.ENTER && evt.ctrlKey) {
- $(evt.target).parents("form").submit();
- }
- }
-});
-// @license-end
-
diff --git a/app/assets/javascripts/inbox.js b/app/assets/javascripts/inbox.js
deleted file mode 100644
index df8bc0219..000000000
--- a/app/assets/javascripts/inbox.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
-$(document).ready(function(){
- $(document).on('click', '.conversation-wrapper', function(){
- var conversation_path = $(this).data('conversation-path');
- $.getScript(conversation_path, function() {
- Diaspora.page.directionDetector.updateBinds();
- });
- history.pushState(null, "", conversation_path);
- return false;
- });
-
- $(window).bind("popstate", function(){
- if (/conversations\/\d+/.test(location.href)) {
- $.getScript(location.href, function() {
- Diaspora.page.directionDetector.updateBinds();
- });
- return false;
- }
- });
-});
-// @license-end
diff --git a/app/assets/javascripts/jasmine-load-all.js b/app/assets/javascripts/jasmine-load-all.js
index 37920dc40..c84835fe3 100644
--- a/app/assets/javascripts/jasmine-load-all.js
+++ b/app/assets/javascripts/jasmine-load-all.js
@@ -3,7 +3,6 @@
//= require templates
//= require main
//= require fileuploader-custom
-//= require inbox
//= require mobile/mobile
//= require jquery.autoSuggest.custom
//= require contact-list
diff --git a/app/assets/stylesheets/mobile/mobile.scss b/app/assets/stylesheets/mobile/mobile.scss
index 588ce26b8..193665b13 100644
--- a/app/assets/stylesheets/mobile/mobile.scss
+++ b/app/assets/stylesheets/mobile/mobile.scss
@@ -594,7 +594,8 @@ form#new_user.new_user input.btn {
text-shadow: 1px 1px 20px rgb(126, 240, 77);
}
-#conversation_inbox, .notifications {
+.conversation-inbox,
+.notifications {
div.pagination {
width: 100%;
margin-left: auto;
diff --git a/app/controllers/conversations_controller.rb b/app/controllers/conversations_controller.rb
index 11dccbd0a..1a09e9d9d 100644
--- a/app/controllers/conversations_controller.rb
+++ b/app/controllers/conversations_controller.rb
@@ -24,7 +24,7 @@ class ConversationsController < ApplicationController
gon.contacts = contacts_data
respond_with do |format|
- format.html
+ format.html { render "index", locals: {no_contacts: current_user.contacts.mutual.empty?} }
format.json { render json: @visibilities.map(&:conversation), status: 200 }
end
end
@@ -53,7 +53,7 @@ class ConversationsController < ApplicationController
@response[:success] = false
@response[:message] = I18n.t('conversations.create.fail')
if person_ids.blank?
- @response[:message] = I18n.t('conversations.create.no_contact')
+ @response[:message] = I18n.t("javascripts.conversation.create.no_recipient")
end
end
respond_to do |format|
@@ -64,7 +64,7 @@ class ConversationsController < ApplicationController
def show
respond_to do |format|
format.html do
- redirect_to conversations_path(:conversation_id => params[:id])
+ redirect_to conversations_path(conversation_id: params[:id])
return
end
@@ -72,7 +72,6 @@ class ConversationsController < ApplicationController
@first_unread_message_id = @conversation.first_unread_message(current_user).try(:id)
@conversation.set_read(current_user)
- format.js
format.json { render :json => @conversation, :status => 200 }
else
redirect_to conversations_path
@@ -80,6 +79,17 @@ class ConversationsController < ApplicationController
end
end
+ def raw
+ @conversation = current_user.conversations.where(id: params[:conversation_id]).first
+ if @conversation
+ @first_unread_message_id = @conversation.first_unread_message(current_user).try(:id)
+ @conversation.set_read(current_user)
+ render partial: "conversations/show", locals: {conversation: @conversation}
+ else
+ render nothing: true, status: 404
+ end
+ end
+
def new
if !params[:modal] && !session[:mobile_view] && request.format.html?
redirect_to conversations_path
diff --git a/app/views/conversations/_messages.haml b/app/views/conversations/_messages.haml
index 4b8f3f633..d41d0e574 100644
--- a/app/views/conversations/_messages.haml
+++ b/app/views/conversations/_messages.haml
@@ -6,17 +6,18 @@
.media-left
= owner_image_tag(:thumb_small)
.media-body
- = form_for [conversation, Message.new], html: {class: "control-group"} do |message|
+ = form_for [conversation, Message.new], html: {id: "response-message", class: "control-group"} do |message|
.form-group
- %label#messageLabel.sr-only{for: "message_text"}
- = t("conversations.new.message")
+ %label.sr-only#message-label{for: "response-message-text"}= t("conversations.new.message")
= message.text_area :text,
- rows: 5,
+ rows: 5,
tabindex: 1,
- class: "form-control form-group",
- aria: {labelledby: "messageLabel"}
+ id: "response-message-text",
+ class: "form-control form-group conversation-message-text",
+ aria: {labelledby: "message-label"}
= message.submit t("conversations.show.reply"),
- "data-disable-with" => t("conversations.show.replying"),
- class: "btn btn-primary pull-right", tabindex: 2
+ "data-disable-with" => t("conversations.show.replying"),
+ :class => "btn btn-primary pull-right",
+ :tabindex => 2
.clearfix
diff --git a/app/views/conversations/_new.haml b/app/views/conversations/_new.haml
index 2358b8b19..e8ab11d37 100644
--- a/app/views/conversations/_new.haml
+++ b/app/views/conversations/_new.haml
@@ -1,22 +1,25 @@
.container-fluid
- = form_for Conversation.new, html: {class: "form-horizontal form_do_not_clear"}, remote: true do |conversation|
+ = form_for Conversation.new, html: {id: "new-conversation",
+ class: "new-conversation form-horizontal form-do-not-clear"}, remote: true do |conversation|
.form-group
%label#toLabel{for: "contact_ids"}
= t(".to")
- = text_field_tag "contact_autocomplete", nil, class: "form-control"
+ = text_field_tag "contact_autocomplete", nil, id: "contact-autocomplete", class: "form-control"
.form-group
- %label#subjectLabel{for: "conversation_subject"}
+ %label#subject-label{for: "conversation-subject"}
= t(".subject")
= conversation.text_field :subject,
+ id: "conversation-subject",
class: "input-block-level form-control",
- aria: {labelledby: "subjectLabel"}
+ aria: {labelledby: "subject-label"},
+ value: "",
+ placeholder: t("conversations.new.subject_default")
.form-group
- %label#messageLabel.sr-only{for: "conversation_text"}
- = t(".message")
- = text_area_tag "conversation[text]",
- "",
- rows: 5,
- class: "input-block-level form-control",
- aria: {labelledby: "messageLabel"}
+ %label.sr-only#message-label{for: "new-message-text"} = t(".message")
+ = text_area_tag "conversation[text]", "",
+ rows: 5,
+ id: "new-message-text",
+ class: "conversation-message-text input-block-level form-control",
+ aria: {labelledby: "message-label"}
.form-group
- = conversation.submit t('.send'), 'data-disable-with' => t('.sending'), class: 'btn btn-primary pull-right'
+ = conversation.submit t(".send"), "data-disable-with" => t(".sending"), :class => "btn btn-primary pull-right"
diff --git a/app/views/conversations/create.js.erb b/app/views/conversations/create.js.erb
index 92ae238e2..3310fcb5b 100644
--- a/app/views/conversations/create.js.erb
+++ b/app/views/conversations/create.js.erb
@@ -1,10 +1,12 @@
var response = <%= raw @response.to_json %>;
<% if session[:mobile_view] %>
+if(response.success) {
window.location.href = "<%= conversations_path(conversation_id: @conversation.id) %>";
+}
<% else %>
if(response.success){
app.flashMessages.success(response.message);
- $("#new_conversation").removeClass('form_do_not_clear').clearForm();
+ $("#new-conversation").removeClass('form-do-not-clear').clearForm();
window.location.href = "<%= conversations_path(conversation_id: @conversation.id) %>";
} else {
app.flashMessages.error(response.message);
diff --git a/app/views/conversations/index.haml b/app/views/conversations/index.haml
index 31297c79e..c60d0d22e 100644
--- a/app/views/conversations/index.haml
+++ b/app/views/conversations/index.haml
@@ -1,41 +1,35 @@
-- content_for :head do
- = javascript_include_tag :inbox
-
- content_for :page_title do
- = t('.conversations_inbox')
+ = t(".conversations_inbox")
-.container-fluid#conversations_container
+.container-fluid#conversations-container
.row
.col-md-4
.sidebar#left_pane
.sidebar-header.clearfix#left_pane_header
.pull-right
- = link_to t(".new_conversation"), conversations_path, class: "btn btn-default"
+ = link_to t(".new_conversation"), conversations_path, class: "new-conversation-btn btn btn-default"
%h3
= t(".inbox")
- .conversation-inbox#conversation_inbox
- .stream.conversations
+ .conversation-inbox#conversation-inbox
+ .conversations-form-container.stream.conversations
- if @visibilities.count > 0
= render partial: "conversations/conversation", collection: @visibilities, as: :visibility
- else
.no-conversations
- = t('.no_messages')
+ = t(".no_messages")
.pagination-container
= will_paginate @visibilities, previous_label: "«", next_label: "»", inner_window: 1,
renderer: WillPaginate::ActionView::BootstrapLinkRenderer
-
-
.col-md-8
- - if @conversation
- .stream_container
- #conversation_show
+ .conversations-form-container.stream_container
+ #conversation-show{class: @conversation ? "" : "hidden"}
+ - if @conversation
= render 'conversations/show', conversation: @conversation
- - else
- .stream_container.hidden
- #conversation_show
- .framed-content.clearfix#conversation_new
+ #conversation-new{class: @conversation ? "framed-content clearfix hidden" : "framed-content clearfix"}
.new-conversation
- %h3.text-center
- = t("conversations.index.new_conversation")
- = render "conversations/new"
+ %h3.text-center= t("conversations.index.new_conversation")
+ - if no_contacts
+ .well.text-center= t("javascripts.conversation.new.no_contacts")
+ - else
+ = render "conversations/new"
diff --git a/app/views/conversations/index.mobile.haml b/app/views/conversations/index.mobile.haml
index 89dfd53d3..fe6eefa32 100644
--- a/app/views/conversations/index.mobile.haml
+++ b/app/views/conversations/index.mobile.haml
@@ -12,7 +12,7 @@
.stream
%p{ class: "conversation_#{name}" }= msg
-#conversation_inbox
+.conversation-inbox#conversation-inbox
.stream.conversations
- if @visibilities.count > 0
= render partial: "conversations/conversation", collection: @visibilities, as: :visibility
diff --git a/app/views/conversations/new.mobile.haml b/app/views/conversations/new.mobile.haml
index 5bc187d0d..32d328b0f 100644
--- a/app/views/conversations/new.mobile.haml
+++ b/app/views/conversations/new.mobile.haml
@@ -5,7 +5,7 @@
:javascript
$(document).ready(function () {
var data = $.parseJSON( "#{escape_javascript(@contacts_json)}" ),
- autocompleteInput = $("#contact_autocomplete");
+ autocompleteInput = $("#contact-autocomplete");
autocompleteInput.autoSuggest(data, {
selectedItemProp: "name",
diff --git a/app/views/conversations/show.js.erb b/app/views/conversations/show.js.erb
deleted file mode 100644
index ad87047bb..000000000
--- a/app/views/conversations/show.js.erb
+++ /dev/null
@@ -1,10 +0,0 @@
-if($('.stream_container').hasClass('hidden')){
- $('#conversation_new').hide();
- $('.stream_container').removeClass('hidden');
-}
-
-$('#conversation_show').html("<%= escape_javascript(render('conversations/show', :conversation => @conversation)) %>");
-
-$(".stream-element", "#conversation_inbox").removeClass('selected');
-$(".stream-element[data-guid='<%= @conversation.id %>']", "#conversation_inbox").addClass('selected');
-$('#conversation_show').trigger("conversation:loaded");
diff --git a/app/views/people/contacts.haml b/app/views/people/contacts.haml
index 489f0eb17..3eeddb083 100644
--- a/app/views/people/contacts.haml
+++ b/app/views/people/contacts.haml
@@ -27,7 +27,7 @@
id: 'mentionModal'
-if @contact
- #new_conversation_pane
+ .conversations-form-container#new_conversation_pane
= render 'shared/modal',
path: new_conversation_path(:contact_id => @contact.id, name: @contact.person.name, modal: true),
title: t('conversations.index.new_conversation'),
diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml
index f98d9e467..81bcfe05d 100644
--- a/config/locales/diaspora/en.yml
+++ b/config/locales/diaspora/en.yml
@@ -274,6 +274,7 @@ en:
new_conversation: "New conversation"
no_messages: "No messages"
inbox: "Inbox"
+ no_contacts: "You need to add some contacts before you can start a conversation"
show:
reply: "Reply"
replying: "Replying..."
@@ -290,7 +291,6 @@ en:
create:
sent: "Message sent"
fail: "Invalid message"
- no_contact: "Hey, you need to add the contact first!"
new_conversation:
fail: "Invalid message"
destroy:
diff --git a/config/locales/javascript/javascript.en.yml b/config/locales/javascript/javascript.en.yml
index 081057195..2f1f7362c 100644
--- a/config/locales/javascript/javascript.en.yml
+++ b/config/locales/javascript/javascript.en.yml
@@ -240,6 +240,8 @@ en:
posts: "Posts"
conversation:
+ create:
+ no_recipient: "Hey, you need to add a recipient first!"
new:
no_contacts: "You need to add some contacts before you can start a conversation."
diff --git a/config/routes.rb b/config/routes.rb
index 09fa750bb..4e7b9d498 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -77,6 +77,7 @@ Diaspora::Application.routes.draw do
resources :conversations, except: %i(edit update destroy) do
resources :messages, only: %i(create)
delete 'visibility' => 'conversation_visibilities#destroy'
+ get "raw"
end
resources :notifications, :only => [:index, :update] do
diff --git a/features/desktop/conversations.feature b/features/desktop/conversations.feature
index 2f152b8ef..2fcc54c2f 100644
--- a/features/desktop/conversations.feature
+++ b/features/desktop/conversations.feature
@@ -18,10 +18,10 @@ Feature: private conversations
Scenario: send a message
When I sign in as "bob@bob.bob"
And I send a message with subject "Greetings" and text "hello, alice!" to "Alice Awesome"
- Then I should see "Greetings" within "#conversation_inbox"
- And I should see "Greetings" within "#conversation_show"
- And I should see "less than a minute ago" within "#conversation_inbox"
- And I should see "less than a minute ago" within "#conversation_show"
+ Then I should see "Greetings" within "#conversation-inbox"
+ And I should see "Greetings" within "#conversation-show"
+ And I should see "less than a minute ago" within "#conversation-inbox"
+ And I should see "less than a minute ago" within "#conversation-show"
And I should see "Alice Awesome" as a participant
And "Alice Awesome" should be part of active conversation
And I should see "hello, alice!" within ".stream_container"
@@ -34,8 +34,8 @@ Feature: private conversations
Scenario: send a message using keyboard shortcuts
When I sign in as "bob@bob.bob"
And I send a message with subject "Greetings" and text "hello, alice!" to "Alice Awesome" using keyboard shortcuts
- Then I should see "Greetings" within "#conversation_inbox"
- And I should see "Greetings" within "#conversation_show"
+ Then I should see "Greetings" within "#conversation-inbox"
+ And I should see "Greetings" within "#conversation-show"
And "Alice Awesome" should be part of active conversation
And I should see "hello, alice!" within ".stream_container"
When I reply with "hey, how you doing?" using keyboard shortcuts
@@ -47,9 +47,9 @@ Feature: private conversations
Scenario: delete a conversation
When I sign in as "bob@bob.bob"
And I send a message with subject "Greetings" and text "hello, alice!" to "Alice Awesome"
- Then I should see "Greetings" within "#conversation_inbox"
+ Then I should see "Greetings" within "#conversation-inbox"
When I click on selector ".hide_conversation"
- Then I should not see "Greetings" within "#conversation_inbox"
+ Then I should not see "Greetings" within "#conversation-inbox"
When I sign in as "alice@alice.alice"
Then I should have 1 unread private message
And I should have 1 email delivery
diff --git a/features/step_definitions/conversations_steps.rb b/features/step_definitions/conversations_steps.rb
index d47c36bf7..1e7bace8b 100644
--- a/features/step_definitions/conversations_steps.rb
+++ b/features/step_definitions/conversations_steps.rb
@@ -14,38 +14,38 @@ end
Then /^I send a message with subject "([^"]*)" and text "([^"]*)" to "([^"]*)"$/ do |subject, text, person|
step %(I am on the conversations page)
- within("#conversation_new", match: :first) do
+ within("#new-conversation", match: :first) do
step %(I fill in "contact_autocomplete" with "#{person}")
step %(I press the first ".as-result-item" within ".as-results")
- step %(I fill in "conversation_subject" with "#{subject}")
- step %(I fill in "conversation_text" with "#{text}")
+ step %(I fill in "conversation-subject" with "#{subject}")
+ step %(I fill in "new-message-text" with "#{text}")
step %(I press "Send")
end
end
Then /^I send a message with subject "([^"]*)" and text "([^"]*)" to "([^"]*)" using keyboard shortcuts$/ do |subject, text, person|
step %(I am on the conversations page)
- within("#conversation_new", match: :first) do
+ within("#new-conversation", match: :first) do
step %(I fill in "contact_autocomplete" with "#{person}")
step %(I press the first ".as-result-item" within ".as-results")
- step %(I fill in "conversation_subject" with "#{subject}")
- step %(I fill in "conversation_text" with "#{text}")
- find("#conversation_text").native.send_key %i(Ctrl Return)
+ step %(I fill in "conversation-subject" with "#{subject}")
+ step %(I fill in "new-message-text" with "#{text}")
+ find("#new-message-text").native.send_key %i(Ctrl Return)
end
end
When /^I reply with "([^"]*)"$/ do |text|
step %(I am on the conversations page)
step %(I press the first ".conversation" within ".conversations")
- step %(I fill in "message_text" with "#{text}")
+ step %(I fill in "response-message-text" with "#{text}")
step %(I press "Reply")
end
When /^I reply with "([^"]*)" using keyboard shortcuts$/ do |text|
step %(I am on the conversations page)
step %(I press the first ".conversation" within ".conversations")
- step %(I fill in "message_text" with "#{text}")
- find("#message_text").native.send_key %i(Ctrl Return)
+ step %(I fill in "response-message-text" with "#{text}")
+ find("#response-message-text").native.send_key %i(Ctrl Return)
end
Then /^I send a mobile message with subject "([^"]*)" and text "([^"]*)" to "([^"]*)"$/ do |subject, text, person|
@@ -53,8 +53,8 @@ Then /^I send a mobile message with subject "([^"]*)" and text "([^"]*)" to "([^
step %(I follow "New conversation")
step %(I fill in "contact_autocomplete" with "#{person}")
step %(I press the first ".as-result-item" within ".as-results")
- step %(I fill in "conversation_subject" with "#{subject}")
- step %(I fill in "conversation_text" with "#{text}")
+ step %(I fill in "conversation-subject" with "#{subject}")
+ step %(I fill in "new-message-text" with "#{text}")
step %(I press "Send")
end
diff --git a/spec/controllers/conversations_controller_spec.rb b/spec/controllers/conversations_controller_spec.rb
index 2dd8681c9..5ba05c98f 100644
--- a/spec/controllers/conversations_controller_spec.rb
+++ b/spec/controllers/conversations_controller_spec.rb
@@ -259,7 +259,7 @@ describe ConversationsController, :type => :controller do
it 'should set response with success to false and message to fail due to no contact' do
post :create, @hash
expect(assigns[:response][:success]).to eq(false)
- expect(assigns[:response][:message]).to eq(I18n.t('conversations.create.no_contact'))
+ expect(assigns[:response][:message]).to eq(I18n.t("javascripts.conversation.create.no_recipient"))
end
end
@@ -300,12 +300,6 @@ describe ConversationsController, :type => :controller do
@conversation = Conversation.create(hash)
end
- it 'succeeds with js' do
- xhr :get, :show, :id => @conversation.id, :format => :js
- expect(response).to be_success
- expect(assigns[:conversation]).to eq(@conversation)
- end
-
it 'succeeds with json' do
get :show, :id => @conversation.id, :format => :json
expect(response).to be_success
@@ -318,4 +312,26 @@ describe ConversationsController, :type => :controller do
expect(response).to redirect_to(conversations_path(:conversation_id => @conversation.id))
end
end
+
+ describe "#raw" do
+ before do
+ hash = {
+ author: alice.person,
+ participant_ids: [alice.contacts.first.person.id, alice.person.id],
+ subject: "not spam",
+ messages_attributes: [{author: alice.person, text: "cool stuff"}]
+ }
+ @conversation = Conversation.create(hash)
+ end
+
+ it "returns html of conversation" do
+ get :raw, conversation_id: @conversation.id
+ expect(response).to render_template(partial: "show", locals: {conversation: @conversation})
+ end
+
+ it "returns 404 when requesting non-existant conversation" do
+ get :raw, conversation_id: -1
+ expect(response).to have_http_status(404)
+ end
+ end
end
diff --git a/spec/javascripts/app/router_spec.js b/spec/javascripts/app/router_spec.js
index 3b185a5f2..640441dc3 100644
--- a/spec/javascripts/app/router_spec.js
+++ b/spec/javascripts/app/router_spec.js
@@ -80,6 +80,30 @@ describe('app.Router', function () {
});
});
+ describe("conversations", function() {
+ beforeEach(function() {
+ this.router = new app.Router();
+ });
+
+ it("doesn't do anything if no conversation id is passed", function() {
+ spyOn(app.views.ConversationsInbox.prototype, "renderConversation");
+ this.router.conversations();
+ expect(app.views.ConversationsInbox.prototype.renderConversation).not.toHaveBeenCalled();
+ });
+
+ it("doesn't do anything if id is not a readable number", function() {
+ spyOn(app.views.ConversationsInbox.prototype, "renderConversation");
+ this.router.conversations("yolo");
+ expect(app.views.ConversationsInbox.prototype.renderConversation).not.toHaveBeenCalled();
+ });
+
+ it("renders the conversation if id is a readable number", function() {
+ spyOn(app.views.ConversationsInbox.prototype, "renderConversation");
+ this.router.conversations("12");
+ expect(app.views.ConversationsInbox.prototype.renderConversation).toHaveBeenCalledWith("12");
+ });
+ });
+
describe("stream", function() {
it("calls _initializeStreamView", function() {
spyOn(app.router, "_initializeStreamView");
diff --git a/spec/javascripts/app/views/conversations_form_view_spec.js b/spec/javascripts/app/views/conversations_form_view_spec.js
new file mode 100644
index 000000000..79305115a
--- /dev/null
+++ b/spec/javascripts/app/views/conversations_form_view_spec.js
@@ -0,0 +1,110 @@
+describe("app.views.ConversationsForm", function() {
+ describe("keyDown", function() {
+ beforeEach(function() {
+ this.submitCallback = jasmine.createSpy().and.returnValue(false);
+ spec.loadFixture("conversations_read");
+ new app.views.ConversationsForm();
+ });
+
+ context("on new message form", function() {
+ beforeEach(function() {
+ $("#conversation-new").removeClass("hidden");
+ $("#conversation-show").addClass("hidden");
+ });
+
+ it("should submit the form with ctrl+enter", function() {
+ $("#new-conversation").submit(this.submitCallback);
+ var e = $.Event("keydown", {which: Keycodes.ENTER, ctrlKey: true});
+ $("#new-message-text").trigger(e);
+ expect(this.submitCallback).toHaveBeenCalled();
+ });
+
+ it("shouldn't submit the form without the ctrl key", function() {
+ $("#new-conversation").submit(this.submitCallback);
+ var e = $.Event("keydown", {which: Keycodes.ENTER, ctrlKey: false});
+ $("#new-message-text").trigger(e);
+ expect(this.submitCallback).not.toHaveBeenCalled();
+ });
+ });
+
+ context("on response message form", function() {
+ beforeEach(function() {
+ $("#conversation-new").addClass("hidden");
+ $("#conversation-show").removeClass("hidden");
+ });
+
+ it("should submit the form with ctrl+enter", function() {
+ $("#response-message").submit(this.submitCallback);
+ var e = $.Event("keydown", {which: Keycodes.ENTER, ctrlKey: true});
+ $("#response-message-text").trigger(e);
+ expect(this.submitCallback).toHaveBeenCalled();
+ });
+
+ it("shouldn't submit the form without the ctrl key", function() {
+ $("#response-message").submit(this.submitCallback);
+ var e = $.Event("keydown", {which: Keycodes.ENTER, ctrlKey: false});
+ $("#response-message-text").trigger(e);
+ expect(this.submitCallback).not.toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe("onSubmitNewConversation", function() {
+ beforeEach(function() {
+ spec.loadFixture("conversations_read");
+ $("#conversation-new").removeClass("hidden");
+ $("#conversation-show").addClass("hidden");
+ spyOn(app.views.ConversationsForm.prototype, "onSubmitNewConversation").and.callThrough();
+ this.target = new app.views.ConversationsForm();
+ });
+
+ it("onSubmitNewConversation is called when submitting the conversation form", function() {
+ spyOn(app.views.ConversationsForm.prototype, "getConversationParticipants").and.returnValue([]);
+ $("#conversation-new").trigger("submit");
+
+ expect(app.views.ConversationsForm.prototype.onSubmitNewConversation).toHaveBeenCalled();
+ });
+
+ it("does not submit a conversation with no recipient", function() {
+ spyOn(app.views.ConversationsForm.prototype, "getConversationParticipants").and.returnValue([]);
+ var event = jasmine.createSpyObj("event", ["preventDefault", "stopPropagation"]);
+
+ this.target.onSubmitNewConversation(event);
+
+ expect(event.preventDefault).toHaveBeenCalled();
+ expect(event.stopPropagation).toHaveBeenCalled();
+ });
+
+ it("submits a conversation with recipients", function() {
+ spyOn(app.views.ConversationsForm.prototype, "getConversationParticipants").and.returnValue([1]);
+ var event = jasmine.createSpyObj("event", ["preventDefault", "stopPropagation"]);
+
+ this.target.onSubmitNewConversation(event);
+
+ expect(event.preventDefault).toHaveBeenCalled();
+ expect(event.stopPropagation).not.toHaveBeenCalled();
+ });
+
+ it("flashes an error message when submitting a conversation with no recipient", function() {
+ spyOn(app.views.FlashMessages.prototype, "error");
+ spyOn(app.views.ConversationsForm.prototype, "getConversationParticipants").and.returnValue([]);
+ var event = jasmine.createSpyObj("event", ["preventDefault", "stopPropagation"]);
+
+ this.target.onSubmitNewConversation(event);
+
+ expect(app.views.FlashMessages.prototype.error)
+ .toHaveBeenCalledWith(Diaspora.I18n.t("conversation.create.no_recipient"));
+ });
+
+ it("does not flash an error message when submitting a conversation with recipients", function() {
+ spyOn(app.views.FlashMessages.prototype, "error");
+ spyOn(app.views.ConversationsForm.prototype, "getConversationParticipants").and.returnValue([1]);
+ var event = jasmine.createSpyObj("event", ["preventDefault", "stopPropagation"]);
+
+ this.target.onSubmitNewConversation(event);
+
+ expect(app.views.FlashMessages.prototype.error).not
+ .toHaveBeenCalledWith(Diaspora.I18n.t("conversation.create.no_recipient"));
+ });
+ });
+});
diff --git a/spec/javascripts/app/views/conversations_inbox_view_spec.js b/spec/javascripts/app/views/conversations_inbox_view_spec.js
new file mode 100644
index 000000000..122c889c1
--- /dev/null
+++ b/spec/javascripts/app/views/conversations_inbox_view_spec.js
@@ -0,0 +1,164 @@
+describe("app.views.ConversationsInbox", function() {
+ describe("initialize", function() {
+ beforeEach(function() {
+ spec.loadFixture("conversations_read");
+ $("#conversation-new").removeClass("hidden");
+ $("#conversation-show").addClass("hidden");
+ });
+
+ it("initializes the conversations form", function() {
+ spyOn(app.views.ConversationsForm.prototype, "initialize");
+ new app.views.ConversationsInbox();
+ expect(app.views.ConversationsForm.prototype.initialize).toHaveBeenCalled();
+ });
+
+ it("call setupConversation", function() {
+ spyOn(app.views.ConversationsInbox.prototype, "setupConversation");
+ new app.views.ConversationsInbox();
+ expect(app.views.ConversationsInbox.prototype.setupConversation).toHaveBeenCalled();
+ });
+ });
+
+ describe("renderConversation", function() {
+ beforeEach(function() {
+ spec.loadFixture("conversations_read");
+ $("#conversation-new").removeClass("hidden");
+ $("#conversation-show").addClass("hidden");
+ var conversations = $("#conversation-inbox .stream-element");
+ conversations.removeClass("selected");
+ this.conversationId = conversations.first().data("guid");
+ this.target = new app.views.ConversationsInbox();
+ });
+
+ it("renders conversation of given id", function() {
+ spyOn($, "ajax").and.callThrough();
+ spyOn(app.views.ConversationsInbox.prototype, "selectConversation");
+ spyOn(app.views.ConversationsInbox.prototype, "setupConversation");
+ this.target.renderConversation(this.conversationId);
+ jasmine.Ajax.requests.mostRecent().respondWith({
+ status: 200,
+ responseText: ""
+ });
+
+ expect($.ajax).toHaveBeenCalled();
+ expect(jasmine.Ajax.requests.mostRecent().url).toBe("/conversations/" + this.conversationId + "/raw");
+ expect(app.views.ConversationsInbox.prototype.selectConversation).toHaveBeenCalledWith(this.conversationId);
+ expect(app.views.ConversationsInbox.prototype.setupConversation).toHaveBeenCalled();
+ expect($("#conversation-new")).toHaveClass("hidden");
+ expect($("#conversation-show")).not.toHaveClass("hidden");
+ expect($("#conversation-show #fake-conversation-content").length).toBe(1);
+ });
+ });
+
+ describe("selectConversation", function() {
+ beforeEach(function() {
+ spec.loadFixture("conversations_read");
+ this.conversationId = $("#conversation-inbox .stream-element").first().data("guid");
+ this.target = new app.views.ConversationsInbox();
+ $("#conversation-inbox .stream-element").addClass("selected");
+ });
+
+ it("unselects every conversation if called with no parameters", function() {
+ expect($("#conversation-inbox .stream-element.selected").length).not.toBe(0);
+ this.target.selectConversation();
+ expect($("#conversation-inbox .stream-element.selected").length).toBe(0);
+ });
+
+ it("selects the given conversation", function() {
+ expect($("#conversation-inbox .stream-element.selected").length).not.toBe(1);
+ this.target.selectConversation(this.conversationId);
+ expect($("#conversation-inbox .stream-element.selected").length).toBe(1);
+ expect($("#conversation-inbox .stream-element.selected").data("guid")).toBe(this.conversationId);
+ });
+ });
+
+ describe("displayNewConversation", function() {
+ beforeEach(function() {
+ spec.loadFixture("conversations_read");
+ $("#conversation-new").addClass("hidden");
+ $("#conversation-show").removeClass("hidden");
+ spyOn(app.views.ConversationsInbox.prototype, "selectConversation");
+ new app.views.ConversationsInbox();
+ });
+
+ it("displays the new conversation panel", function() {
+ $(".new-conversation-btn").click();
+
+ expect(app.views.ConversationsInbox.prototype.selectConversation).toHaveBeenCalledWith();
+ expect($("#conversation-new")).not.toHaveClass("hidden");
+ expect($("#conversation-show")).toHaveClass("hidden");
+ expect(window.location.pathname).toBe("/conversations");
+ });
+ });
+
+ describe("setupConversation", function() {
+ context("for unread conversations", function() {
+ beforeEach(function() {
+ spec.loadFixture("conversations_unread");
+ // select second conversation that is still unread
+ $(".conversation-wrapper > .conversation.selected").removeClass("selected");
+ $(".conversation-wrapper > .conversation.unread").addClass("selected");
+ });
+
+ it("removes the unread class from the conversation", function() {
+ expect($(".conversation-wrapper > .conversation.selected")).toHaveClass("unread");
+ new app.views.ConversationsInbox();
+ expect($(".conversation-wrapper > .conversation.selected")).not.toHaveClass("unread");
+ });
+
+ it("removes the unread message counter from the conversation", function() {
+ expect($(".conversation-wrapper > .conversation.selected .unread-message-count").length).toEqual(1);
+ new app.views.ConversationsInbox();
+ expect($(".conversation-wrapper > .conversation.selected .unread-message-count").length).toEqual(0);
+ });
+
+ it("decreases the unread message count in the header", function() {
+ var badge = "";
+ $("header").append(badge);
+ expect($("#conversations-link .badge").text().trim()).toEqual("3");
+ expect($(".conversation-wrapper > .conversation .unread-message-count").text().trim()).toEqual("1");
+ new app.views.ConversationsInbox();
+ expect($("#conversations-link .badge").text().trim()).toEqual("2");
+ });
+
+ it("removes the badge in the header if there are no unread messages left", function() {
+ var badge = "";
+ $("header").append(badge);
+ expect($("#conversations-link .badge").text().trim()).toEqual("1");
+ expect($(".conversation-wrapper > .conversation.selected .unread-message-count").text().trim()).toEqual("1");
+ new app.views.ConversationsInbox();
+ expect($("#conversations-link .badge").text().trim()).toEqual("0");
+ expect($("#conversations-link .badge")).toHaveClass("hidden");
+ });
+ });
+
+ context("for read conversations", function() {
+ beforeEach(function() {
+ spec.loadFixture("conversations_read");
+ });
+
+ it("does not change the badge in the header", function() {
+ var badge = "";
+ $("header").append(badge);
+ expect($("#conversations-link .badge").text().trim()).toEqual("3");
+ new app.views.ConversationsInbox();
+ expect($("#conversations-link .badge").text().trim()).toEqual("3");
+ });
+ });
+ });
+
+ describe("displayConversation", function() {
+ beforeEach(function() {
+ spyOn(app.router, "navigate");
+ spec.loadFixture("conversations_read");
+ new app.views.ConversationsInbox();
+ });
+
+ it("calls app.router.navigate with correct parameters", function() {
+ var conversationEl = $(".conversation-wrapper").first();
+ var conversationPath = conversationEl.data("conversation-path");
+ conversationEl.children().first().click();
+ expect(app.router.navigate).toHaveBeenCalledWith(conversationPath, {trigger: true});
+ });
+ });
+});
diff --git a/spec/javascripts/app/views/conversations_view_spec.js b/spec/javascripts/app/views/conversations_view_spec.js
deleted file mode 100644
index ae5d62e10..000000000
--- a/spec/javascripts/app/views/conversations_view_spec.js
+++ /dev/null
@@ -1,79 +0,0 @@
-describe("app.views.Conversations", function(){
- describe("setupConversation", function() {
- context("for unread conversations", function() {
- beforeEach(function() {
- spec.loadFixture("conversations_unread");
- // select second conversation that is still unread
- $(".conversation-wrapper > .conversation.selected").removeClass("selected");
- $(".conversation-wrapper > .conversation.unread").addClass("selected");
- });
-
- it("removes the unread class from the conversation", function() {
- expect($(".conversation-wrapper > .conversation.selected")).toHaveClass("unread");
- new app.views.Conversations();
- expect($(".conversation-wrapper > .conversation.selected")).not.toHaveClass("unread");
- });
-
- it("removes the unread message counter from the conversation", function() {
- expect($(".conversation-wrapper > .conversation.selected .unread-message-count").length).toEqual(1);
- new app.views.Conversations();
- expect($(".conversation-wrapper > .conversation.selected .unread-message-count").length).toEqual(0);
- });
-
- it("decreases the unread message count in the header", function() {
- var badge = "";
- $("header").append(badge);
- expect($("#conversations-link .badge").text().trim()).toEqual("3");
- expect($(".conversation-wrapper > .conversation .unread-message-count").text().trim()).toEqual("1");
- new app.views.Conversations();
- expect($("#conversations-link .badge").text().trim()).toEqual("2");
- });
-
- it("removes the badge in the header if there are no unread messages left", function() {
- var badge = "";
- $("header").append(badge);
- expect($("#conversations-link .badge").text().trim()).toEqual("1");
- expect($(".conversation-wrapper > .conversation.selected .unread-message-count").text().trim()).toEqual("1");
- new app.views.Conversations();
- expect($("#conversations-link .badge").text().trim()).toEqual("0");
- expect($("#conversations-link .badge")).toHaveClass("hidden");
- });
- });
-
- context("for read conversations", function() {
- beforeEach(function() {
- spec.loadFixture("conversations_read");
- });
-
- it("does not change the badge in the header", function() {
- var badge = "";
- $("header").append(badge);
- expect($("#conversations-link .badge").text().trim()).toEqual("3");
- new app.views.Conversations();
- expect($("#conversations-link .badge").text().trim()).toEqual("3");
- });
- });
- });
-
- describe("keyDown", function(){
- beforeEach(function() {
- this.submitCallback = jasmine.createSpy().and.returnValue(false);
- spec.loadFixture("conversations_read");
- new app.views.Conversations();
- });
-
- it("should submit the form with ctrl+enter", function(){
- $("form#new_message").submit(this.submitCallback);
- var e = $.Event("keydown", { which: Keycodes.ENTER, ctrlKey: true });
- $("textarea#message_text").trigger(e);
- expect(this.submitCallback).toHaveBeenCalled();
- });
-
- it("shouldn't submit the form without the ctrl key", function(){
- $("form#new_message").submit(this.submitCallback);
- var e = $.Event("keydown", { which: Keycodes.ENTER, ctrlKey: false });
- $("textarea#message_text").trigger(e);
- expect(this.submitCallback).not.toHaveBeenCalled();
- });
- });
-});