Refactor contacts page using pagination
This commit is contained in:
parent
d022e51a0c
commit
993f3d5ab0
11 changed files with 374 additions and 168 deletions
|
|
@ -8,7 +8,6 @@ app.pages.Contacts = Backbone.View.extend({
|
||||||
"click #contacts_visibility_toggle" : "toggleContactVisibility",
|
"click #contacts_visibility_toggle" : "toggleContactVisibility",
|
||||||
"click #chat_privilege_toggle" : "toggleChatPrivilege",
|
"click #chat_privilege_toggle" : "toggleChatPrivilege",
|
||||||
"click #change_aspect_name" : "showAspectNameForm",
|
"click #change_aspect_name" : "showAspectNameForm",
|
||||||
"keyup #contact_list_search" : "searchContactList",
|
|
||||||
"click .conversation_button": "showMessageModal",
|
"click .conversation_button": "showMessageModal",
|
||||||
"click #invitations-button": "showInvitationsModal"
|
"click #invitations-button": "showInvitationsModal"
|
||||||
},
|
},
|
||||||
|
|
@ -79,10 +78,6 @@ app.pages.Contacts = Backbone.View.extend({
|
||||||
$(".header > h3").show();
|
$(".header > h3").show();
|
||||||
},
|
},
|
||||||
|
|
||||||
searchContactList: function(e) {
|
|
||||||
this.stream.search($(e.target).val());
|
|
||||||
},
|
|
||||||
|
|
||||||
showMessageModal: function(){
|
showMessageModal: function(){
|
||||||
app.helpers.showModal("#conversationModal");
|
app.helpers.showModal("#conversationModal");
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -81,13 +81,14 @@ app.Router = Backbone.Router.extend({
|
||||||
).render();
|
).render();
|
||||||
},
|
},
|
||||||
|
|
||||||
contacts: function() {
|
contacts: function(params) {
|
||||||
app.aspect = new app.models.Aspect(gon.preloads.aspect);
|
app.aspect = new app.models.Aspect(gon.preloads.aspect);
|
||||||
this._loadContacts();
|
this._loadContacts();
|
||||||
|
|
||||||
var stream = new app.views.ContactStream({
|
var stream = new app.views.ContactStream({
|
||||||
collection: app.contacts,
|
collection: app.contacts,
|
||||||
el: $(".stream.contacts #contact_stream")
|
el: $(".stream.contacts #contact_stream"),
|
||||||
|
urlParams: params
|
||||||
});
|
});
|
||||||
|
|
||||||
app.page = new app.pages.Contacts({stream: stream});
|
app.page = new app.pages.Contacts({stream: stream});
|
||||||
|
|
|
||||||
|
|
@ -1,77 +1,79 @@
|
||||||
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
|
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
|
||||||
|
|
||||||
app.views.ContactStream = Backbone.View.extend({
|
app.views.ContactStream = Backbone.View.extend({
|
||||||
initialize: function() {
|
initialize: function(opts) {
|
||||||
this.itemCount = 0;
|
this.page = 1;
|
||||||
this.perPage = 25;
|
|
||||||
this.query = '';
|
|
||||||
this.resultList = this.collection.toArray();
|
|
||||||
var throttledScroll = _.throttle(_.bind(this.infScroll, this), 200);
|
var throttledScroll = _.throttle(_.bind(this.infScroll, this), 200);
|
||||||
$(window).scroll(throttledScroll);
|
$(window).scroll(throttledScroll);
|
||||||
this.on('renderContacts', this.renderContacts, this);
|
this.on("fetchContacts", this.fetchContacts, this);
|
||||||
|
this.urlParams = opts.urlParams;
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
if( _.isEmpty(this.resultList) ) {
|
this.fetchContacts();
|
||||||
var content = document.createDocumentFragment();
|
|
||||||
content = '<div id="no_contacts" class="well">' +
|
|
||||||
' <h4>' +
|
|
||||||
Diaspora.I18n.t('contacts.search_no_results') +
|
|
||||||
' </h4>' +
|
|
||||||
'</div>';
|
|
||||||
this.$el.html(content);
|
|
||||||
} else {
|
|
||||||
this.$el.html('');
|
|
||||||
this.renderContacts();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renderContacts: function() {
|
fetchContacts: function() {
|
||||||
this.$el.addClass("loading");
|
this.$el.addClass("loading");
|
||||||
var content = document.createDocumentFragment();
|
$("#paginate .loader").removeClass("hidden");
|
||||||
_.rest(_.first(this.resultList , this.itemCount + this.perPage), this.itemCount).forEach( function(item) {
|
$.ajax(this._fetchUrl(), {
|
||||||
var view = new app.views.Contact({model: item});
|
context: this
|
||||||
content.appendChild(view.render().el);
|
}).success(function(response) {
|
||||||
|
if (response.length === 0) {
|
||||||
|
this.onEmptyResponse();
|
||||||
|
} else {
|
||||||
|
this.appendContactViews(response);
|
||||||
|
this.page++;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
var size = _.size(this.resultList);
|
_fetchUrl: function() {
|
||||||
if( this.itemCount + this.perPage >= size ){
|
var url = Routes.contacts({format: "json", page: this.page});
|
||||||
this.itemCount = size;
|
if (this.urlParams) {
|
||||||
this.off('renderContacts');
|
url += "&" + this.urlParams;
|
||||||
} else {
|
|
||||||
this.itemCount += this.perPage;
|
|
||||||
}
|
}
|
||||||
|
return url;
|
||||||
|
},
|
||||||
|
|
||||||
|
onEmptyResponse: function() {
|
||||||
|
if (this.collection.length === 0) {
|
||||||
|
var content = document.createDocumentFragment();
|
||||||
|
content = "<div id='no_contacts' class='well'>" +
|
||||||
|
" <h4>" +
|
||||||
|
Diaspora.I18n.t("contacts.search_no_results") +
|
||||||
|
" </h4>" +
|
||||||
|
"</div>";
|
||||||
|
this.$el.html(content);
|
||||||
|
}
|
||||||
|
this.off("fetchContacts");
|
||||||
|
this.$el.removeClass("loading");
|
||||||
|
$("#paginate .loader").addClass("hidden");
|
||||||
|
},
|
||||||
|
|
||||||
|
appendContactViews: function(contacts) {
|
||||||
|
var content = document.createDocumentFragment();
|
||||||
|
contacts.forEach(function(contactData) {
|
||||||
|
var contact = new app.models.Contact(contactData);
|
||||||
|
this.collection.add(contact);
|
||||||
|
var view = new app.views.Contact({model: contact});
|
||||||
|
content.appendChild(view.render().el);
|
||||||
|
}.bind(this));
|
||||||
this.$el.append(content);
|
this.$el.append(content);
|
||||||
this.$el.removeClass("loading");
|
this.$el.removeClass("loading");
|
||||||
},
|
$("#paginate .loader").addClass("hidden");
|
||||||
|
|
||||||
search: function(query) {
|
|
||||||
query = query.trim();
|
|
||||||
if( query || this.query ) {
|
|
||||||
this.off('renderContacts');
|
|
||||||
this.on('renderContacts', this.renderContacts, this);
|
|
||||||
this.itemCount = 0;
|
|
||||||
if( query ) {
|
|
||||||
this.query = query;
|
|
||||||
var regex = new RegExp(query,'i');
|
|
||||||
this.resultList = this.collection.filter(function(contact) {
|
|
||||||
return regex.test(contact.get('person').name) ||
|
|
||||||
regex.test(contact.get('person').diaspora_id);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.resultList = this.collection.toArray();
|
|
||||||
this.query = '';
|
|
||||||
}
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
infScroll: function() {
|
infScroll: function() {
|
||||||
if( this.$el.hasClass('loading') ) return;
|
if (this.$el.hasClass("loading")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var distanceTop = $(window).height() + $(window).scrollTop(),
|
var distanceTop = $(window).height() + $(window).scrollTop(),
|
||||||
distanceBottom = $(document).height() - distanceTop;
|
distanceBottom = $(document).height() - distanceTop;
|
||||||
if(distanceBottom < 300) this.trigger('renderContacts');
|
if (distanceBottom < 300) {
|
||||||
|
this.trigger("fetchContacts");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// @license-end
|
// @license-end
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,13 @@
|
||||||
margin-bottom: 11px;
|
margin-bottom: 11px;
|
||||||
margin-top: 11px;
|
margin-top: 11px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.aspect-controls { margin: 7px -10px 7px 0; }
|
.stream.contacts .aspect-controls {
|
||||||
|
margin-bottom: 7px;
|
||||||
|
margin-left: 30px;
|
||||||
|
margin-right: -10px;
|
||||||
|
margin-top: 7px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,7 +28,7 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#contact_list_search {
|
#contact_list_search {
|
||||||
margin: 11px 30px 0 0;
|
margin: 11px 0 0;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
&:focus { width: 250px; }
|
&:focus { width: 250px; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,13 @@ class ContactsController < ApplicationController
|
||||||
# Used by the mobile site
|
# Used by the mobile site
|
||||||
format.mobile { set_up_contacts_mobile }
|
format.mobile { set_up_contacts_mobile }
|
||||||
|
|
||||||
# Used for mentions in the publisher
|
# Used for mentions in the publisher and pagination on the contacts page
|
||||||
format.json {
|
format.json {
|
||||||
@people = Person.search(params[:q], current_user, only_contacts: true).limit(15)
|
@people = if params[:q].present?
|
||||||
|
Person.search(params[:q], current_user, only_contacts: true).limit(15)
|
||||||
|
else
|
||||||
|
set_up_contacts_json
|
||||||
|
end
|
||||||
render json: @people
|
render json: @people
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
@ -30,30 +34,54 @@ class ContactsController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_up_contacts
|
def set_up_contacts
|
||||||
type = params[:set].presence
|
if params[:a_id].present?
|
||||||
type ||= "by_aspect" if params[:a_id].present?
|
@aspect = current_user.aspects.find(params[:a_id])
|
||||||
type ||= "receiving"
|
gon.preloads[:aspect] = AspectPresenter.new(@aspect).as_json
|
||||||
|
end
|
||||||
|
@contacts_size = current_user.contacts.size
|
||||||
|
end
|
||||||
|
|
||||||
@contacts = contacts_by_type(type)
|
def set_up_contacts_json
|
||||||
@contacts_size = @contacts.length
|
type = params[:set].presence
|
||||||
gon.preloads[:contacts] = @contacts.map {|c| ContactPresenter.new(c, current_user).full_hash_with_person }
|
if params[:a_id].present?
|
||||||
|
type ||= "by_aspect"
|
||||||
|
@aspect = current_user.aspects.find(params[:a_id])
|
||||||
|
end
|
||||||
|
type ||= "receiving"
|
||||||
|
contacts_by_type(type).paginate(page: params[:page], per_page: 25)
|
||||||
|
.map {|c| ContactPresenter.new(c, current_user).full_hash_with_person }
|
||||||
end
|
end
|
||||||
|
|
||||||
def contacts_by_type(type)
|
def contacts_by_type(type)
|
||||||
case type
|
order = ["profiles.first_name ASC", "profiles.last_name ASC", "profiles.diaspora_handle ASC"]
|
||||||
|
contacts = case type
|
||||||
when "all"
|
when "all"
|
||||||
|
order.unshift "receiving DESC"
|
||||||
current_user.contacts
|
current_user.contacts
|
||||||
when "only_sharing"
|
when "only_sharing"
|
||||||
current_user.contacts.only_sharing
|
current_user.contacts.only_sharing
|
||||||
when "receiving"
|
when "receiving"
|
||||||
current_user.contacts.receiving
|
current_user.contacts.receiving
|
||||||
when "by_aspect"
|
when "by_aspect"
|
||||||
@aspect = current_user.aspects.find(params[:a_id])
|
order.unshift "contact_id IS NOT NULL DESC"
|
||||||
gon.preloads[:aspect] = AspectPresenter.new(@aspect).as_json
|
contacts_by_aspect(@aspect.id)
|
||||||
current_user.contacts
|
|
||||||
else
|
else
|
||||||
raise ArgumentError, "unknown type #{type}"
|
raise ArgumentError, "unknown type #{type}"
|
||||||
end
|
end
|
||||||
|
contacts.includes(person: :profile)
|
||||||
|
.order(order)
|
||||||
|
end
|
||||||
|
|
||||||
|
def contacts_by_aspect(aspect_id)
|
||||||
|
contacts = current_user.contacts.arel_table
|
||||||
|
aspect_memberships = AspectMembership.arel_table
|
||||||
|
current_user.contacts.joins(
|
||||||
|
contacts.outer_join(aspect_memberships).on(
|
||||||
|
aspect_memberships[:aspect_id].eq(aspect_id).and(
|
||||||
|
aspect_memberships[:contact_id].eq(contacts[:id])
|
||||||
|
)
|
||||||
|
).join_sources
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_up_contacts_mobile
|
def set_up_contacts_mobile
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,11 @@
|
||||||
= link_to @aspect, method: "delete", data: { confirm: t("aspects.edit.confirm_remove_aspect") }, class: "delete contacts_button", id: "delete_aspect" do
|
= link_to @aspect, method: "delete", data: { confirm: t("aspects.edit.confirm_remove_aspect") }, class: "delete contacts_button", id: "delete_aspect" do
|
||||||
%i.entypo-trash.contacts-header-icon{title: t("delete")}
|
%i.entypo-trash.contacts-header-icon{title: t("delete")}
|
||||||
.pull-right.contact-list-search
|
.pull-right.contact-list-search
|
||||||
= search_field_tag :contact_search, "", id: "contact_list_search", class: "search-query form-control", placeholder: t("contacts.index.user_search")
|
%form#contact-search-form{role: "search", method: "get", action: "/search"}
|
||||||
|
= search_field_tag :q, "",
|
||||||
|
id: "contact_list_search",
|
||||||
|
class: "search-query form-control",
|
||||||
|
placeholder: t("contacts.index.user_search")
|
||||||
%h3
|
%h3
|
||||||
%span#aspect_name
|
%span#aspect_name
|
||||||
= @aspect.name
|
= @aspect.name
|
||||||
|
|
@ -32,6 +36,13 @@
|
||||||
= aspect.submit t('aspects.edit.update'), 'data-disable-with' => t('aspects.edit.updating'), class: "btn btn-default"
|
= aspect.submit t('aspects.edit.update'), 'data-disable-with' => t('aspects.edit.updating'), class: "btn btn-default"
|
||||||
|
|
||||||
- else
|
- else
|
||||||
|
.pull-right.contact-list-search
|
||||||
|
%form#contact-search-form{role: "search", method: "get", action: "/search"}
|
||||||
|
= search_field_tag :q, "",
|
||||||
|
id: "contact_list_search",
|
||||||
|
class: "search-query form-control",
|
||||||
|
placeholder: t("contacts.index.user_search")
|
||||||
|
|
||||||
%h3
|
%h3
|
||||||
- case params["set"]
|
- case params["set"]
|
||||||
- when "only_sharing"
|
- when "only_sharing"
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@
|
||||||
.btn.btn-link{ 'data-toggle' => 'modal' }
|
.btn.btn-link{ 'data-toggle' => 'modal' }
|
||||||
= t('invitations.new.invite_someone_to_join')
|
= t('invitations.new.invite_someone_to_join')
|
||||||
|
|
||||||
|
#paginate
|
||||||
|
%span.loader.hidden
|
||||||
|
.spinner
|
||||||
|
|
||||||
-if @aspect
|
-if @aspect
|
||||||
#new_conversation_pane
|
#new_conversation_pane
|
||||||
= render 'shared/modal',
|
= render 'shared/modal',
|
||||||
|
|
|
||||||
|
|
@ -24,51 +24,97 @@ describe ContactsController, :type => :controller do
|
||||||
expect(response).to be_success
|
expect(response).to be_success
|
||||||
end
|
end
|
||||||
|
|
||||||
it "assigns contacts" do
|
it "doesn't assign contacts" do
|
||||||
get :index
|
get :index
|
||||||
contacts = assigns(:contacts)
|
contacts = assigns(:contacts)
|
||||||
expect(contacts.to_set).to eq(bob.contacts.to_set)
|
expect(contacts).to be_nil
|
||||||
end
|
|
||||||
|
|
||||||
it "shows only contacts a user is sharing with" do
|
|
||||||
contact = bob.contacts.first
|
|
||||||
contact.update_attributes(:sharing => false)
|
|
||||||
|
|
||||||
get :index
|
|
||||||
contacts = assigns(:contacts)
|
|
||||||
expect(contacts.to_set).to eq(bob.contacts.receiving.to_set)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "shows all contacts (sharing and receiving)" do
|
|
||||||
contact = bob.contacts.first
|
|
||||||
contact.update_attributes(:sharing => false)
|
|
||||||
|
|
||||||
get :index, :set => "all"
|
|
||||||
contacts = assigns(:contacts)
|
|
||||||
expect(contacts.to_set).to eq(bob.contacts.to_set)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "format json" do
|
context "format json" do
|
||||||
before do
|
context "for the contacts search" do
|
||||||
@person1 = FactoryGirl.create(:person)
|
before do
|
||||||
bob.share_with(@person1, bob.aspects.first)
|
@person1 = FactoryGirl.create(:person)
|
||||||
@person2 = FactoryGirl.create(:person)
|
bob.share_with(@person1, bob.aspects.first)
|
||||||
|
@person2 = FactoryGirl.create(:person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "succeeds" do
|
||||||
|
get :index, q: @person1.first_name, format: "json"
|
||||||
|
expect(response).to be_success
|
||||||
|
end
|
||||||
|
|
||||||
|
it "responds with json" do
|
||||||
|
get :index, q: @person1.first_name, format: "json"
|
||||||
|
expect(response.body).to eq([@person1].to_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "only returns contacts" do
|
||||||
|
get :index, q: @person2.first_name, format: "json"
|
||||||
|
expect(response.body).to eq([].to_json)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "succeeds" do
|
context "for pagination on the contacts page" do
|
||||||
get :index, q: @person1.first_name, format: "json"
|
context "without parameters" do
|
||||||
expect(response).to be_success
|
it "returns contacts" do
|
||||||
end
|
get :index, format: "json", page: "1"
|
||||||
|
contact_ids = JSON.parse(response.body).map {|c| c["id"] }
|
||||||
|
expect(contact_ids.to_set).to eq(bob.contacts.map(&:id).to_set)
|
||||||
|
end
|
||||||
|
|
||||||
it "responds with json" do
|
it "returns only contacts which are receiving (the user is sharing with them)" do
|
||||||
get :index, q: @person1.first_name, format: "json"
|
contact = bob.contacts.first
|
||||||
expect(response.body).to eq([@person1].to_json)
|
contact.update_attributes(receiving: false)
|
||||||
end
|
|
||||||
|
|
||||||
it "only returns contacts" do
|
get :index, format: "json", page: "1"
|
||||||
get :index, q: @person2.first_name, format: "json"
|
contact_ids = JSON.parse(response.body).map {|c| c["id"] }
|
||||||
expect(response.body).to eq([].to_json)
|
expect(contact_ids.to_set).to eq(bob.contacts.receiving.map(&:id).to_set)
|
||||||
|
expect(contact_ids).not_to include(contact.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "set: all" do
|
||||||
|
before do
|
||||||
|
contact = bob.contacts.first
|
||||||
|
contact.update_attributes(receiving: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns all contacts (sharing and receiving)" do
|
||||||
|
get :index, format: "json", page: "1", set: "all"
|
||||||
|
contact_ids = JSON.parse(response.body).map {|c| c["id"] }
|
||||||
|
expect(contact_ids.to_set).to eq(bob.contacts.map(&:id).to_set)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sorts contacts by receiving status" do
|
||||||
|
get :index, format: "json", page: "1", set: "all"
|
||||||
|
contact_ids = JSON.parse(response.body).map {|c| c["id"] }
|
||||||
|
expect(contact_ids).to eq(bob.contacts.order("receiving DESC").map(&:id))
|
||||||
|
expect(contact_ids.last).to eq(bob.contacts.first.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with an aspect id" do
|
||||||
|
before do
|
||||||
|
@aspect = bob.aspects.create(name: "awesome contacts")
|
||||||
|
@person = FactoryGirl.create(:person)
|
||||||
|
bob.share_with(@person, @aspect)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns all contacts" do
|
||||||
|
get :index, format: "json", a_id: @aspect.id, page: "1"
|
||||||
|
contact_ids = JSON.parse(response.body).map {|c| c["id"] }
|
||||||
|
expect(contact_ids.to_set).to eq(bob.contacts.map(&:id).to_set)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sorts contacts by aspect memberships" do
|
||||||
|
get :index, format: "json", a_id: @aspect.id, page: "1"
|
||||||
|
expect(JSON.parse(response.body).first["person"]["id"]).to eq(@person.id)
|
||||||
|
|
||||||
|
get :index, format: "json", a_id: bob.aspects.first.id, page: "1"
|
||||||
|
expect(JSON.parse(response.body).first["person"]["id"]).not_to eq(@person.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,11 @@ describe ContactsController, :type => :controller do
|
||||||
it "generates the aspects_manage fixture", :fixture => true do
|
it "generates the aspects_manage fixture", :fixture => true do
|
||||||
get :index, :a_id => @aspect.id
|
get :index, :a_id => @aspect.id
|
||||||
save_fixture(html_for("body"), "aspects_manage")
|
save_fixture(html_for("body"), "aspects_manage")
|
||||||
save_fixture(controller.gon.preloads[:contacts].to_json, "aspects_manage_contacts_json")
|
end
|
||||||
|
|
||||||
|
it "generates the aspects_manage_contacts_json fixture", fixture: true do
|
||||||
|
get :index, format: :json, a_id: @aspect.id, page: "1"
|
||||||
|
save_fixture(response.body, "aspects_manage_contacts_json")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "generates the contacts_json fixture", :fixture => true do
|
it "generates the contacts_json fixture", :fixture => true do
|
||||||
|
|
|
||||||
|
|
@ -88,19 +88,6 @@ describe("app.pages.Contacts", function(){
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('search contact list', function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
this.searchinput = $('#contact_list_search');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls stream.search', function() {
|
|
||||||
this.view.stream.search = jasmine.createSpy();
|
|
||||||
this.searchinput.val("Username");
|
|
||||||
this.searchinput.trigger('keyup');
|
|
||||||
expect(this.view.stream.search).toHaveBeenCalledWith("Username");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("updateBadgeCount", function() {
|
describe("updateBadgeCount", function() {
|
||||||
it("increases the badge count of an aspect", function() {
|
it("increases the badge count of an aspect", function() {
|
||||||
var aspect = $("#aspect_nav .aspect").eq(0);
|
var aspect = $("#aspect_nav .aspect").eq(0);
|
||||||
|
|
|
||||||
|
|
@ -2,76 +2,199 @@ describe("app.views.ContactStream", function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
|
loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
|
||||||
spec.loadFixture("aspects_manage");
|
spec.loadFixture("aspects_manage");
|
||||||
this.contacts = new app.collections.Contacts($.parseJSON(spec.readFixture("contacts_json")));
|
this.contacts = new app.collections.Contacts();
|
||||||
app.aspect = new app.models.Aspect(this.contacts.first().get('aspect_memberships')[0].aspect);
|
this.contactsData = $.parseJSON(spec.readFixture("contacts_json"));
|
||||||
|
app.aspect = new app.models.Aspect(this.contactsData[0].aspect_memberships[0].aspect);
|
||||||
this.view = new app.views.ContactStream({
|
this.view = new app.views.ContactStream({
|
||||||
collection : this.contacts,
|
collection : this.contacts,
|
||||||
el: $('.stream.contacts #contact_stream')
|
el: $(".stream.contacts #contact_stream"),
|
||||||
|
urlParams: "set=all"
|
||||||
});
|
});
|
||||||
|
|
||||||
this.view.perPage=1;
|
|
||||||
|
|
||||||
//clean the page
|
|
||||||
this.view.$el.html('');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("initialize", function() {
|
describe("initialize", function() {
|
||||||
it("binds an infinite scroll listener", function() {
|
it("binds an infinite scroll listener", function() {
|
||||||
spyOn($.fn, "scroll");
|
spyOn($.fn, "scroll");
|
||||||
new app.views.ContactStream({collection : this.contacts});
|
new app.views.ContactStream({collection: this.contacts});
|
||||||
expect($.fn.scroll).toHaveBeenCalled();
|
expect($.fn.scroll).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("binds 'fetchContacts'", function() {
|
||||||
|
spyOn(app.views.ContactStream.prototype, "fetchContacts");
|
||||||
|
this.view = new app.views.ContactStream({collection: this.contacts});
|
||||||
|
this.view.trigger("fetchContacts");
|
||||||
|
expect(app.views.ContactStream.prototype.fetchContacts).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets the current page for pagination to 1", function() {
|
||||||
|
expect(this.view.page).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets urlParams to the given value", function() {
|
||||||
|
expect(this.view.urlParams).toBe("set=all");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("search", function() {
|
describe("render", function() {
|
||||||
it("filters the contacts", function() {
|
it("calls fetchContacts", function() {
|
||||||
|
spyOn(this.view, "fetchContacts");
|
||||||
this.view.render();
|
this.view.render();
|
||||||
expect(this.view.$el.html()).toContain("alice");
|
expect(this.view.fetchContacts).toHaveBeenCalled();
|
||||||
this.view.search("eve");
|
});
|
||||||
expect(this.view.$el.html()).not.toContain("alice");
|
});
|
||||||
expect(this.view.$el.html()).toContain("eve");
|
|
||||||
|
describe("fetchContacts", function() {
|
||||||
|
it("adds the loading class", function() {
|
||||||
|
expect(this.view.$el).not.toHaveClass("loading");
|
||||||
|
this.view.fetchContacts();
|
||||||
|
expect(this.view.$el).toHaveClass("loading");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("displays the loading spinner", function() {
|
||||||
|
expect($("#paginate .loader")).toHaveClass("hidden");
|
||||||
|
this.view.fetchContacts();
|
||||||
|
expect($("#paginate .loader")).not.toHaveClass("hidden");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls $.ajax with the URL given by _fetchUrl", function() {
|
||||||
|
spyOn(this.view, "_fetchUrl").and.returnValue("/myAwesomeFetchUrl?foo=bar");
|
||||||
|
this.view.fetchContacts();
|
||||||
|
expect(jasmine.Ajax.requests.mostRecent().url).toBe("/myAwesomeFetchUrl?foo=bar");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls onEmptyResponse on an empty response", function() {
|
||||||
|
spyOn(this.view, "onEmptyResponse");
|
||||||
|
this.view.fetchContacts();
|
||||||
|
jasmine.Ajax.requests.mostRecent().respondWith({status: 200, responseText: JSON.stringify([])});
|
||||||
|
expect(this.view.onEmptyResponse).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls appendContactViews on a non-empty response", function() {
|
||||||
|
spyOn(this.view, "appendContactViews");
|
||||||
|
this.view.fetchContacts();
|
||||||
|
jasmine.Ajax.requests.mostRecent().respondWith({status: 200, responseText: JSON.stringify(this.contactsData)});
|
||||||
|
expect(this.view.appendContactViews).toHaveBeenCalledWith(this.contactsData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("increases the current page on a non-empty response", function() {
|
||||||
|
this.view.page = 42;
|
||||||
|
this.view.fetchContacts();
|
||||||
|
jasmine.Ajax.requests.mostRecent().respondWith({status: 200, responseText: JSON.stringify(this.contactsData)});
|
||||||
|
expect(this.view.page).toBe(43);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("_fetchUrl", function() {
|
||||||
|
it("returns the correct URL to fetch contacts", function() {
|
||||||
|
this.view.page = 15;
|
||||||
|
this.view.urlParams = undefined;
|
||||||
|
expect(this.view._fetchUrl()).toBe("/contacts.json?page=15");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("appends urlParams if those are set", function() {
|
||||||
|
this.view.page = 23;
|
||||||
|
expect(this.view._fetchUrl()).toBe("/contacts.json?page=23&set=all");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("onEmptyResponse", function() {
|
||||||
|
context("with an empty collection", function() {
|
||||||
|
it("adds a 'no contacts' div", function() {
|
||||||
|
this.view.onEmptyResponse();
|
||||||
|
expect(this.view.$("#no_contacts").text().trim()).toBe(Diaspora.I18n.t("contacts.search_no_results"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hides the loading spinner", function() {
|
||||||
|
this.view.$el.addClass("loading");
|
||||||
|
$("#paginate .loader").removeClass("hidden");
|
||||||
|
this.view.onEmptyResponse();
|
||||||
|
expect(this.view.$el).not.toHaveClass("loading");
|
||||||
|
expect($("#paginate .loader")).toHaveClass("hidden");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("unbinds 'fetchContacts'", function() {
|
||||||
|
spyOn(this.view, "off");
|
||||||
|
this.view.onEmptyResponse();
|
||||||
|
expect(this.view.off).toHaveBeenCalledWith("fetchContacts");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context("with a non-empty collection", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
this.view.collection.add(factory.contact());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds no 'no contacts' div", function() {
|
||||||
|
this.view.onEmptyResponse();
|
||||||
|
expect(this.view.$("#no_contacts").length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hides the loading spinner", function() {
|
||||||
|
this.view.$el.addClass("loading");
|
||||||
|
$("#paginate .loader").removeClass("hidden");
|
||||||
|
this.view.onEmptyResponse();
|
||||||
|
expect(this.view.$el).not.toHaveClass("loading");
|
||||||
|
expect($("#paginate .loader")).toHaveClass("hidden");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("unbinds 'fetchContacts'", function() {
|
||||||
|
spyOn(this.view, "off");
|
||||||
|
this.view.onEmptyResponse();
|
||||||
|
expect(this.view.off).toHaveBeenCalledWith("fetchContacts");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("appendContactViews", function() {
|
||||||
|
it("hides the loading spinner", function() {
|
||||||
|
this.view.$el.addClass("loading");
|
||||||
|
$("#paginate .loader").removeClass("hidden");
|
||||||
|
this.view.appendContactViews(this.contactsData);
|
||||||
|
expect(this.view.$el).not.toHaveClass("loading");
|
||||||
|
expect($("#paginate .loader")).toHaveClass("hidden");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds all contacts to an empty collection", function() {
|
||||||
|
expect(this.view.collection.length).toBe(0);
|
||||||
|
this.view.appendContactViews(this.contactsData);
|
||||||
|
expect(this.view.collection.length).toBe(this.contactsData.length);
|
||||||
|
expect(this.view.collection.pluck("id")).toEqual(_.pluck(this.contactsData, "id"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("appends contacts to an existing collection", function() {
|
||||||
|
this.view.collection.add(this.contactsData[0]);
|
||||||
|
expect(this.view.collection.length).toBe(1);
|
||||||
|
this.view.appendContactViews(_.rest(this.contactsData));
|
||||||
|
expect(this.view.collection.length).toBe(this.contactsData.length);
|
||||||
|
expect(this.view.collection.pluck("id")).toEqual(_.pluck(this.contactsData, "id"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders all added contacts", function() {
|
||||||
|
expect(this.view.$(".stream_element.contact").length).toBe(0);
|
||||||
|
this.view.appendContactViews(this.contactsData);
|
||||||
|
expect(this.view.$(".stream_element.contact").length).toBe(this.contactsData.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("appends contacts to an existing contact list", function() {
|
||||||
|
this.view.appendContactViews([this.contactsData[0]]);
|
||||||
|
expect(this.view.$(".stream_element.contact").length).toBe(1);
|
||||||
|
this.view.appendContactViews(_.rest(this.contactsData));
|
||||||
|
expect(this.view.$(".stream_element.contact").length).toBe(this.contactsData.length);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("infScroll", function() {
|
describe("infScroll", function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
this.view.off("renderContacts");
|
this.view.off("fetchContacts");
|
||||||
this.fn = jasmine.createSpy();
|
this.fn = jasmine.createSpy();
|
||||||
this.view.on("renderContacts", this.fn);
|
this.view.on("fetchContacts", this.fn);
|
||||||
spyOn($.fn, "height").and.returnValue(0);
|
spyOn($.fn, "height").and.returnValue(0);
|
||||||
spyOn($.fn, "scrollTop").and.returnValue(100);
|
spyOn($.fn, "scrollTop").and.returnValue(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("triggers renderContacts when the user is at the bottom of the page", function() {
|
it("triggers fetchContacts when the user is at the bottom of the page", function() {
|
||||||
this.view.infScroll();
|
this.view.infScroll();
|
||||||
expect(this.fn).toHaveBeenCalled();
|
expect(this.fn).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("render", function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
spyOn(this.view, "renderContacts");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("calls renderContacts", function() {
|
|
||||||
this.view.render();
|
|
||||||
expect(this.view.renderContacts).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("renderContacts", function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
this.view.off("renderContacts");
|
|
||||||
this.view.renderContacts();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders perPage contacts", function() {
|
|
||||||
expect(this.view.$el.find('.stream_element.contact').length).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders more contacts when called a second time", function() {
|
|
||||||
this.view.renderContacts();
|
|
||||||
expect(this.view.$el.find('.stream_element.contact').length).toBe(2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue