Update existing notifications in dropdown on fetch

When fetching notifications this merges existing notifications and changes
their appearance in the dropdown if the html or the unread status changed.

This doesn't update all notifications in the dropdown but only those that are
returned by the server.

Related to #7247.
This commit is contained in:
Steffen van Bergerem 2016-12-14 20:57:51 +01:00 committed by Benjamin Neff
parent ade9b97215
commit abe7ef3d18
6 changed files with 57 additions and 10 deletions

View file

@ -84,7 +84,8 @@ app.collections.Notifications = Backbone.Collection.extend({
/* eslint-disable new-cap */
var model = new this.model(item);
/* eslint-enable new-cap */
model.on("change:unread", this.onChangedUnreadStatus.bind(this));
model.on("userChangedUnreadStatus", this.onChangedUnreadStatus.bind(this));
model.on("change:unread", function() { this.trigger("update"); }.bind(this));
return model;
}.bind(this));
},

View file

@ -40,6 +40,10 @@ app.models.Notification = Backbone.Model.extend({
* }
*/
parse: function(response) {
if (response.id) {
// already correct format
return response;
}
var result = {type: response.type};
result = $.extend(result, response[result.type]);
return result;
@ -62,7 +66,10 @@ app.models.Notification = Backbone.Model.extend({
/* eslint-enable camelcase */
type: "PUT",
context: this,
success: function() { this.set("unread", state); }
success: function() {
this.set("unread", state);
this.trigger("userChangedUnreadStatus", this);
}
});
}
}

View file

@ -21,6 +21,7 @@ app.views.NotificationDropdown = app.views.Base.extend({
this.collection.on("pushFront", this.onPushFront.bind(this));
this.collection.on("pushBack", this.onPushBack.bind(this));
this.collection.on("finishedLoading", this.finishLoading.bind(this));
this.collection.on("change:note_html", this.onNotificationChange.bind(this));
},
toggleDropdown: function(evt){
@ -87,6 +88,13 @@ app.views.NotificationDropdown = app.views.Base.extend({
$(node).find(this.avatars.selector).error(this.avatars.fallback);
},
onNotificationChange: function(notification) {
var node = this.dropdownNotifications.find("[data-guid=" + notification.get("id") + "]");
$(node).replaceWith(notification.get("note_html"));
$(node).find(".unread-toggle .entypo-eye").tooltip("destroy").tooltip();
$(node).find(this.avatars.selector).error(this.avatars.fallback);
},
finishLoading: function() {
app.helpers.timeago(this.dropdownNotifications);
this.updateScrollbar();

View file

@ -205,7 +205,7 @@ describe("app.collections.Notifications", function() {
});
it("correctly binds the change:unread event", function() {
spyOn(app.collections.Notifications.prototype, "onChangedUnreadStatus");
spyOn(this.target, "trigger");
/* eslint-disable camelcase */
var parsed = this.target.parse({
@ -216,8 +216,24 @@ describe("app.collections.Notifications", function() {
/* eslint-enable camelcase */
parsed[0].set("unread", true);
expect(this.target.trigger).toHaveBeenCalledWith("update");
});
expect(app.collections.Notifications.prototype.onChangedUnreadStatus).toHaveBeenCalled();
it("correctly binds the userChangedUnreadStatus event", function() {
spyOn(this.target, "onChangedUnreadStatus");
/* eslint-disable camelcase */
var parsed = this.target.parse({
unread_count: 15,
unread_count_by_type: {reshared: 6},
notification_list: [{"reshared": {id: 1}, "type": "reshared"}]
});
/* eslint-enable camelcase */
parsed[0].set("unread", true);
parsed[0].trigger("userChangedUnreadStatus", parsed[0]);
expect(this.target.onChangedUnreadStatus).toHaveBeenCalled();
});
});

View file

@ -18,8 +18,8 @@ describe("app.models.Notification", function() {
});
describe("parse", function() {
it("correctly parses the object", function() {
var parsed = this.model.parse({
beforeEach(function() {
this.response = {
"reshared": {
"id": 45,
"target_type": "Post",
@ -31,9 +31,8 @@ describe("app.models.Notification", function() {
"note_html": "<html/>"
},
"type": "reshared"
});
expect(parsed).toEqual({
};
this.parsedResponse = {
"type": "reshared",
"id": 45,
"target_type": "Post",
@ -43,7 +42,17 @@ describe("app.models.Notification", function() {
"created_at": "2015-10-27T19:56:30.000Z",
"updated_at": "2015-10-27T19:56:30.000Z",
"note_html": "<html/>"
});
};
});
it("correctly parses the object", function() {
var parsed = this.model.parse(this.response);
expect(parsed).toEqual(this.parsedResponse);
});
it("correctly parses the object twice", function() {
var parsed = this.model.parse(this.parsedResponse);
expect(parsed).toEqual(this.parsedResponse);
});
});
@ -67,6 +76,7 @@ describe("app.models.Notification", function() {
beforeEach(function() {
this.target = new app.models.Notification({"reshared": {id: 16}, "type": "reshared"});
spyOn(app.models.Notification.prototype, "set").and.callThrough();
spyOn(app.models.Notification.prototype, "trigger");
});
it("calls calls ajax with correct parameters and sets 'unread' attribute", function() {
@ -80,6 +90,7 @@ describe("app.models.Notification", function() {
/* eslint-enable camelcase */
expect(call.method).toEqual("PUT");
expect(app.models.Notification.prototype.set).toHaveBeenCalledWith("unread", true);
expect(app.models.Notification.prototype.trigger).toHaveBeenCalledWith("userChangedUnreadStatus", this.target);
});
});
});

View file

@ -15,9 +15,11 @@ describe("app.views.NotificationDropdown", function() {
this.view.collection.off("pushFront");
this.view.collection.off("pushBack");
this.view.collection.off("finishedLoading");
this.view.collection.off("change:note_html");
spyOn(this.view, "onPushFront");
spyOn(this.view, "onPushBack");
spyOn(this.view, "finishLoading");
spyOn(this.view, "onNotificationChange");
});
it("binds collection events", function() {
@ -26,10 +28,12 @@ describe("app.views.NotificationDropdown", function() {
this.collection.trigger("pushFront");
this.collection.trigger("pushBack");
this.collection.trigger("finishedLoading");
this.collection.trigger("change:note_html");
expect(this.view.onPushFront).toHaveBeenCalled();
expect(this.view.onPushBack).toHaveBeenCalled();
expect(this.view.finishLoading).toHaveBeenCalled();
expect(this.view.onNotificationChange).toHaveBeenCalled();
});
});