Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add collection for notification #6952

Closed
wants to merge 1 commit into
base: develop
from

Conversation

Projects
None yet
5 participants
@AugierLe42e
Copy link
Contributor

AugierLe42e commented Aug 9, 2016

This supersedes #6493, solves #5780 and partly solves #3890.

@denschub denschub referenced this pull request Aug 11, 2016

Closed

Fetch notification counts #6493

0 of 4 tasks complete

@AugierLe42e AugierLe42e force-pushed the AugierLe42e:notifications-header-use-collection branch from 4485b81 to 9c7d337 Aug 15, 2016

postRenderTemplate: function() {
if (!app.collections.Notifications.instance) {
app.collections.Notifications.instance = new app.collections.Notifications();
}

This comment has been minimized.

@AugierLe42e

AugierLe42e Aug 15, 2016

Author Contributor

I'd like an advice on this. The collection needs to be shared with the notification view of the /notifications page so I create a class instance but I'm not really satisfied with this solution.

This comment has been minimized.

@svbergerem

svbergerem Sep 10, 2016

Member
setupHeader: function() {
  if(app.currentUser.authenticated()) {
    app.notificationsCollection = new app.collections.Notifications();
    app.header = new app.views.Header();
    $("header").prepend(app.header.el);
    app.header.render();
  }
},

in app/assets/javascripts/app/app.js?

This comment has been minimized.

@AugierLe42e

AugierLe42e Sep 11, 2016

Author Contributor

Ah, yes! It's nicer. Thx.

@AugierLe42e AugierLe42e force-pushed the AugierLe42e:notifications-header-use-collection branch 3 times, most recently from d8ef3e0 to d52d13b Sep 18, 2016

@AugierLe42e AugierLe42e changed the title [WIP] Add collection for notification [Add collection for notification Sep 19, 2016

@AugierLe42e

This comment has been minimized.

Copy link
Contributor Author

AugierLe42e commented Sep 19, 2016

Ready for review. I added an option for browser notification when there are unread diaspora notifications.

@AugierLe42e AugierLe42e changed the title [Add collection for notification Add collection for notification Sep 19, 2016

@AugierLe42e AugierLe42e force-pushed the AugierLe42e:notifications-header-use-collection branch 2 times, most recently from e311e66 to 73c412e Sep 24, 2016

@AugierLe42e AugierLe42e force-pushed the AugierLe42e:notifications-header-use-collection branch from 73c412e to fc36652 Oct 23, 2016

@AugierLe42e

This comment has been minimized.

Copy link
Contributor Author

AugierLe42e commented Nov 6, 2016

Ping.

unreadCountByType: {},

initialize: function() {
this.pollNotifications();

This comment has been minimized.

@svbergerem

svbergerem Nov 11, 2016

Member

Maybe

setInterval(this.pollNotifications.bind(this), 30000)

instead?

This comment has been minimized.

@svbergerem

svbergerem Nov 11, 2016

Member

I'd also increase the time between two fetches to a minute or even 5 minutes. This feature increases the load for servers with a lot of users and I'd like to keep the additional load small. We can go back to 30 seconds in a later PR when this has been tested on bigger pods.

@denschub It would be great if you could add your opinion here.

This comment has been minimized.

@svbergerem

svbergerem Nov 11, 2016

Member

In combination with the browser notification stuff I wrote a few lines below the new code could be

this.fetch();
this.timeout = 300000 // 5 minutes
setTimeout(setInterval(this.pollNotifications.bind(this), this.timeout).bind(this), this.timeout);
Diaspora.BrowserNotification.requestPermission();

This comment has been minimized.

@AugierLe42e

AugierLe42e Nov 13, 2016

Author Contributor

It has been in production on diaspote for 3 months now with no problem. Maybe @Flaburgan can merge it on d-fr before it is merged on develop to see what happens?

This comment has been minimized.

@Flaburgan

Flaburgan Nov 13, 2016

Member

I can, even if d-fr is not a big pod like frama or jd so I would tend to pick the careful way here and choose 1 or 2 minutes at the moment.

if (unreadCountBefore < this.unreadCount) {
Diaspora.BrowserNotification.spawnNotification(Diaspora.I18n.t("notifications.new_notifications"));
}
setTimeout(this.pollNotifications.bind(this), 30000);

This comment has been minimized.

@svbergerem

svbergerem Nov 11, 2016

Member

Remove this line when using setInterval.


/**
* Add new models to the collection at the end or at the beginning of the collection and
* then fires an event for each model of the collection. It will fire a different event

This comment has been minimized.

@svbergerem

svbergerem Nov 11, 2016

Member

Either

Adds new models to the collection at the end or at the beginning of the collection and then fires an event for each model of the collection.

or

Add new models to the collection at the end or at the beginning of the collection and then fire an event for each model of the collection.

var accu = function(model) { models.push(model); };
this.on("add", accu);
Backbone.Collection.prototype.set.apply(this, [items, options]);
this.off("add", accu);

This comment has been minimized.

@svbergerem

svbergerem Nov 11, 2016

Member

http://backbonejs.org/#Collection-set

Returns the touched models in the collection.

Have you already tried

var models = Backbone.Collection.prototype.set.apply(this, [items, options]);

?

This comment has been minimized.

@AugierLe42e

AugierLe42e Nov 13, 2016

Author Contributor

Hmm... Nope, I didn't. Thanks for highlighting that.

This comment has been minimized.

@AugierLe42e

AugierLe42e Nov 13, 2016

Author Contributor

Ok, it doesn't work. It also return models that haven't been newly added and creates duplications in the dropdown.

/* eslint-disable new-cap */
var model = new this.model(item);
/* eslint-enable new-cap */
model.on("change:unread", this.onChagedUnreadStatus.bind(this));

This comment has been minimized.

@svbergerem

svbergerem Nov 11, 2016

Member

this.onChangedUnreadStatus.bind(this)

expect(Backbone.Collection.prototype.set).toHaveBeenCalledWith([], {pushBack: false, at: 0});
});

it("inserts the items at the end of the collection with option 'pushBack' is true", function() {

This comment has been minimized.

@svbergerem

svbergerem Nov 11, 2016

Member

inserts the items at the end of the collection if option 'pushBack' is true

expect(calls.argsFor(index + 3)).toEqual(["pushFront", this.model1]);
});

it("triggers a 'pushBack' event for each model in when option 'pushBack' is true", function() {

This comment has been minimized.

@svbergerem

svbergerem Nov 11, 2016

Member

triggers a 'pushBack' event for each model in normal order when option 'pushBack' is true

it("calls calls ajax with correct parameters and sets 'unread' attribute", function() {
this.target.setUnreadStatus(true);
jasmine.Ajax.requests.mostRecent().respondWith({status: 200, responseText: '{"guid": 16, "unread": true}'});
var call = jQuery.ajax.calls.mostRecent();

This comment has been minimized.

@svbergerem

svbergerem Nov 11, 2016

Member

Have you tried

var call = jasmine.Ajax.requests.mostRecent();

?

Then you wouldn't have to spy on jQuery.ajax.

var call = jQuery.ajax.calls.mostRecent();

expect(jQuery.ajax).toHaveBeenCalled();
expect(call.args[0].url).toBe("/notifications/16");

This comment has been minimized.

@svbergerem

svbergerem Nov 11, 2016

Member

With jasmine.Ajax.requests.mostRecent() this would be

expect(jasmine.Ajax.requests.mostRecent().url).toBe('/notifications/16')

or

expect(call.url).toBe('/notifications/16')
this.fetch();
if (unreadCountBefore < this.unreadCount) {
Diaspora.BrowserNotification.spawnNotification(Diaspora.I18n.t("notifications.new_notifications"));
}

This comment has been minimized.

@svbergerem

svbergerem Nov 11, 2016

Member

At least while testing this feature it didn't work because the check unreadCountBefore < this.unreadCount was done before fetch finished. You can fix this by listening to the finishedLoading event:

   pollNotifications: function() {
     var unreadCountBefore = this.unreadCount;
+    this.once("finishedLoading", function() {
+      if (unreadCountBefore < this.unreadCount) {
+        Diaspora.BrowserNotification.spawnNotification(Diaspora.I18n.t("notifications.new_notifications"));
+      }
+    }, this);
     this.fetch();
-    if (unreadCountBefore < this.unreadCount) {
-      Diaspora.BrowserNotification.spawnNotification(Diaspora.I18n.t("notifications.new_notifications"));
-    }

This would also show a browser notification immediately after loading the page when there are unread notifications. To fix this call this.fetch() in initialize and call pollNotifications the first time after some timeout.

icon: ImagePaths.get("branding/logos/asterisk_white_mobile.png")
});

setTimeout(notification.close.bind(notification), 5000);

This comment has been minimized.

@svbergerem

svbergerem Nov 11, 2016

Member

I think I'd just remove this. Users can close the notifications themselves. When I'm afk for a few minutes then I'd like to see the notification when I come back.

Any other opinions about this?

@AugierLe42e AugierLe42e force-pushed the AugierLe42e:notifications-header-use-collection branch 2 times, most recently from f64985a to 05568ac Nov 13, 2016

@AugierLe42e

This comment has been minimized.

Copy link
Contributor Author

AugierLe42e commented Nov 15, 2016

It's ok on my side.

/* eslint-enable camelcase */
type: "PUT",
context: this,
success: function() { this.set("unread", state); }.bind(this)

This comment has been minimized.

@svbergerem

svbergerem Nov 17, 2016

Member

Binding this shouldn't be needed since you already set context: this.

this.dropdownNotifications.scroll(function(){
self.dropdownScroll();
});
this.dropdownNotifications.scroll(function() { this.dropdownScroll(); }.bind(this));

This comment has been minimized.

@svbergerem

svbergerem Nov 17, 2016

Member

This would attach the dropdownScroll function to the scroll event again and again. Doing this once at the end of the initialize function should work, too.

expect(Backbone.Collection.prototype.set).toHaveBeenCalledWith([], {at: 0});
});

it(" the items at the beginning of the collection if option 'pushBack' is false", function() {

This comment has been minimized.

@svbergerem

svbergerem Nov 17, 2016

Member

it("inserts the items...


this.view.updateView(this.guid, this.type, false);

This comment has been minimized.

@svbergerem

svbergerem Nov 17, 2016

Member

This decreased the notifications count before which is now missing. Now you just call updateView twice without any changes.


this.view.updateView(this.guid, this.type, false);

This comment has been minimized.

@svbergerem

svbergerem Nov 17, 2016

Member

Again: you just call updateView twice without any changes.

it("hides badge count when notification count is zero", function() {
Object.keys(this.collection.unreadCountByType).forEach(function(notificationType) {
this.collection.unreadCountByType[notificationType] = 0;
this.collection.unreadCount = 0;

This comment has been minimized.

@svbergerem

svbergerem Nov 17, 2016

Member

This should be outside of the forEach loop.

expect(parseInt(badge2.text(), 10)).toBe(count + 1);
this.view.updateView();
expect(parseInt(badge2.text(), 10)).toBe(this.collection.unreadCount);
});

This comment has been minimized.

@svbergerem

svbergerem Nov 17, 2016

Member

Before we checked both increase and decrease of the unread counts. Calling updateView once should also be sufficient for changes to badge1 and badge2, so just do "increase", updateView, "check badge1 and badge2", "decrease", updateView, "check badge1 and badge2".

this.readN.find(".unread-toggle").trigger("click");

expect(this.view.setUnread).toHaveBeenCalledWith(this.guid);
});

This comment has been minimized.

@svbergerem

svbergerem Nov 17, 2016

Member

Am I missing something or are there now no tests for marking notifications as read/unread via click?

This comment has been minimized.

@AugierLe42e

AugierLe42e Nov 17, 2016

Author Contributor

The tests happens in notifications_collection_spec.js. I can reinstanciate these ones for the click event.

this.bindCollectionEvents();
},

bindCollectionEvents: function() {

This comment has been minimized.

@svbergerem

svbergerem Nov 17, 2016

Member

Some tests would be great.

this.bindCollectionEvents();
},

bindCollectionEvents: function() {

This comment has been minimized.

@svbergerem

svbergerem Nov 17, 2016

Member

Some tests would be great.

@AugierLe42e AugierLe42e force-pushed the AugierLe42e:notifications-header-use-collection branch from 05568ac to 7587fcd Nov 17, 2016

@AugierLe42e

This comment has been minimized.

Copy link
Contributor Author

AugierLe42e commented Nov 18, 2016

@svbergerem : I don't understand why this test and this one won't pass. According to the test failure report the spied methods seem not mocked at all and called anyway. I use strickly the same method in this one with no problem :/

@svbergerem

This comment has been minimized.

Copy link
Member

svbergerem commented Nov 18, 2016

@AugierLe42e Here you already bind the events and you bind them here a second time with new functions. Thus both old and new functions are called and calling the old ones makes the tests fail.

diff --git a/spec/javascripts/app/views/notification_dropdown_view_spec.js b/spec/javascripts/app/views/notification_dropdown_view_spec.js
index 3b01319..f47e945 100644
--- a/spec/javascripts/app/views/notification_dropdown_view_spec.js
+++ b/spec/javascripts/app/views/notification_dropdown_view_spec.js
@@ -12,6 +12,9 @@ describe("app.views.NotificationDropdown", function() {

   describe("bindCollectionEvents", function() {
     it("binds collection events", function() {
+      this.view.collection.off("pushFront");
+      this.view.collection.off("pushBack");
+      this.view.collection.off("finishedLoading");
       spyOn(this.view, "onPushFront");
       spyOn(this.view, "onPushBack");
       spyOn(this.view, "finishLoading");
@@ -20,11 +23,11 @@ describe("app.views.NotificationDropdown", function() {

       this.collection.trigger("pushFront");
       this.collection.trigger("pushBack");
-      this.collection.trigger("finishLoading");
+      this.collection.trigger("finishedLoading");

       expect(this.view.onPushFront).toHaveBeenCalled();
       expect(this.view.onPushBack).toHaveBeenCalled();
-      expect(this.view.pushBack).toHaveBeenCalled();
+      expect(this.view.finishLoading).toHaveBeenCalled();
     });
   });

You should be able to fix your second problem in a similar way.

@AugierLe42e

This comment has been minimized.

Copy link
Contributor Author

AugierLe42e commented Nov 18, 2016

JS is so tricky sometimes. Thx, @svbergerem.

@AugierLe42e AugierLe42e force-pushed the AugierLe42e:notifications-header-use-collection branch from 7587fcd to 59181e2 Nov 18, 2016

@AugierLe42e

This comment has been minimized.

Copy link
Contributor Author

AugierLe42e commented Nov 18, 2016

Done.

@svbergerem svbergerem added this to the 0.6.2.0 milestone Nov 18, 2016

@svbergerem

This comment has been minimized.

Copy link
Member

svbergerem commented Nov 18, 2016

Thank you! :)

@Flaburgan

This comment has been minimized.

Copy link
Member

Flaburgan commented Nov 18, 2016

I just pulled this on d-fr, looks like it works nicely. I'll report here the load usage if I see some significant changes. If not, I'll try to set it to 1 minute instead of 5 and then test again. This setting could probably be added in the config file btw.

@AugierLe42e AugierLe42e deleted the AugierLe42e:notifications-header-use-collection branch Nov 18, 2016

@AugierLe42e

This comment has been minimized.

Copy link
Contributor Author

AugierLe42e commented Nov 18, 2016

@svbergerem: Thanks for your careful reviewing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.