Commit 4fc9c641 authored by Steffen van Bergerem's avatar Steffen van Bergerem

Port notifications to Bootstrap

parent eabdc739
......@@ -14,6 +14,7 @@
* Update to jQuery 10
* Port publisher and bookmarklet to Bootstrap [#4678](https://github.com/diaspora/diaspora/pull/4678)
* Improve search page, add better indications [#4794](https://github.com/diaspora/diaspora/pull/4794)
* Port notifications and hovercards to Bootstrap [#4814](https://github.com/diaspora/diaspora/pull/4814)
## Bug fixes
* Improve time agos by updating the plugin [#4280](https://github.com/diaspora/diaspora/issues/4280)
......
app.views.Notifications = Backbone.View.extend({
events: {
"click .unread-toggle" : "toggleUnread"
},
initialize: function() {
Diaspora.page.header.notifications.setUpNotificationPage(this);
$('.aspect_membership_dropdown').each(function(){
new app.views.AspectMembership({el: this});
});
},
toggleUnread: function(evt) {
note = $(evt.target).closest(".stream_element");
unread = note.hasClass("unread");
if (unread) {
this.setRead(note.data("guid"));
}
else {
this.setUnread(note.data("guid"));
}
},
setRead: function(guid) {
$.ajax({
url: "/notifications/" + guid,
data: { set_unread: false },
type: "PUT",
context: this,
success: this.clickSuccess
});
},
setUnread: function(guid) {
$.ajax({
url: "/notifications/" + guid,
data: { set_unread: true },
type: "PUT",
context: this,
success: this.clickSuccess
});
},
clickSuccess: function(data) {
type = $('.stream_element[data-guid=' + data["guid"] + ']').data('type');
this.updateView(data["guid"], type, data["unread"]);
},
updateView: function(guid, type, unread) {
change = unread ? 1 : -1;
all_notes = $('ul.nav > li:eq(0) .badge');
type_notes = $('ul.nav > li[data-type=' + type + '] .badge');
header_badge = $('#notification_badge .badge_count');
note = $('.stream_element[data-guid=' + guid + ']');
if(unread) {
note.removeClass("read").addClass("unread");
$(".unread-toggle", note).text(Diaspora.I18n.t('notifications.mark_read'));
}
else {
note.removeClass("unread").addClass("read");
$(".unread-toggle", note).text(Diaspora.I18n.t('notifications.mark_unread'));
}
all_notes.text( function(i,text) { return parseInt(text) + change });
type_notes.text( function(i,text) { return parseInt(text) + change });
header_badge.text( function(i,text) { return parseInt(text) + change });
if(all_notes.text()>0){
all_notes.addClass('badge-important');
} else {
all_notes.removeClass('badge-important');
}
if(type_notes.text()>0){
type_notes.addClass('badge-important');
} else {
type_notes.removeClass('badge-important');
}
if(header_badge.text()>0){
header_badge.removeClass('hidden');
} else {
header_badge.addClass('hidden');
}
}
});
......@@ -12,8 +12,8 @@
$.extend(self, {
badge: badge,
count: parseInt(badge.html()) || 0,
notificationArea: null,
notificationMenu: notificationMenu
notificationMenu: notificationMenu,
notificationPage: null
});
$("a.more").click( function(evt) {
......@@ -31,11 +31,6 @@
self.notificationMenu.find('.unread').each(function(index) {
self.setUpRead( $(this) );
});
if ( self.notificationArea ) {
self.notificationArea.find('.unread').each(function(index) {
self.setUpRead( $(this) );
});
}
self.resetCount();
}
});
......@@ -43,15 +38,8 @@
return false;
});
});
this.setUpNotificationPage = function( contentArea ) {
self.notificationArea = contentArea;
contentArea.find(".unread,.read").each(function(index) {
if ( $(this).hasClass("unread") ) {
self.setUpUnread( $(this) );
} else {
self.setUpRead( $(this) );
}
});
this.setUpNotificationPage = function( page ) {
self.notificationPage = page;
}
this.unreadClick = function() {
$.ajax({
......@@ -106,16 +94,9 @@
}
}
});
if ( self.notificationArea ) {
self.notificationArea.find('.read,.unread').each(function(index) {
if ( $(this).data("guid") == itemID ) {
if ( isUnread ) {
self.setUpUnread( $(this) )
} else {
self.setUpRead( $(this) )
}
}
});
if ( self.notificationPage != null ) {
var type = $('.notification_element[data-guid=' + data["guid"] + ']').data('type');
self.notificationPage.updateView(data["guid"], type, isUnread);
}
};
this.showNotification = function(notification) {
......@@ -134,18 +115,12 @@
this.changeNotificationCount = function(change) {
self.count = Math.max( self.count + change, 0 )
self.badge.text(self.count);
if ( self.notificationArea )
self.notificationArea.find( ".notification_count" ).text(self.count);
if(self.count === 0) {
self.badge.addClass("hidden");
if ( self.notificationArea )
self.notificationArea.find( ".notification_count" ).removeClass("unread");
}
else if(self.count === 1) {
self.badge.removeClass("hidden");
if ( self.notificationArea )
self.notificationArea.find( ".notification_count" ).addClass("unread");
}
};
this.resetCount = function(change) {
......
......@@ -776,25 +776,6 @@ ul#press_logos
#aspects_list
:height auto
.notifications_for_day
.stream_element
:padding 0.2em 0.5em
:width 500px
.day_group
:min-height 100px
:margin
:bottom 10px
.stream_element
&:last-child
:border none
.stream.notifications
> li:hover
:background none
:border
:bottom 1px solid #eee
.show_comments
:border
:top 1px solid $border-grey
......
......@@ -38,3 +38,6 @@
/* bookmarklet */
@import 'bookmarklet';
/* notifications */
@import 'notifications';
#notifications_container {
padding-top: 50px;
.nav.nav-tabs{
li > a {
color: $text-dark-grey;
.entypo {
color: $text-dark-grey;
margin-right: 5px;
}
}
li.active > a {
background-color: $background-grey;
color: $black;
.entypo { color: $black; }
}
}
.stream {
.header {
border-bottom: 1px solid $border-grey;
.btn-toolbar, h4 {
margin-bottom: 10px;
line-height: 40px;
}
margin-bottom: 10px;
}
.day_group {
margin-bottom: 20px;
.date {
text-align: center;
color: $light-grey;
margin-bottom: 5px;
.day {
font-size: 40px;
line-height: 40px;
}
.month {
font-size: 16px;
line-height: 16px;
}
}
}
.media, .media-body {
overflow: visible;
}
.stream_element.media {
padding: 10px;
margin: 0px;
font-size: 13px;
line-height: 18px;
border-bottom: 1px solid $border-grey;
&:last-child { border: none !important; }
&.unread {
background-color: $background-grey;
.unread-toggle { opacity: 1 !important; }
}
&:hover {
.unread-toggle { opacity: 1 !important; }
}
.avatar {
width: 35px;
height: 35px;
}
.unread-toggle {
opacity: 0;
margin-top: 4px;
float: right;
}
.btn-group.aspect_membership_dropdown { margin: 5px 0; }
}
.pagination { text-align: center; }
}
}
.media.stream_element{:data=>{:guid => note.id, :type => (Notification.types.key(note.type) || '') }, :class => (note.unread ? 'unread' : 'read')}
%button.btn.btn-link.btn-small.unread-toggle
= note.unread ? t('notifications.index.mark_read') : t('notifications.index.mark_unread')
- if note.type == "Notifications::StartedSharing" && contact = current_user.contact_for(note.effective_target)
.pull-right
= aspect_membership_dropdown(contact, note.effective_target, 'left')
.media-object.pull-left
= person_image_link note.actors.first, :size => :thumb_small, :class => 'hovercardable'
.media-body
= notification_message_for(note)
%div
= timeago(note.created_at)
#notifications_content
.span-13
%h2
%span.notification_count{:class => ('unread' if @unread_notification_count >0 )}
= @unread_notification_count
= t('.notifications')
.span-8.last
= link_to t('.mark_all_as_read'), notifications_read_all_path, :class => "button #{'disabled' unless @unread_notification_count > 0}"
.span-24.last
.stream.notifications
.container-fluid#notifications_container
.row-fluid
.span3
%h3
= t('.notifications')
%ul.nav.nav-tabs.nav-stacked
%li{ :class => ('active' unless params[:type] && @grouped_unread_notification_counts.has_key?(params[:type])) }
%a{ :href => '/notifications' + (params[:show] == 'unread' ? '?show=unread' : '') }
%span.pull-right.badge{:class => ('badge-important' if @unread_notification_count > 0)}
= @unread_notification_count
= t('.all_notifications')
- @grouped_unread_notification_counts.each do |key, count|
%li{ :class => ('active' if params[:type] == key), :data => { :type => key } }
%a{ :href => '/notifications?type=' + key + (params[:show] == 'unread' ? '&show=unread' : '') }
%span.pull-right.badge{ :class => ('badge-important' if count > 0) }
= count
- case key
- when 'also_commented', 'comment_on_post'
%i.entypo.comment
- when 'liked'
%i.entypo.heart
- when 'mentioned'
%i.entypo.pencil
- when 'reshared'
%i.entypo.retweet
- when 'started_sharing'
%i.entypo.users
= t('.'+key)
.span9.stream.notifications
.row-fluid.header
.span12
.btn-toolbar.pull-right
.btn-group
%a.btn.btn-default{ :class => ('active' unless params[:show] == 'unread'), :href => '/notifications' + (params[:type] ? '?type=' + params[:type] : '') }
= t('.show_all')
%a.btn.btn-default{ :class => ('active' if params[:show] == 'unread'), :href => '/notifications?show=unread' + (params[:type] ? '&type=' + params[:type] : '') }
= t('.show_unread')
%a.btn.btn-default{:href => notifications_read_all_path, :class => ('disabled' unless @unread_notification_count > 0)}
= t('.mark_all_as_read')
- @group_days.each do |day, notes|
.day_group.span-24.last
.span-3
.date
.day= the_day(day.split(' '))
.month= the_month(day.split(' '))
.day_group.row-fluid
.date.span2
.day= the_day(day.split(' '))
.month= the_month(day.split(' '))
.span-8.notifications_for_day
.notifications_for_day.span10
- notes.each do |note|
.stream_element{:data=>{:guid => note.id}, :class => "#{note.unread ? 'unread' : 'read'}"}
- if note.type == "Notifications::StartedSharing" && contact = current_user.contact_for(note.effective_target)
.float-right
= aspect_membership_dropdown(contact, note.effective_target, 'left')
.media
.bd
= person_image_tag note.actors.first, :thumb_medium
= notification_message_for(note)
%div
= timeago(note.created_at)
= link_to t('.mark_unread'), "#", :class => "unread-setter"
= render :partial => 'notifications/notification', :locals => { :note => note }
= will_paginate @notifications
= will_paginate @notifications, :renderer => WillPaginate::ActionView::BootstrapLinkRenderer
:javascript
$(document).ready(function(){
Diaspora.page.header.notifications.setUpNotificationPage( $("#notifications_content" ) );
new app.views.Notifications({ el: '#notifications_container' });
});
......@@ -637,25 +637,25 @@ en:
one: "%{actors} sent you a message."
other: "%{actors} sent you a message."
comment_on_post:
zero: "%{actors} commented on your post »%{post_link}«."
one: "%{actors} commented on your post »%{post_link}«."
other: "%{actors} commented on your post »%{post_link}«."
zero: "%{actors} commented on your post %{post_link}."
one: "%{actors} commented on your post %{post_link}."
other: "%{actors} commented on your post %{post_link}."
also_commented:
zero: "%{actors} also commented on %{post_author}'s post »%{post_link}«."
one: "%{actors} also commented on %{post_author}'s post »%{post_link}«."
other: "%{actors} also commented on %{post_author}'s post »%{post_link}«."
zero: "%{actors} also commented on %{post_author}'s post %{post_link}."
one: "%{actors} also commented on %{post_author}'s post %{post_link}."
other: "%{actors} also commented on %{post_author}'s post %{post_link}."
mentioned:
zero: "%{actors} have mentioned you in the post »%{post_link}«."
one: "%{actors} has mentioned you in the post »%{post_link}«."
other: "%{actors} have mentioned you in the »%{post_link}«."
zero: "%{actors} have mentioned you in the post %{post_link}."
one: "%{actors} has mentioned you in the post %{post_link}."
other: "%{actors} have mentioned you in the %{post_link}."
liked:
zero: "%{actors} have liked your post »%{post_link}«."
one: "%{actors} has liked your post »%{post_link}«."
other: "%{actors} have liked your post »%{post_link}«."
zero: "%{actors} have liked your post %{post_link}."
one: "%{actors} has liked your post %{post_link}."
other: "%{actors} have liked your post %{post_link}."
reshared:
zero: "%{actors} have reshared your post »%{post_link}«."
one: "%{actors} has reshared your post »%{post_link}«."
other: "%{actors} have reshared your post »%{post_link}«."
zero: "%{actors} have reshared your post %{post_link}."
one: "%{actors} has reshared your post %{post_link}."
other: "%{actors} have reshared your post %{post_link}."
post: "post"
also_commented_deleted:
zero: "%{actors} commented on a deleted post."
......
......@@ -73,5 +73,16 @@ Feature: Notifications
When I sign in as "bob@bob.bob"
And I follow "Notifications" in the header
Then the notification dropdown should be visible
Then I should see "mentioned you in a post"
Then I should see "mentioned you in the post"
And I should have 1 email delivery
Scenario: filter notifications
Given a user with email "bob@bob.bob" is connected with "alice@alice.alice"
And Alice has a post mentioning Bob
When I sign in as "bob@bob.bob"
And I am on the notifications page
Then I should see "mentioned you in the post"
When I filter notifications by likes
Then I should not see "mentioned you in the post"
When I filter notifications by mentions
Then I should see "mentioned you in the post"
When /^I filter notifications by likes$/ do
step %(I follow "Liked" within "#notifications_container ul.nav.nav-tabs")
end
When /^I filter notifications by mentions$/ do
step %(I follow "Mentioned" within "#notifications_container ul.nav.nav-tabs")
end
describe("app.views.Notifications", function(){
beforeEach(function() {
spec.loadFixture("notifications");
this.view = new app.views.Notifications({el: '#notifications_container'});
});
context('mark read', function() {
beforeEach(function() {
this.unreadN = $('.stream_element.unread').first();
this.guid = this.unreadN.data("guid");
});
it('calls "setRead"', function() {
spyOn(this.view, "setRead");
this.unreadN.find('.unread-toggle').trigger('click');
expect(this.view.setRead).toHaveBeenCalledWith(this.guid);
});
});
context('mark unread', function() {
beforeEach(function() {
this.readN = $('.stream_element.read').first();
this.guid = this.readN.data("guid");
});
it('calls "setUnread"', function() {
spyOn(this.view, "setUnread");
this.readN.find('.unread-toggle').trigger('click');
expect(this.view.setUnread).toHaveBeenCalledWith(this.guid);
});
});
context('updateView', function() {
beforeEach(function() {
this.readN = $('.stream_element.read').first();
this.guid = this.readN.data('guid');
this.type = this.readN.data('type');
});
it('changes the "all notifications" count', function() {
badge = $('ul.nav > li:eq(0) .badge');
count = parseInt(badge.text());
this.view.updateView(this.guid, this.type, true);
expect(parseInt(badge.text())).toBe(count + 1);
this.view.updateView(this.guid, this.type, false);
expect(parseInt(badge.text())).toBe(count);
});
it('changes the notification type count', function() {
badge = $('ul.nav > li[data-type=' + this.type + '] .badge');
count = parseInt(badge.text());
this.view.updateView(this.guid, this.type, true);
expect(parseInt(badge.text())).toBe(count + 1);
this.view.updateView(this.guid, this.type, false);
expect(parseInt(badge.text())).toBe(count);
});
it('toggles the unread class and changes the link text', function() {
this.view.updateView(this.readN.data('guid'), this.readN.data('type'), true);
expect(this.readN.hasClass('unread')).toBeTruethy;
expect(this.readN.hasClass('read')).toBeFalsy;
expect(this.readN.find('.unread-toggle').text()).toContain(Diaspora.I18n.t('notifications.mark_read'));
this.view.updateView(this.readN.data('guid'), this.readN.data('type'), false);
expect(this.readN.hasClass('read')).toBeTruethy;
expect(this.readN.hasClass('unread')).toBeFalsy;
expect(this.readN.find('.unread-toggle').text()).toContain(Diaspora.I18n.t('notifications.mark_unread'));
});
});
});
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment