diff --git a/Changelog.md b/Changelog.md index 922bfa4ea..7a934236a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -98,7 +98,7 @@ diaspora.yml file**. The existing settings from 0.4.x and before will not work a * Display photos on the profile page as thumbnails [#5521](https://github.com/diaspora/diaspora/pull/5521) * Unify not connected pages (sign in, sign up, forgot password) [#5391](https://github.com/diaspora/diaspora/pull/5391) * Port remaining stream pages to Bootstrap [#5715](https://github.com/diaspora/diaspora/pull/5715) -* Partial Backbone port of the notification dropdown [#5707](https://github.com/diaspora/diaspora/pull/5707) +* Port notification dropdown to Backbone [#5707](https://github.com/diaspora/diaspora/pull/5707) [#5761](https://github.com/diaspora/diaspora/pull/5761) * Add rounded corners for avatars [#5733](https://github.com/diaspora/diaspora/pull/5733) * Move registration form to a partial [#5764](https://github.com/diaspora/diaspora/pull/5764) * Add tests for liking and unliking posts [#5741](https://github.com/diaspora/diaspora/pull/5741) diff --git a/app/assets/javascripts/app/views/header_view.js b/app/assets/javascripts/app/views/header_view.js index a309942e7..814ef3a87 100644 --- a/app/assets/javascripts/app/views/header_view.js +++ b/app/assets/javascripts/app/views/header_view.js @@ -2,44 +2,47 @@ app.views.Header = app.views.Base.extend({ - templateName : "header", + templateName: "header", - className : "dark-header", + className: "dark-header", - events : { - "click ul.dropdown li:first-child" : "toggleDropdown", + events: { + "click ul.dropdown li:first-child": "toggleUserDropdown", "focusin #q": "toggleSearchActive", "focusout #q": "toggleSearchActive" }, - initialize : function() { - $(document.body).click($.proxy(this.hideDropdown, this)); + initialize: function(){ + $(document.body).click($.proxy(this.hideUserDropdown, this)); + return this; }, postRenderTemplate: function(){ new app.views.Notifications({ el: '#notification_dropdown' }); + new app.views.NotificationDropdown({ el: '#notification_badge' }); + new app.views.Search({ el: '#header-search-form' }); }, - menuElement : function() { return this.$("ul.dropdown"); }, + menuElement: function(){ return this.$("ul.dropdown"); }, - toggleDropdown : function(evt) { + toggleUserDropdown: function(evt){ if(evt){ evt.preventDefault(); } this.menuElement().toggleClass("active"); - if($.browser.msie) { + if($.browser.msie){ this.$("header").toggleClass('ie-user-menu-active'); } }, - hideDropdown : function(evt) { - if(this.menuElement().hasClass("active") && !$(evt.target).parents("#user_menu").length) { + hideUserDropdown: function(evt){ + if(this.menuElement().hasClass("active") && !$(evt.target).parents("#user_menu").length){ this.menuElement().removeClass("active"); } }, - toggleSearchActive: function(ev) { + toggleSearchActive: function(ev){ // jQuery produces two events for focus/blur (for bubbling) // don't rely on which event arrives first, by allowing for both variants var is_active = (_.indexOf(['focus','focusin'], ev.type) !== -1); @@ -48,4 +51,3 @@ app.views.Header = app.views.Base.extend({ } }); // @license-end - diff --git a/app/assets/javascripts/app/views/notification_dropdown_view.js b/app/assets/javascripts/app/views/notification_dropdown_view.js new file mode 100644 index 000000000..a2c7525db --- /dev/null +++ b/app/assets/javascripts/app/views/notification_dropdown_view.js @@ -0,0 +1,118 @@ +// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later + +app.views.NotificationDropdown = app.views.Base.extend({ + events:{ + "click #notifications-badge": "toggleDropdown" + }, + + initialize: function(){ + $(document.body).click($.proxy(this.hideDropdown, this)); + + this.notifications = []; + this.perPage = 5; + this.hasMoreNotifs = true; + this.badge = this.$el; + this.dropdown = $('#notification_dropdown'); + this.dropdownNotifications = this.dropdown.find('.notifications'); + this.ajaxLoader = this.dropdown.find('.ajax_loader'); + }, + + toggleDropdown: function(evt){ + evt.preventDefault(); + evt.stopPropagation(); + if(this.dropdownShowing()){ this.hideDropdown(evt); } + else{ this.showDropdown(); } + }, + + dropdownShowing: function(){ + return this.dropdown.css('display') === 'block'; + }, + + showDropdown: function(){ + this.resetParams(); + this.ajaxLoader.show(); + this.badge.addClass('active'); + this.dropdown.css('display', 'block'); + this.dropdownNotifications.addClass('loading'); + this.getNotifications(); + }, + + hideDropdown: function(evt){ + var inDropdown = $(evt.target).parents().is(this.dropdown); + var inHovercard = $.contains(app.hovercard.el, evt.target); + if(!inDropdown && !inHovercard && this.dropdownShowing()){ + this.badge.removeClass('active'); + this.dropdown.css('display', 'none'); + this.dropdownNotifications.perfectScrollbar('destroy'); + } + }, + + dropdownScroll: function(){ + var isLoading = ($('.loading').length === 1); + if (this.isBottom() && this.hasMoreNotifs && !isLoading){ + this.dropdownNotifications.addClass('loading'); + this.getNotifications(); + } + }, + + getParams: function(){ + if(this.notifications.length === 0){ return{ per_page: 10, page: 1 }; } + else{ return{ per_page: this.perPage, page: this.nextPage }; } + }, + + resetParams: function(){ + this.notifications.length = 0; + this.hasMoreNotifs = true; + delete this.nextPage; + }, + + isBottom: function(){ + var bottom = this.dropdownNotifications.prop('scrollHeight') - this.dropdownNotifications.height(); + var currentPosition = this.dropdownNotifications.scrollTop(); + return currentPosition + 50 >= bottom; + }, + + getNotifications: function(){ + var self = this; + $.getJSON(Routes.notifications_path(this.getParams()), function(notifications){ + $.each(notifications, function(){ self.notifications.push(this); }); + self.hasMoreNotifs = notifications.length >= self.perPage; + if(self.nextPage){ self.nextPage++; } + else { self.nextPage = 3; } + self.renderNotifications(); + }); + }, + + hideAjaxLoader: function(){ + var self = this; + this.ajaxLoader.find('img').fadeTo(200, 0, function(){ + self.ajaxLoader.hide(300, function(){ + self.ajaxLoader.find('img').css('opacity', 1); + }); + }); + }, + + renderNotifications: function(){ + var self = this; + this.dropdownNotifications.find('.media.stream_element').remove(); + $.each(self.notifications, function(index, notifications){ + $.each(notifications, function(index, notification){ + if($.inArray(notification, notifications) === -1){ + var node = self.dropdownNotifications.append(notification.note_html); + $(node).find('.unread-toggle .entypo').tooltip('destroy').tooltip(); + } + }); + }); + + this.hideAjaxLoader(); + + app.helpers.timeago(this.dropdownNotifications); + + this.dropdownNotifications.perfectScrollbar('destroy').perfectScrollbar(); + this.dropdownNotifications.removeClass('loading'); + this.dropdownNotifications.scroll(function(){ + self.dropdownScroll(); + }); + } +}); +// @license-end diff --git a/app/assets/javascripts/app/views/search_view.js b/app/assets/javascripts/app/views/search_view.js new file mode 100644 index 000000000..3453a969e --- /dev/null +++ b/app/assets/javascripts/app/views/search_view.js @@ -0,0 +1,70 @@ +// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later +app.views.Search = app.views.Base.extend({ + initialize: function(){ + this.searchFormAction = this.$el.attr('action'); + this.searchInput = this.$('input[type="search"]'); + this.searchInputName = this.$('input[type="search"]').attr('name'); + this.searchInputHandle = this.$('input[type="search"]').attr('handle'); + this.options = { + cacheLength: 15, + delay: 800, + extraParams: {limit: 4}, + formatItem: this.formatItem, + formatResult: this.formatResult, + max: 5, + minChars: 2, + onSelect: this.selectItemCallback, + parse: this.parse, + scroll: false, + context: this + }; + + var self = this; + this.searchInput.autocomplete(self.searchFormAction + '.json', + $.extend(self.options, { element: self.searchInput })); + }, + + formatItem: function(row){ + if(typeof row.search !== 'undefined') { return Diaspora.I18n.t('search_for', row); } + else { + var item = ''; + if (row.avatar) { item += ''; } + item += row.name; + if (row.handle) { item += '
' + row.handle + '
'; } + return item; + } + }, + + formatResult: function(row){ return Handlebars.Utils.escapeExpression(row.name); }, + + parse: function(data) { + var self = this.context; + + var results = data.map(function(person){ + person.name = self.formatResult(person); + return {data : person, value : person.name}; + }); + + results.push({ + data: { + name: self.searchInput.val(), + url: self.searchFormAction + '?' + self.searchInputName + '=' + self.searchInput.val(), + search: true + }, + value: self.searchInput.val() + }); + + return results; + }, + + selectItemCallback: function(evt, data, formatted){ + if(data.search === true){ + window.location = this.searchFormAction + '?' + this.searchInputName + '=' + data.name; + } + else{ // The actual result + this.options.element.val(formatted); + window.location = data.url ? data.url : '/tags/' + data.name.substring(1); + } + } +}); +// @license-ends diff --git a/app/assets/javascripts/widgets/header.js b/app/assets/javascripts/widgets/header.js deleted file mode 100644 index 8d5fd8a9d..000000000 --- a/app/assets/javascripts/widgets/header.js +++ /dev/null @@ -1,25 +0,0 @@ -// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later - -(function() { - var Header = function() { - var self = this; - - this.subscribe("widget/ready", function(evt, header) { - self.notifications = self.instantiate("Notifications", - header.find("#notification_badge .badge_count"), - header.find("#notification_dropdown") - ); - - self.notificationsDropdown = self.instantiate("NotificationsDropdown", - header.find("#notification_badge"), - header.find("#notification_dropdown") - ); - - self.search = self.instantiate("Search", header.find(".search_form")); - }); - }; - - Diaspora.Widgets.Header = Header; -})(); -// @license-end - diff --git a/app/assets/javascripts/widgets/notifications-badge.js b/app/assets/javascripts/widgets/notifications-badge.js deleted file mode 100644 index fadb8e185..000000000 --- a/app/assets/javascripts/widgets/notifications-badge.js +++ /dev/null @@ -1,118 +0,0 @@ -// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later - -(function() { - var NotificationDropdown = function() { - var self = this; - var currentPage = 2; - var notificationsLoaded = 10; - - this.subscribe("widget/ready",function(evt, badge, dropdown) { - $.extend(self, { - badge: badge, - badgeLink: badge.find("a"), - documentBody: $(document.body), - dropdown: dropdown, - dropdownNotifications: dropdown.find(".notifications"), - ajaxLoader: dropdown.find(".ajax_loader") - }); - - if( ! $.browser.msie ) { - self.badge.on('click', self.badgeLink, function(evt){ - evt.preventDefault(); - evt.stopPropagation(); - if (self.dropdownShowing()){ - self.hideDropdown(); - } else { - self.showDropdown(); - } - }); - } - - self.documentBody.click(function(evt) { - var inDropdown = $(evt.target).parents().is(self.dropdown); - var inHovercard = $.contains(app.hovercard.el, evt.target); - - if(!inDropdown && !inHovercard && self.dropdownShowing()) { - self.badgeLink.click(); - } - }); - }); - - - this.dropdownShowing = function() { - return this.dropdown.css("display") === "block"; - }; - - this.showDropdown = function() { - self.ajaxLoader.show(); - self.badge.addClass("active"); - self.dropdown.css("display", "block"); - self.dropdownNotifications.addClass("loading"); - self.getNotifications(); - - }; - - this.hideDropdown = function() { - self.badge.removeClass("active"); - self.dropdown.css("display", "none"); - self.dropdownNotifications.perfectScrollbar('destroy'); - }; - - this.getMoreNotifications = function(page) { - $.getJSON("/notifications?per_page=5&page=" + page, function(notifications) { - for(var i = 0; i < notifications.length; ++i) - self.notifications.push(notifications[i]); - notificationsLoaded += 5; - self.renderNotifications(); - }); - }; - - this.hideAjaxLoader = function(){ - self.ajaxLoader.find('img').fadeTo(200, 0, function(){ - self.ajaxLoader.hide(300, function(){ - self.ajaxLoader.find('img').css('opacity', 1); - }); - }); - }; - - this.getNotifications = function() { - $.getJSON("/notifications?per_page="+notificationsLoaded, function(notifications) { - self.notifications = notifications; - self.renderNotifications(); - }); - }; - - this.renderNotifications = function() { - this.dropdownNotifications.find('.media.stream_element').remove(); - $.each(self.notifications, function(index, notifications) { - $.each(notifications, function(index, notification) { - if($.inArray(notification, notifications) === -1){ - var node = self.dropdownNotifications.append(notification.note_html); - $(node).find(".unread-toggle .entypo").tooltip('destroy').tooltip(); - } - }); - }); - - self.hideAjaxLoader(); - - app.helpers.timeago(self.dropdownNotifications); - - self.dropdownNotifications.perfectScrollbar('destroy').perfectScrollbar(); - var isLoading = false; - self.dropdownNotifications.removeClass("loading"); - //Infinite Scrolling - self.dropdownNotifications.scroll(function() { - var bottom = self.dropdownNotifications.prop('scrollHeight') - self.dropdownNotifications.height(); - var currentPosition = self.dropdownNotifications.scrollTop(); - isLoading = ($('.loading').length === 1); - if (currentPosition + 50 >= bottom && notificationsLoaded <= self.notifications.length && !isLoading) { - self.dropdownNotifications.addClass("loading"); - self.getMoreNotifications(++currentPage); - } - }); - }; - }; - - Diaspora.Widgets.NotificationsDropdown = NotificationDropdown; -})(); -// @license-end diff --git a/app/assets/javascripts/widgets/search.js b/app/assets/javascripts/widgets/search.js deleted file mode 100644 index 554075194..000000000 --- a/app/assets/javascripts/widgets/search.js +++ /dev/null @@ -1,83 +0,0 @@ -// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later - -(function() { - var Search = function() { - var self = this; - - this.subscribe("widget/ready", function(evt, searchForm) { - $.extend(self, { - searchForm: searchForm, - searchFormAction: searchForm.attr("action"), - searchInput: searchForm.find("input[type='search']"), - searchInputName: searchForm.find("input[type='search']").attr("name"), - searchInputHandle: searchForm.find("input[type='search']").attr("handle"), - options: { - cacheLength : 15, - delay : 800, - extraParams : {limit : 4}, - formatItem : self.formatItem, - formatResult : self.formatResult, - max : 5, - minChars : 2, - onSelect: self.selectItemCallback, - parse : self.parse, - scroll : false - } - }); - - self.searchInput.autocomplete(self.searchFormAction + ".json", $.extend(self.options, { - element: self.searchInput - })); - }); - - this.formatItem = function(row) { - if (typeof row.search !== "undefined") { - return Diaspora.I18n.t("search_for", row); - } else { - var item = ""; - if (row.avatar) { - item += ""; - } - item += row.name; - if (row.handle) { - item += "
" + row.handle + "
"; - } - return item; - } - }; - - this.formatResult = function(row) { - return Handlebars.Utils.escapeExpression(row.name); - }; - - this.parse = function(data) { - var results = data.map(function(person){ - person['name'] = Handlebars.Utils.escapeExpression(person['name']); - return {data : person, value : person['name']}; - }); - - results.push({ - data: { - name: self.searchInput.val(), - url: self.searchFormAction + "?" + self.searchInputName + "=" + self.searchInput.val(), - search: true - }, - value: self.searchInput.val() - }); - - return results; - }; - - this.selectItemCallback = function(evt, data, formatted) { - if (data['search'] === true) { // The placeholder "search for" result - window.location = self.searchFormAction + '?' + self.searchInputName + '=' + data['name']; - } else { // The actual result - self.options.element.val(formatted); - window.location = data['url'] ? data['url'] : "/tags/" + data['name'].substring(1); // we don't want the #-character - } - }; - }; - - Diaspora.Widgets.Search = Search; -})(); -// @license-end diff --git a/app/assets/templates/header_tpl.jst.hbs b/app/assets/templates/header_tpl.jst.hbs index ab3ebc62c..3922d6b5d 100644 --- a/app/assets/templates/header_tpl.jst.hbs +++ b/app/assets/templates/header_tpl.jst.hbs @@ -22,7 +22,7 @@