diff --git a/app/assets/javascripts/app/pages/contacts.js b/app/assets/javascripts/app/pages/contacts.js index 9b26efcae..88eaf46f5 100644 --- a/app/assets/javascripts/app/pages/contacts.js +++ b/app/assets/javascripts/app/pages/contacts.js @@ -20,6 +20,8 @@ app.pages.Contacts = Backbone.View.extend({ $("#people_stream.contacts .header i").tooltip({"placement": "bottom"}); $(document).on("ajax:success", "form.edit_aspect", this.updateAspectName); app.events.on("aspect:create", function(){ window.location.reload() }); + app.events.on("aspect_membership:create", this.addAspectMembership, this); + app.events.on("aspect_membership:destroy", this.removeAspectMembership, this); this.aspectCreateView = new app.views.AspectCreate({ el: $("#newAspectContainer") }); this.aspectCreateView.render(); @@ -95,6 +97,26 @@ app.pages.Contacts = Backbone.View.extend({ revert: true, helper: "clone" }); + }, + + updateAspectMembershipCount: function(id, change) { + var count = parseInt($("#aspect_nav [data-aspect-id='" + id + "'] .badge").text(), 10); + $("#aspect_nav [data-aspect-id='" + id + "'] .badge").text(count + change); + }, + + updateContactCount: function(change) { + var count = parseInt($("#aspect_nav .all_aspects .badge").text(), 10); + $("#aspect_nav .all_aspects .badge").text(count + change); + }, + + addAspectMembership: function(data) { + if(data.startSharing) { this.updateContactCount(1); } + this.updateAspectMembershipCount(data.membership.aspectId, 1); + }, + + removeAspectMembership: function(data) { + if(data.stopSharing) { this.updateContactCount(-1); } + this.updateAspectMembershipCount(data.membership.aspectId, -1); } }); // @license-end diff --git a/app/assets/javascripts/app/views/aspect_membership_view.js b/app/assets/javascripts/app/views/aspect_membership_view.js index e15d654a4..bbf81d749 100644 --- a/app/assets/javascripts/app/views/aspect_membership_view.js +++ b/app/assets/javascripts/app/views/aspect_membership_view.js @@ -77,20 +77,28 @@ app.views.AspectMembership = app.views.AspectsDropdown.extend({ return aspect_membership.save(); }, - _successSaveCb: function(aspect_membership) { - var aspect_id = aspect_membership.get('aspect_id'); - var membership_id = aspect_membership.get('id'); - var li = this.dropdown.find('li[data-aspect_id="'+aspect_id+'"]'); + _successSaveCb: function(aspectMembership) { + var aspectId = aspectMembership.get("aspect_id"), + membershipId = aspectMembership.get("id"), + li = this.dropdown.find("li[data-aspect_id='" + aspectId + "']"), + personId = li.closest("ul.dropdown-menu").data("person_id"), + startSharing = false; // the user didn't have this person in any aspects before, congratulate them // on their newly found friendship ;) - if( this.dropdown.find('li.selected').length === 0 ) { - var msg = Diaspora.I18n.t('aspect_dropdown.started_sharing_with', { 'name': this._name() }); - Diaspora.page.flashMessages.render({ 'success':true, 'notice':msg }); + if( this.dropdown.find("li.selected").length === 0 ) { + var msg = Diaspora.I18n.t("aspect_dropdown.started_sharing_with", { "name": this._name() }); + startSharing = true; + Diaspora.page.flashMessages.render({ "success": true, "notice": msg }); } - li.attr('data-membership_id', membership_id) // just to be sure... - .data('membership_id', membership_id); + app.events.trigger("aspect_membership:create", { + membership: { aspectId: aspectId, personId: personId }, + startSharing: startSharing + }); + + li.attr("data-membership_id", membershipId) // just to be sure... + .data("membership_id", membershipId); this.updateSummary(li); this._done(); @@ -119,21 +127,30 @@ app.views.AspectMembership = app.views.AspectsDropdown.extend({ return aspect_membership.destroy(); }, - _successDestroyCb: function(aspect_membership) { - var membership_id = aspect_membership.get('id'); - var li = this.dropdown.find('li[data-membership_id="'+membership_id+'"]'); + _successDestroyCb: function(aspectMembership) { + var membershipId = aspectMembership.get("id"), + li = this.dropdown.find("li[data-membership_id='" + membershipId + "']"), + aspectId = li.data("aspect_id"), + personId = li.closest("ul.dropdown-menu").data("person_id"), + stopSharing = false; - li.removeAttr('data-membership_id') - .removeData('membership_id'); + li.removeAttr("data-membership_id") + .removeData("membership_id"); this.updateSummary(li); // we just removed the last aspect, inform the user with a flash message // that he is no longer sharing with that person - if( this.dropdown.find('li.selected').length === 0 ) { - var msg = Diaspora.I18n.t('aspect_dropdown.stopped_sharing_with', { 'name': this._name() }); - Diaspora.page.flashMessages.render({ 'success':true, 'notice':msg }); + if( this.dropdown.find("li.selected").length === 0 ) { + var msg = Diaspora.I18n.t("aspect_dropdown.stopped_sharing_with", { "name": this._name() }); + stopSharing = true; + Diaspora.page.flashMessages.render({ "success": true, "notice": msg }); } + app.events.trigger("aspect_membership:destroy", { + membership: { aspectId: aspectId, personId: personId }, + stopSharing: stopSharing + }); + this._done(); }, diff --git a/app/assets/javascripts/app/views/contact_view.js b/app/assets/javascripts/app/views/contact_view.js index 87422ed32..05c4c4c6a 100644 --- a/app/assets/javascripts/app/views/contact_view.js +++ b/app/assets/javascripts/app/views/contact_view.js @@ -36,31 +36,49 @@ app.views.Contact = app.views.Base.extend({ addContactToAspect: function(){ var self = this; + // do we create the first aspect membership for this person? + var startSharing = this.model.get("aspect_memberships").length === 0; this.model.aspect_memberships.create({ - 'person_id': this.model.get('person_id'), - 'aspect_id': app.aspect.get('id') + "person_id": this.model.get("person_id"), + "aspect_id": app.aspect.get("id") },{ success: function(){ + app.events.trigger("aspect_membership:create", { + membership: { + aspectId: app.aspect.get("id"), + personId: self.model.get("person_id") + }, + startSharing: startSharing + }); self.render(); }, error: function(){ - var msg = Diaspora.I18n.t('contacts.error_add', { 'name': self.model.get('person').name }); - Diaspora.page.flashMessages.render({ 'success':false, 'notice':msg }); + var msg = Diaspora.I18n.t("contacts.error_add", { "name": self.model.get("person").name }); + Diaspora.page.flashMessages.render({ "success": false, "notice": msg }); } }); }, removeContactFromAspect: function(){ var self = this; + // do we destroy the last aspect membership for this person? + var stopSharing = this.model.get("aspect_memberships").length <= 1; this.model.aspect_memberships - .find(function(membership){ return membership.get('aspect').id === app.aspect.id; }) + .find(function(membership){ return membership.get("aspect").id === app.aspect.id; }) .destroy({ success: function(){ + app.events.trigger("aspect_membership:destroy", { + membership: { + aspectId: app.aspect.get("id"), + personId: self.model.get("person_id") + }, + stopSharing: stopSharing + }); self.render(); }, error: function(){ - var msg = Diaspora.I18n.t('contacts.error_remove', { 'name': self.model.get('person').name }); - Diaspora.page.flashMessages.render({ 'success':false, 'notice':msg }); + var msg = Diaspora.I18n.t("contacts.error_remove", { "name": self.model.get("person").name }); + Diaspora.page.flashMessages.render({ "success": false, "notice": msg }); } }); } diff --git a/spec/javascripts/app/pages/contacts_spec.js b/spec/javascripts/app/pages/contacts_spec.js index b7679ffe9..b0bca9d73 100644 --- a/spec/javascripts/app/pages/contacts_spec.js +++ b/spec/javascripts/app/pages/contacts_spec.js @@ -98,4 +98,150 @@ describe("app.pages.Contacts", function(){ expect(this.view.stream.search).toHaveBeenCalledWith("Username"); }); }); + + describe("updateAspectMembershipCount", function() { + it("increases the badge count of an aspect", function() { + var aspect = $("#aspect_nav .aspect").eq(0); + $(".badge", aspect).text("15"); + this.view.updateAspectMembershipCount(aspect.data("aspect-id"), 27); + expect($(".badge", aspect).text()).toBe("42"); + }); + + it("decreases the badge count of an aspect", function() { + var aspect = $("#aspect_nav .aspect").eq(1); + $(".badge", aspect).text("42"); + this.view.updateAspectMembershipCount(aspect.data("aspect-id"), -15); + expect($(".badge", aspect).text()).toBe("27"); + }); + }); + + describe("updateContactCount", function() { + it("increases the badge count of 'my aspects'", function() { + $("#aspect_nav .all_aspects .badge").text("15"); + this.view.updateContactCount(27); + expect($("#aspect_nav .all_aspects .badge").text()).toBe("42"); + }); + + it("decreases the badge count of 'my aspects'", function() { + $("#aspect_nav .all_aspects .badge").text("42"); + this.view.updateContactCount(-15); + expect($("#aspect_nav .all_aspects .badge").text()).toBe("27"); + }); + }); + + describe("addAspectMembership", function() { + context("when the user starts sharing", function() { + beforeEach(function() { + this.data = { + membership: { aspectId: $("#aspect_nav .aspect").eq(0).data("aspect-id") }, + startSharing: true + }; + }); + + it("is called on aspect_membership:create", function() { + spyOn(app.pages.Contacts.prototype, "addAspectMembership"); + this.view = new app.pages.Contacts({stream: {render: function(){}}}); + app.events.trigger("aspect_membership:create", this.data); + expect(app.pages.Contacts.prototype.addAspectMembership).toHaveBeenCalledWith(this.data); + }); + + it("calls updateContactCount", function() { + spyOn(this.view, "updateContactCount"); + this.view.addAspectMembership(this.data); + expect(this.view.updateContactCount).toHaveBeenCalledWith(1); + }); + + it("calls updateAspectMembershipCount", function() { + spyOn(this.view, "updateAspectMembershipCount"); + this.view.addAspectMembership(this.data); + expect(this.view.updateAspectMembershipCount).toHaveBeenCalledWith(this.data.membership.aspectId, 1); + }); + }); + + context("when the user doesn't start sharing", function() { + beforeEach(function() { + this.data = { + membership: { aspectId: $("#aspect_nav .aspect").eq(0).data("aspect-id") }, + startSharing: false + }; + }); + + it("is called on aspect_membership:create", function() { + spyOn(app.pages.Contacts.prototype, "addAspectMembership"); + this.view = new app.pages.Contacts({stream: {render: function(){}}}); + app.events.trigger("aspect_membership:create", this.data); + expect(app.pages.Contacts.prototype.addAspectMembership).toHaveBeenCalledWith(this.data); + }); + + it("doesn't call updateContactCount", function() { + spyOn(this.view, "updateContactCount"); + this.view.addAspectMembership(this.data); + expect(this.view.updateContactCount).not.toHaveBeenCalled(); + }); + + it("calls updateAspectMembershipCount", function() { + spyOn(this.view, "updateAspectMembershipCount"); + this.view.addAspectMembership(this.data); + expect(this.view.updateAspectMembershipCount).toHaveBeenCalledWith(this.data.membership.aspectId, 1); + }); + }); + }); + + describe("removeAspectMembership", function() { + context("when the user stops sharing", function() { + beforeEach(function() { + this.data = { + membership: { aspectId: $("#aspect_nav .aspect").eq(0).data("aspect-id") }, + stopSharing: true + }; + }); + + it("is called on aspect_membership:destroy", function() { + spyOn(app.pages.Contacts.prototype, "removeAspectMembership"); + this.view = new app.pages.Contacts({stream: {render: function(){}}}); + app.events.trigger("aspect_membership:destroy", this.data); + expect(app.pages.Contacts.prototype.removeAspectMembership).toHaveBeenCalledWith(this.data); + }); + + it("calls updateContactCount", function() { + spyOn(this.view, "updateContactCount"); + this.view.removeAspectMembership(this.data); + expect(this.view.updateContactCount).toHaveBeenCalledWith(-1); + }); + + it("calls updateAspectMembershipCount", function() { + spyOn(this.view, "updateAspectMembershipCount"); + this.view.removeAspectMembership(this.data); + expect(this.view.updateAspectMembershipCount).toHaveBeenCalledWith(this.data.membership.aspectId, -1); + }); + }); + + context("when the user doesn't stop sharing", function() { + beforeEach(function() { + this.data = { + membership: { aspectId: $("#aspect_nav .aspect").eq(0).data("aspect-id") }, + stopSharing: false + }; + }); + + it("is called on aspect_membership:destroy", function() { + spyOn(app.pages.Contacts.prototype, "removeAspectMembership"); + this.view = new app.pages.Contacts({stream: {render: function(){}}}); + app.events.trigger("aspect_membership:destroy", this.data); + expect(app.pages.Contacts.prototype.removeAspectMembership).toHaveBeenCalledWith(this.data); + }); + + it("doesn't call updateContactCount", function() { + spyOn(this.view, "updateContactCount"); + this.view.removeAspectMembership(this.data); + expect(this.view.updateContactCount).not.toHaveBeenCalled(); + }); + + it("calls updateAspectMembershipCount", function() { + spyOn(this.view, "updateAspectMembershipCount"); + this.view.removeAspectMembership(this.data); + expect(this.view.updateAspectMembershipCount).toHaveBeenCalledWith(this.data.membership.aspectId, -1); + }); + }); + }); }); diff --git a/spec/javascripts/app/views/aspect_membership_view_spec.js b/spec/javascripts/app/views/aspect_membership_view_spec.js index eb33fe203..7ff709ead 100644 --- a/spec/javascripts/app/views/aspect_membership_view_spec.js +++ b/spec/javascripts/app/views/aspect_membership_view_spec.js @@ -1,13 +1,13 @@ describe("app.views.AspectMembership", function(){ - var resp_success = {status: 200, responseText: '{}'}; + var success = {status: 200, responseText: "{}"}; var resp_fail = {status: 400}; beforeEach(function() { // mock a dummy aspect dropdown spec.loadFixture("aspect_membership_dropdown"); this.view = new app.views.AspectMembership({el: $('.aspect_membership_dropdown')}); - this.person_id = $('.dropdown-menu').data('person_id'); - this.person_name = $('.dropdown-menu').data('person-short-name'); + this.personId = $(".dropdown-menu").data("person_id"); + this.personName = $(".dropdown-menu").data("person-short-name"); Diaspora.I18n.load({ aspect_dropdown: { started_sharing_with: 'you started sharing with <%= name %>', @@ -26,7 +26,7 @@ describe("app.views.AspectMembership", function(){ it('marks the aspect as selected', function() { this.newAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().respondWith(resp_success); + jasmine.Ajax.requests.mostRecent().respondWith(success); expect(this.newAspect.attr('class')).toContain('selected'); }); @@ -34,19 +34,30 @@ describe("app.views.AspectMembership", function(){ it('displays flash message when added to first aspect', function() { spec.content().find('li').removeClass('selected'); this.newAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().respondWith(resp_success); + jasmine.Ajax.requests.mostRecent().respondWith(success); expect($('[id^="flash"]')).toBeSuccessFlashMessage( - Diaspora.I18n.t('aspect_dropdown.started_sharing_with', {name: this.person_name}) + Diaspora.I18n.t("aspect_dropdown.started_sharing_with", {name: this.personName}) ); }); + it("triggers aspect_membership:create", function() { + spyOn(app.events, "trigger"); + spec.content().find("li").removeClass("selected"); + this.newAspect.trigger("click"); + jasmine.Ajax.requests.mostRecent().respondWith(success); + expect(app.events.trigger).toHaveBeenCalledWith("aspect_membership:create", { + membership: {aspectId: this.newAspectId, personId: this.personId}, + startSharing: true + }); + }); + it('displays an error when it fails', function() { this.newAspect.trigger('click'); jasmine.Ajax.requests.mostRecent().respondWith(resp_fail); expect($('[id^="flash"]')).toBeErrorFlashMessage( - Diaspora.I18n.t('aspect_dropdown.error', {name: this.person_name}) + Diaspora.I18n.t("aspect_dropdown.error", {name: this.personName}) ); }); }); @@ -59,7 +70,7 @@ describe("app.views.AspectMembership", function(){ it('marks the aspect as unselected', function(){ this.oldAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().respondWith(resp_success); + jasmine.Ajax.requests.mostRecent().respondWith(success); expect(this.oldAspect.attr('class')).not.toContain('selected'); }); @@ -67,19 +78,30 @@ describe("app.views.AspectMembership", function(){ it('displays a flash message when removed from last aspect', function() { spec.content().find('li.selected:last').removeClass('selected'); this.oldAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().respondWith(resp_success); + jasmine.Ajax.requests.mostRecent().respondWith(success); expect($('[id^="flash"]')).toBeSuccessFlashMessage( - Diaspora.I18n.t('aspect_dropdown.stopped_sharing_with', {name: this.person_name}) + Diaspora.I18n.t("aspect_dropdown.stopped_sharing_with", {name: this.personName}) ); }); + it("triggers aspect_membership:destroy", function() { + spyOn(app.events, "trigger"); + spec.content().find("li.selected:last").removeClass("selected"); + this.oldAspect.trigger("click"); + jasmine.Ajax.requests.mostRecent().respondWith(success); + expect(app.events.trigger).toHaveBeenCalledWith("aspect_membership:destroy", { + membership: {aspectId: this.oldAspect.data("aspect_id"), personId: this.personId}, + stopSharing: true + }); + }); + it('displays an error when it fails', function() { this.oldAspect.trigger('click'); jasmine.Ajax.requests.mostRecent().respondWith(resp_fail); expect($('[id^="flash"]')).toBeErrorFlashMessage( - Diaspora.I18n.t('aspect_dropdown.error_remove', {name: this.person_name}) + Diaspora.I18n.t("aspect_dropdown.error_remove", {name: this.personName}) ); }); }); diff --git a/spec/javascripts/app/views/contact_view_spec.js b/spec/javascripts/app/views/contact_view_spec.js index b1d400ed1..56ac4ed2a 100644 --- a/spec/javascripts/app/views/contact_view_spec.js +++ b/spec/javascripts/app/views/contact_view_spec.js @@ -57,6 +57,19 @@ describe("app.views.Contact", function(){ expect(this.model.aspect_memberships.length).toBe(2); }); + it("triggers aspect_membership:create", function() { + spyOn(app.events, "trigger"); + $(".contact_add-to-aspect", this.contact).trigger("click"); + jasmine.Ajax.requests.mostRecent().respondWith({ + status: 200, // success + responseText: this.response + }); + expect(app.events.trigger).toHaveBeenCalledWith("aspect_membership:create", { + membership: {aspectId: app.aspect.get("id"), personId: this.model.get("person_id")}, + startSharing: false + }); + }); + it('calls render', function() { spyOn(this.view, 'render'); $('.contact_add-to-aspect',this.contact).trigger('click'); @@ -109,6 +122,19 @@ describe("app.views.Contact", function(){ expect(this.model.aspect_memberships.length).toBe(0); }); + it("triggers aspect_membership:destroy", function() { + spyOn(app.events, "trigger"); + $(".contact_remove-from-aspect", this.contact).trigger("click"); + jasmine.Ajax.requests.mostRecent().respondWith({ + status: 200, // success + responseText: this.response + }); + expect(app.events.trigger).toHaveBeenCalledWith("aspect_membership:destroy", { + membership: {aspectId: app.aspect.get("id"), personId: this.model.get("person_id")}, + stopSharing: true + }); + }); + it('calls render', function() { spyOn(this.view, 'render'); $('.contact_remove-from-aspect',this.contact).trigger('click');