Merge branch 'next-minor' into develop

This commit is contained in:
Benjamin Neff 2017-01-13 02:49:18 +01:00
commit 479328d0e3
10 changed files with 164 additions and 29 deletions

View file

@ -18,6 +18,7 @@
* Fix background color of year on notifications page with dark theme [#7263](https://github.com/diaspora/diaspora/pull/7263)
* Fix jasmine tests in firefox [#7246](https://github.com/diaspora/diaspora/pull/7246)
* Prevent scroll to top when clicking 'mark all as read' in the notification dropdown [#7253](https://github.com/diaspora/diaspora/pull/7253)
* Update existing notifications in dropdown on fetch [#7270](https://github.com/diaspora/diaspora/pull/7270)
## Features
* Add links to the aspects and followed tags pages on mobile [#7265](https://github.com/diaspora/diaspora/pull/7265)

View file

@ -85,7 +85,6 @@ gem "entypo-rails", "3.0.0.pre.rc2"
# JavaScript
gem "backbone-on-rails", "1.2.0.0"
gem "handlebars_assets", "0.23.1"
gem "jquery-rails", "4.2.1"
gem "jquery-ui-rails", "5.0.5"
@ -103,6 +102,8 @@ source "https://rails-assets.org" do
gem "rails-assets-markdown-it-sub", "1.0.0"
gem "rails-assets-markdown-it-sup", "1.0.0"
gem "rails-assets-highlightjs", "9.7.0"
gem "rails-assets-backbone", "1.3.3"
gem "rails-assets-bootstrap-markdown", "2.10.0"
gem "rails-assets-corejs-typeahead", "1.0.1"
gem "rails-assets-fineuploader-dist", "5.11.0"

View file

@ -58,11 +58,6 @@ GEM
attr_required (1.0.1)
autoprefixer-rails (6.5.1)
execjs
backbone-on-rails (1.2.0.0)
eco
ejs
jquery-rails
railties
bcrypt (3.1.11)
bindata (2.3.1)
bootstrap-sass (3.3.7)
@ -191,12 +186,6 @@ GEM
docile (1.1.5)
domain_name (0.5.20160615)
unf (>= 0.0.5, < 1.0.0)
eco (1.0.0)
coffee-script
eco-source
execjs
eco-source (1.1.0.rc.1)
ejs (1.1.1)
entypo-rails (3.0.0.pre.rc2)
railties (>= 4.1, <= 5)
equalizer (0.0.10)
@ -641,6 +630,8 @@ GEM
railties (= 4.2.7.1)
sprockets-rails
rails-assets-autosize (3.0.20)
rails-assets-backbone (1.3.3)
rails-assets-underscore (>= 1.8.3)
rails-assets-blueimp-gallery (2.21.3)
rails-assets-bootstrap (3.3.7)
rails-assets-jquery (>= 1.9.1, < 4)
@ -681,6 +672,7 @@ GEM
rails-assets-markdown-it-sub (1.0.0)
rails-assets-markdown-it-sup (1.0.0)
rails-assets-perfect-scrollbar (0.6.12)
rails-assets-underscore (1.8.3)
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
rails-dom-testing (1.0.7)
@ -922,7 +914,6 @@ DEPENDENCIES
addressable (= 2.4.0)
asset_sync (= 1.1.0)
autoprefixer-rails (= 6.5.1)
backbone-on-rails (= 1.2.0.0)
bootstrap-sass (= 3.3.7)
bootstrap-switch-rails (= 3.3.3)
capybara (= 2.10.1)
@ -998,6 +989,7 @@ DEPENDENCIES
rack-ssl (= 1.4.1)
rails (= 4.2.7.1)
rails-assets-autosize (= 3.0.20)!
rails-assets-backbone (= 1.3.3)!
rails-assets-blueimp-gallery (= 2.21.3)!
rails-assets-bootstrap-markdown (= 2.10.0)!
rails-assets-corejs-typeahead (= 1.0.1)!

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){
@ -76,15 +77,26 @@ app.views.NotificationDropdown = app.views.Base.extend({
},
onPushBack: function(notification) {
var node = this.dropdownNotifications.append(notification.get("note_html"));
$(node).find(".unread-toggle .entypo-eye").tooltip("destroy").tooltip();
$(node).find(this.avatars.selector).error(this.avatars.fallback);
var node = $(notification.get("note_html"));
this.dropdownNotifications.append(node);
this.afterNotificationChanges(node);
},
onPushFront: function(notification) {
var node = this.dropdownNotifications.prepend(notification.get("note_html"));
$(node).find(".unread-toggle .entypo-eye").tooltip("destroy").tooltip();
$(node).find(this.avatars.selector).error(this.avatars.fallback);
var node = $(notification.get("note_html"));
this.dropdownNotifications.prepend(node);
this.afterNotificationChanges(node);
},
onNotificationChange: function(notification) {
var node = $(notification.get("note_html"));
this.dropdownNotifications.find("[data-guid=" + notification.get("id") + "]").replaceWith(node);
this.afterNotificationChanges(node);
},
afterNotificationChanges: function(node) {
node.find(".unread-toggle .entypo-eye").tooltip("destroy").tooltip();
node.find(this.avatars.selector).error(this.avatars.fallback);
},
finishLoading: function() {

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();
});
});
@ -106,4 +110,77 @@ describe("app.views.NotificationDropdown", function() {
expect($.fn.perfectScrollbar).not.toHaveBeenCalled();
});
});
describe("notification changes", function() {
beforeEach(function() {
this.collection.fetch();
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
responseText: spec.readFixture("notifications_collection")
});
this.notification = factory.notification({
"id": 1337,
"note_html": "<div class='stream-element' data-guid='1337'>This is a notification</div>"
});
expect(this.collection.length).toBeGreaterThan(0);
expect(this.view.$(".notifications .stream-element").length).toBe(this.collection.length);
});
describe("onPushBack", function() {
it("adds the notification at the end of the rendered list", function() {
this.view.onPushBack(this.notification);
expect(this.view.$(".notifications .stream-element").length).toBe(this.collection.length + 1);
expect(this.view.$(".notifications .stream-element").last().text()).toBe("This is a notification");
});
it("calls afterNotificationChanges", function() {
spyOn(this.view, "afterNotificationChanges");
this.view.onPushBack(this.notification);
expect(this.view.afterNotificationChanges).toHaveBeenCalled();
var node = this.view.afterNotificationChanges.calls.mostRecent().args[0];
expect(node.text()).toBe("This is a notification");
});
});
describe("onPushFront", function() {
it("adds the notification to the beginning of the rendered list", function() {
this.view.onPushFront(this.notification);
expect(this.view.$(".notifications .stream-element").length).toBe(this.collection.length + 1);
expect(this.view.$(".notifications .stream-element").first().text()).toBe("This is a notification");
});
it("calls afterNotificationChanges", function() {
spyOn(this.view, "afterNotificationChanges");
this.view.onPushFront(this.notification);
expect(this.view.afterNotificationChanges).toHaveBeenCalled();
var node = this.view.afterNotificationChanges.calls.mostRecent().args[0];
expect(node.text()).toBe("This is a notification");
});
});
describe("onNotificationChange", function() {
beforeEach(function() {
// create a notification which replaces the first in the collection
var firstNoteId = this.collection.models[0].attributes.id;
this.notification = factory.notification({
"id": firstNoteId,
"note_html": "<div class='stream-element' data-guid='" + firstNoteId + "'>This is a notification</div>"
});
});
it("replaces the notification in the rendered list", function() {
this.view.onNotificationChange(this.notification);
expect(this.view.$(".notifications .stream-element").length).toBe(this.collection.length);
expect(this.view.$(".notifications .stream-element").first().text()).toBe("This is a notification");
});
it("calls afterNotificationChanges", function() {
spyOn(this.view, "afterNotificationChanges");
this.view.onNotificationChange(this.notification);
expect(this.view.afterNotificationChanges).toHaveBeenCalled();
var node = this.view.afterNotificationChanges.calls.mostRecent().args[0];
expect(node.text()).toBe("This is a notification");
});
});
});
});

View file

@ -55,6 +55,23 @@ var factory = {
return new app.models.Contact(_.extend(attrs, overrides));
},
notification: function(overrides) {
var noteId = this.id.next();
var defaultAttrs = {
"type": "reshared",
"id": noteId,
"target_type": "Post",
"target_id": this.id.next(),
"recipient_id": this.id.next(),
"unread": true,
"created_at": "2012-01-04T00:55:30Z",
"updated_at": "2012-01-04T00:55:30Z",
"note_html": "This is a notification!"
};
return new app.models.Notification(_.extend(defaultAttrs, overrides));
},
user : function(overrides) {
return new app.models.User(factory.userAttrs(overrides));
},